diff --git a/.agents/skills/oracle-authoring/SKILL.md b/.agents/skills/oracle-authoring/SKILL.md new file mode 100644 index 0000000000..dfd5454348 --- /dev/null +++ b/.agents/skills/oracle-authoring/SKILL.md @@ -0,0 +1,98 @@ +--- +name: oracle-authoring +description: Discover and articulate subsystem laws, then create or edit independent oracle tests and generated histories in TanStack DB. Use for new oracle coverage, oracle repairs, or changes to a reference model. +--- + +# Author an oracle + +Start by making the law clear enough that someone could predict an observation +without reading production. The hard part is deciding what is owed, to whom, +under which conditions, and when. An executable model enacts that account. + +Use this skill whenever an oracle changes, or the production driver, history +grammar, or comparison that it uses. This includes a change made while you fix +a bug, fix a review finding, or evaluate review findings. + +Read [how oracle work is scored](../../../AGENTS.md#how-oracle-work-is-scored) +first. Auditors check the order of your work and penalize shortcuts. + +Work in this order when a production change is part of the task: + +1. State the law and its authority. +2. Inventory the materially affected public receiving paths and their promised + observation cuts. Include required real-host receiving witnesses in this + pre-edit gate; a controlled core witness does not supply their evidence. +3. Extend the history grammar, driver, or observation until generated + histories reach the state where the law matters. Declare that state with + `createOracleReach` (ORC-015) for each required receiving path. +4. Run those comparisons on unchanged production. Record the defect's RED + result and the neighboring guarantees' results. A setup failure or missing + required witness blocks production edits; repair the test path first. +5. Change production, with the oracle running after each attempt. +6. Record the GREEN result and a hostile mutant that fails. + +Do not change production before step 4. Retrospective runs or a documented +process gap do not satisfy this gate. If a model rule disagrees with +production, find its authority before you edit either one. A rule that you +change only so that it matches production output is model hacking. + +Read [the oracle guide](../../../docs/contributing/oracle-rubric.md) and +[the glossary](../../../docs/contributing/glossary.md) in full, then read the +relevant owner and limits in +[the coverage map](../../../docs/contributing/oracle-coverage.md). + +After a handoff or compaction, reopen required sources whose relevant contents +are absent from the current context. A prior agent's statement that it read +them does not supply their definitions. Apply the glossary's model alignment +rule before naming or renaming a state, action, boundary, or observation. Use +canonical terms and declare abstractions that combine or split production +concepts. Delegated agents must do the reading relevant to their own scope. + +Follow the subsystem reading requirements in AGENTS.md. + +Keep unit tests and standalone concrete type assertions out of oracle files +(ORC-016). Fixed regressions are inputs to the same model, driver, and +comparison as the broader campaign, without their own expected answers. +Compile-time oracles generate legal TypeScript inputs and compare public +types with a reusable independent law; a matrix of fixed assertions does not +satisfy this requirement. Supplemental examples belong in ordinary test files. + +Use the [instrument library](../../../docs/contributing/instruments/index.md) +when there is a material uncertainty. Select cards within the authorized task; +there is no required sequence or instrument quota. + +- When the promised behavior is unclear, use **Law discovery**. Separate the + established contract, observations of current behavior, and proposed design. +- When changing an established model, write old and proposed predictions for a + distinguishing case before editing expectations. **Adversarial review** can + expose a shared mistake in production and the model. +- When obligations collide, use **Tension scan**. When laws overlap or their + concepts seem wrong, use **Law restructuring** to propose and check a better + organization. +- When an oracle is green but may accept a plausible wrong implementation, + use **Mutant gap hunt** to design a semantic edit and classify any survivor. + +Keep the chosen law and its authority visible beside the independent model. +A counterexample can indict production, the model, the history grammar, the +observation checkpoint, or the proposed law. Determine which before repairing +it. A convenient encoding or a stronger assertion is not automatically a better +account of the contract. + +Develop the executable evidence using the oracle guide: legal histories, +production entry point, public observations, and a comparison that rejects a +plausible wrong design. Explain why each model distinction changes an allowed +next action or promised observation. Preserve freedom where the contract leaves +scheduling, representation, or implementation choices open. + +For a law-design request, return candidate laws, source authority, rival +predictions, and the experiment that could discriminate them. Implement tests +when requested by the task. A proposal with unresolved authority can be explored +in a scratch model; do not silently make it a product expectation. + +At closeout, write the evidence block from AGENTS.md (law, reach, RED, GREEN, +mutant, limits). Connect the law to the evidence actually obtained: which histories +and paths ran, which wrong behavior was rejected, and what remains untested. +Use concise prose suited to the change; no particular report template is +required. When changing repository coverage, update the existing owner and +coverage map to reflect the changed scope. A scratch law-design experiment +retains its proposals and limits without claiming new repository coverage. diff --git a/.agents/skills/oracle-review/SKILL.md b/.agents/skills/oracle-review/SKILL.md new file mode 100644 index 0000000000..da83d6f213 --- /dev/null +++ b/.agents/skills/oracle-review/SKILL.md @@ -0,0 +1,89 @@ +--- +name: oracle-review +description: Review TanStack DB oracles and generated-history tests by challenging enforcement, model independence, law formulation, law suitability, and relationships among laws. Use for oracle reviews and proposed oracle contract changes. +--- + +# Review an oracle + +Ask both whether the oracle enforces its stated law and whether that is the +right law to promise. A passing test can coexist with a wrong theory; a failing +test can coexist with correct production. + +Read [the oracle guide](../../../docs/contributing/oracle-rubric.md) and +[the glossary](../../../docs/contributing/glossary.md) in full, then read the +relevant owner and limits in +[the coverage map](../../../docs/contributing/oracle-coverage.md). + +After a handoff or compaction, reopen required sources whose relevant contents +are absent from the current context. A prior agent's statement that it read +them does not supply their definitions. Apply the glossary's model alignment +rule before naming or renaming a state, action, boundary, or observation. Use +canonical terms and declare abstractions that combine or split production +concepts. Delegated agents must do the reading relevant to their own scope. + +Follow the subsystem reading requirements in AGENTS.md. Guide conformance is +judged by its ORC requirements, not by use of these instruments or headings. + +Follow the oracle's authority claim to the nearest public contract source and +inspect neighboring promises within the review scope. The oracle's own summary +can omit the very obligation it should check. + +Reconstruct one promised observation from the contract independently of the +implementation. Trace the actual model, legal histories, production path, +recorder, and comparison that purport to check it. Then select a useful attack +from the [instrument library](../../../docs/contributing/instruments/index.md): + +- **Adversarial review** examines enforcement, formulation, suitability, and + underlying concepts. Name the level each attack addresses. +- **Law discovery** helps when the claim has no clear authority or two plausible + interpretations predict different behavior. +- **Tension scan** examines demands that collide under a concrete condition. +- **Law restructuring** tests whether merging, splitting, replacing, or removing + laws gives a better account while preserving necessary obligations. +- **Mutant gap hunt** tests whether a plausible wrong implementation can pass + the owner and traces survivors to a missing distinction, a contract-permitted + difference, a scoped equivalence argument, or a baseline product bug. + +Choose attacks for the actual uncertainty; do not run every card or require a +finding at every level. Use a fresh reviewer for an independent challenge when +delegation is available and authorized. Give it the artifact, sources, and task, +without the author's desired verdict. Label a self-review as such. + +For each finding, give the challenged claim, source or authority, smallest +legal witness or concrete design consequence, evidence status, and repair or +next experiment. For a confirmed bug, explain why the existing oracle missed +it and identify the smallest general improvement. Distinguish executed results +from source inspection and proposed experiments. A setup error or timeout is +not evidence that a mutant was rejected at the comparison. + +Keep defects under accepted contracts separate from proposals to change those +contracts. Criticism may recommend different, safer, or more conventional +behavior, but must name the external standard or engineering reason, its +applicability, and migration cost. Existing behavior does not establish its own +correctness; a preference does not establish a contract violation. + +Audit the change for the shortcuts that +[the scoring section](../../../AGENTS.md#how-oracle-work-is-scored) penalizes. +Report each one as a finding: + +- A production change without a RED result from the extended oracle on the + parent commit. +- Required public receiving paths tested only after the production repair, + or aggregate reach that hides an unexercised receiving path. Check the + pre-edit inventory, actual comparison results, and their order. Retrospective + evidence or documentation of a gap does not satisfy the pre-edit gate. +- A model rule that changed to match production output without an authority. +- A claim about a state that no declared reach shows generated histories enter. +- A skipped, narrowed, or relaxed comparison without a coverage-map entry and a + maintainer decision. +- A pinned example where the law needs a generator dimension. +- A counterexample labeled as older than the change and then left without a + fix, a coverage-map record, or a maintainer decision. +- A closure claim without a hostile mutant that fails the oracle. + +Report supported findings and remaining uncertainty. Do not invent findings, +retire laws, or rewrite expectations to make a review come out decisive. + +If the review leads to oracle edits, load +[oracle-authoring](../oracle-authoring/SKILL.md) before you edit. This also +applies when you fix a finding during the same task. diff --git a/.changeset/CODEOWNERS b/.changeset/CODEOWNERS new file mode 100644 index 0000000000..ec37670885 --- /dev/null +++ b/.changeset/CODEOWNERS @@ -0,0 +1,6 @@ +.github/ @TanStack/tanstack-core +.nx/ @TanStack/tanstack-core +nx.json @TanStack/tanstack-core +.changeset/config.json @TanStack/tanstack-core +scripts/ @TanStack/tanstack-core +.npmrc @TanStack/tanstack-core \ No newline at end of file diff --git a/.changeset/bright-cups-infer.md b/.changeset/bright-cups-infer.md new file mode 100644 index 0000000000..9e56befd92 --- /dev/null +++ b/.changeset/bright-cups-infer.md @@ -0,0 +1,7 @@ +--- +'@tanstack/db-sqlite-persistence-core': patch +--- + +Fix type inference for schema-free collection options wrapped with persistedCollectionOptions and passed to createCollection. + +Reject caller utilities that use the reserved local persistence names acceptMutations, getLeadershipState, or forceReloadSubset, instead of silently replacing them. diff --git a/.changeset/browser-sqlite-separate-wasm.md b/.changeset/browser-sqlite-separate-wasm.md new file mode 100644 index 0000000000..f9195dc3aa --- /dev/null +++ b/.changeset/browser-sqlite-separate-wasm.md @@ -0,0 +1,5 @@ +--- +'@tanstack/browser-db-sqlite-persistence': patch +--- + +Ship wa-sqlite WebAssembly as a separate asset that the browser loads automatically, instead of inlining it in the OPFS worker. In Vite development, the package finds its assets without configuration; production builds drop that lookup. An install that Vite cannot resolve by package name, or a worker from another installed version of the package, now reports `PersistenceUnavailableError`, and excluding the package from pre-bundling fixes it. diff --git a/.changeset/convex-collection.md b/.changeset/convex-collection.md new file mode 100644 index 0000000000..96323503c2 --- /dev/null +++ b/.changeset/convex-collection.md @@ -0,0 +1,8 @@ +--- +'@tanstack/convex-db-collection': minor +'@tanstack/offline-transactions': minor +--- + +Add a Convex collection adapter with typed query subscriptions, full-row sync, automatic reactive pagination with on-demand windows for a fixed ordered query, durable offline mutation replay, optimistic mutation handlers, and support for both Convex JavaScript and React clients. + +Add opt-in mutation codecs to the durable outbox while preserving the default wire format and retaining unreadable codec records for recovery. diff --git a/.changeset/db-query-identity-laws.md b/.changeset/db-query-identity-laws.md new file mode 100644 index 0000000000..8020f09d16 --- /dev/null +++ b/.changeset/db-query-identity-laws.md @@ -0,0 +1,9 @@ +--- +'@tanstack/db': minor +--- + +Collect the helpers the framework adapters share into one adapter layer, exported from `@tanstack/db` and marked unstable for external use. It adds `identifyLiveQuery`, which checks a prepared query's identity and source Collection IDs for a hook that compares query identity, and `bindLiveQueryWindow`, the window binding every `useLiveInfiniteQuery` now uses. The window helpers that binding replaces are no longer exported. + +Add `SourceCollectionIdReuseError`, which a mounted hook that compares query identity throws when a different source Collection appears under a Collection ID it already read. + +Add `rejectLiveQueryInput` and `releaseDeferredOnReclaim` for framework hooks. The first releases the sync starts a rejected query deferred and rethrows the rejection. The second releases a render's deferred sync starts when garbage collection reclaims the live query it built, so a render that never commits, because a sibling threw or it suspended, does not keep its sources deferred. Each released source follows its own start policy, so `startSyncImmediate()` or another hook can still start it. diff --git a/.changeset/defer-term-behind-queued-reads.md b/.changeset/defer-term-behind-queued-reads.md new file mode 100644 index 0000000000..ff603970d4 --- /dev/null +++ b/.changeset/defer-term-behind-queued-reads.md @@ -0,0 +1,10 @@ +--- +'@tanstack/db-sqlite-persistence-core': minor +'@tanstack/electron-db-sqlite-persistence': patch +--- + +Reloaded persisted collections now read their rows before they write a leader election term. A collection still takes its leader lock when it subscribes, but it reserves its durable term only after the database work already queued on the shared scheduler has settled, or as soon as something needs a leader during that election: a local write, a follower request, a sync commit during startup, or `ensureLeadership`. A need that an earlier leader already served does not let a later takeover skip the wait. A tab that takes over treats the departed leader's route as gone, so a sync commit queued behind one of its reads goes to the new leader and settles without waiting out an RPC timeout. The Electron renderer applies the same rule to its own hydrations. The renderer now has a hydration scope, so its startup runs as one hydration, as a Browser tab's does, and a restart keeps its metadata and row reads in one scope, so a term cannot run between them. + +`ensureLeadership` now resolves when this node becomes leader, keeps its place in the Web Locks queue instead of replacing its lock request, and rejects if the coordinator is disposed. + +Migration for custom persistence adapters: `runInRegularScope` is now a `RegularScope`, which must also provide `afterQueuedWork()`. It resolves once every operation queued or running on the adapter's scheduler at the call has settled. An adapter without a scheduler can return a promise that settles with its running hydrations. A coordinator now rejects an adapter whose `runInRegularScope` lacks `afterQueuedWork` with `InvalidPersistenceAdapterError`. diff --git a/.changeset/electric-cleanup-stream-listeners.md b/.changeset/electric-cleanup-stream-listeners.md new file mode 100644 index 0000000000..4faea3325e --- /dev/null +++ b/.changeset/electric-cleanup-stream-listeners.md @@ -0,0 +1,5 @@ +--- +'@tanstack/electric-db-collection': patch +--- + +Release the shape stream's visibility listener when a sync run is cleaned up. The Electric client registers a `visibilitychange` listener for each `ShapeStream` (an `AppState` subscription on React Native, from `@electric-sql/client` 1.5.23) and removes it only in `unsubscribeAll()`, so aborting the stream left the listener behind, and the listener kept the stream reachable. An app that cleans up and recreates collections, or whose on-demand cache rotates its provider session, gained one retained stream each time. diff --git a/.changeset/first-rc.md b/.changeset/first-rc.md new file mode 100644 index 0000000000..9d4870748e --- /dev/null +++ b/.changeset/first-rc.md @@ -0,0 +1,39 @@ +--- +'@tanstack/angular-db': major +'@tanstack/browser-db-sqlite-persistence': major +'@tanstack/capacitor-db-sqlite-persistence': major +'@tanstack/cloudflare-durable-objects-db-sqlite-persistence': major +'@tanstack/convex-db-collection': major +'@tanstack/db': major +'@tanstack/db-ivm': major +'@tanstack/db-sqlite-persistence-core': major +'@tanstack/electric-db-collection': major +'@tanstack/electron-db-sqlite-persistence': major +'@tanstack/expo-db-sqlite-persistence': major +'@tanstack/node-db-sqlite-persistence': major +'@tanstack/offline-transactions': minor +'@tanstack/powersync-db-collection': major +'@tanstack/preact-db': major +'@tanstack/query-db-collection': minor +'@tanstack/react-db': major +'@tanstack/react-native-db-sqlite-persistence': major +'@tanstack/react-router-with-db': major +'@tanstack/rxdb-db-collection': major +'@tanstack/solid-db': major +'@tanstack/svelte-db': major +'@tanstack/tauri-db-sqlite-persistence': major +'@tanstack/trailbase-db-collection': major +'@tanstack/vue-db': major +--- + +Release the first 1.0 candidate. Remove deprecated pre-1.0 APIs and give +published row metadata precedence over colliding selected fields. Structured +source spreads and direct source-row returns keep source attribution; new +functional objects and other projections receive output Collection metadata. +Keep the two packages accidentally released as stable 1.x on their stable +release lines. Both stable minor releases require `@tanstack/db@^1.0.0-rc.0` +for the RC health and restoration settlement APIs; upgrade DB with them. + +Remove Query Collection’s automatic refetch after mutation handlers. Handlers +return `Promise` and must await `collection.utils.refetch()` or write +confirmed data with the `write*` utilities; `{ refetch: false }` is removed. diff --git a/.changeset/gc-waits-for-persisting-transactions.md b/.changeset/gc-waits-for-persisting-transactions.md new file mode 100644 index 0000000000..f5dcb11836 --- /dev/null +++ b/.changeset/gc-waits-for-persisting-transactions.md @@ -0,0 +1,5 @@ +--- +'@tanstack/db': patch +--- + +Garbage collection no longer cleans up a collection while a transaction that touches it is persisting. Cleaning it up dropped the transaction's optimistic rows while its mutation handler still ran, and rejected waits such as Electric's `awaitTxId`, so a write could fail after the screen that made it unmounted. The `gcTime` delay now starts again when the last such transaction completes or fails, cancels obsolete queued idle cleanup, and preserves the remaining startup grace. Rolling back a manual transaction that stayed pending preserves the existing deadline. Explicit `cleanup()` still starts immediately; a transaction that settles afterward no longer restores another transaction's cleared optimistic rows. diff --git a/.changeset/keep-live-query-through-failed-replay.md b/.changeset/keep-live-query-through-failed-replay.md new file mode 100644 index 0000000000..12b98fdbe7 --- /dev/null +++ b/.changeset/keep-live-query-through-failed-replay.md @@ -0,0 +1,11 @@ +--- +'@tanstack/db': patch +--- + +A query that joins and includes the same source has two subscriptions to it. Their truncate replays now share one barrier, so neither publishes while the other still loads. When a lazy demand of either fails while a failed replay of the source holds the query's results, the query keeps its last complete result until a later replay of that source succeeds. Before, retiring the other replay's failed demand could publish the window without that demand's rows. + +Some sources refetch and then report that their pages shifted. An ordered window over such a source no longer skips a row tied with the refetched first page's new end. When the report shows that the page's end moved, the window repairs its first page. It no longer trusts a row that an older tie request installed. + +A window move made while a truncate replay holds a live query's results now applies after that replay, even when a newer truncate discards it. Before, the move could reject, and an older move's window stayed. A reload of the query or of a source still ends the move. + +An ordered, limited query over a source without an order index can read too few rows to fill its window. A join that drops rows can cause this. It no longer reads the whole source then. It reads a prefix twice as long instead. It grows again while that adds rows, so every request stays bounded. diff --git a/.changeset/on-demand-network-first.md b/.changeset/on-demand-network-first.md new file mode 100644 index 0000000000..443926353a --- /dev/null +++ b/.changeset/on-demand-network-first.md @@ -0,0 +1,11 @@ +--- +'@tanstack/db-sqlite-persistence-core': minor +--- + +`initialRender: { strategy: 'network-first' }` now works for on-demand persisted Collections, including Electric's `progressive` mode. Previously the configuration threw `InvalidPersistedCollectionConfigError`. + +A subset load that settles online records persisted demand coverage for its exact demand. In a later session, a load of the same demand settles on its rows from SQLite once `networkTimeoutMs` (default 3 seconds) has passed, or at once if the network load fails. The network load keeps running, and its rows publish when they arrive. If it fails after that, the query stays ready. A demand that never settled online still waits for the network. Coverage proves that the rows were complete when they were loaded, not that they are still current. A source truncate, a snapshot transaction, scoped recovery, or a cache-generation rotation clears it. A load that a reset made obsolete, a load that was released before it settled, and a load during the rest of a scoped-recovery sync run record none. Coverage also ends when SQLite holds fewer rows for the demand than when it settled, for example after a source delete or Query DB retention expiry. A `refetch: true` load always waits for the network. An ordered window with a `limit` stays covered only while SQLite holds the same rows for it. + +A query that joins eager and on-demand sources becomes ready when every eager source is ready, which network-first allows from its restored rows after the deadline, and every demand the query reaches on an on-demand source has settled. A network failure alone does not settle an on-demand demand that is not covered. + +This applies only with the default single-process coordinator. With a multi-tab coordinator, such as `BrowserCollectionCoordinator`, on-demand loads still wait for the network. diff --git a/.changeset/opt-in-operators.md b/.changeset/opt-in-operators.md new file mode 100644 index 0000000000..507da726cc --- /dev/null +++ b/.changeset/opt-in-operators.md @@ -0,0 +1,18 @@ +--- +'@tanstack/db': minor +'@tanstack/react-db': patch +'@tanstack/preact-db': patch +'@tanstack/svelte-db': patch +'@tanstack/vue-db': patch +'@tanstack/solid-db': patch +'@tanstack/angular-db': patch +--- + +Breaking: grouping, includes, and pooling are now opt-in. An app bundles an operator's code only when it imports the value for that operator. Joins do not change. A typical app is about 8 kB smaller after gzip. This app has a query collection, a filtered live query, and React `useLiveQuery`. + +- An include needs `include()`, `toArray()`, or `materialize()`. Change `issues: q.from(...)` in `select()` to `issues: include(q.from(...))`. A subquery in `select()` without one of these throws `IncludeOperatorRequiredError`. +- Aggregates such as `count()` need no change. A `groupBy` with no aggregate takes `groupKeys()`: `.groupBy(groupKeys(({ todo }) => todo.status))`. Without it, the query throws `GroupingOperatorRequiredError`. +- Hooks do not pool live queries by default. To share one partition among many equality-filtered queries, pass `pool: pooledLiveQueries` in the hook's query config: `useLiveQuery({ query, pool: pooledLiveQueries })`. Results do not change. Angular's `{ params, query }` form takes `pool` the same way. Any other `pool` value, such as the old boolean, throws `InvalidPoolOptionError`, also for a disabled query. For adapter authors: the `pool` option of `resolveLiveQueryValue` is now `pooledLiveQueries` or `false`, not a boolean. An adapter that reads `pool` from its own options checks it with `configPool` first. +- Code that builds query IR by hand must supply the operator code. `IR.Aggregate` takes `groupingCompiler` as its third argument. `IR.IncludesSubquery` takes `includesRuntime` as its fifth argument, before `parentFilters`. Import both from `@tanstack/db`. IR without them throws `GroupingOperatorRequiredError` or `IncludeOperatorRequiredError` when it compiles, unless another aggregate or `groupKeys()` in the query supplies the grouping compiler. Queries that the query builder builds need no change. +- Operator code is part of query identity. A hook that re-renders a grouped query without `groupKeys()` now throws `GroupingOperatorRequiredError`. Before, React and Preact kept the grouped query. +- Vue: when a changed query fails to compile, `useLiveQuery` no longer shows the rows of the previous query. diff --git a/.changeset/ordered-loader-superseded-failure.md b/.changeset/ordered-loader-superseded-failure.md new file mode 100644 index 0000000000..333468eaad --- /dev/null +++ b/.changeset/ordered-loader-superseded-failure.md @@ -0,0 +1,9 @@ +--- +'@tanstack/db': patch +--- + +Keep ordered live queries accurate and loading when source work overlaps. A window with `orderBy` and `limit` no longer stops loading, keeps old rows, or holds acquisitions in these cases: a failed or superseded request, a truncate replay, a source change during a repair, or an on-demand joined source. Recovery after a failure uses one full-source read, and a window move whose load succeeded now resolves. + +When an adapter's `unloadSubset` throws, TanStack DB now reports the error as an uncaught error and continues as if the release succeeded. The error is no longer a subset failure. It does not set `lastError` or `lastSubsetError`, fail a truncate replay, reject `setWindow()`, or dispose an Effect. `unsubscribe()`, `releaseSnapshot()`, and Effect `dispose()` no longer throw or reject with it. + +The newest `setWindow()` call now wins. A pending source recovery that defers two calls no longer lets the older one set the window. A superseded call that succeeds now resolves only after the newest call settles, so awaiting it shows the window that won. diff --git a/.changeset/persisted-reload-keeps-status.md b/.changeset/persisted-reload-keeps-status.md new file mode 100644 index 0000000000..99c3bd75c9 --- /dev/null +++ b/.changeset/persisted-reload-keeps-status.md @@ -0,0 +1,5 @@ +--- +'@tanstack/db-sqlite-persistence-core': patch +--- + +Keep a persisted Collection's status when another tab's commit reloads its rows. Before, a reload from durable rows could mark a tab `ready` while its own source was still loading, or move a tab whose initial sync failed from `error` to `ready`. Now only the Collection's own source changes its status. diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 0000000000..36af03dffd --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,74 @@ +{ + "mode": "pre", + "tag": "rc", + "initialVersions": { + "todos": "0.0.27", + "offline-first-electron": "1.0.7", + "offline-transactions-react-native": "1.0.14", + "shopping-list-react-native": "1.0.14", + "@tanstack/db-example-react-action-enforcement": "0.0.1", + "@tanstack/db-example-react-next-ssr-e2e": "0.0.9", + "@tanstack/db-example-paced-mutations-demo": "0.0.19", + "@tanstack/db-example-react-start-ssr-e2e": "0.0.3", + "@tanstack/db-example-react-todo": "0.1.31", + "@tanstack/db-example-solid-todo": "0.0.39", + "@tanstack/angular-db": "1.0.0-rc.0", + "@tanstack/browser-db-sqlite-persistence": "1.0.0-rc.0", + "@tanstack/capacitor-db-sqlite-persistence": "1.0.0-rc.0", + "@tanstack/capacitor-db-sqlite-persistence-e2e-app": "0.0.49", + "@tanstack/cloudflare-durable-objects-db-sqlite-persistence": "1.0.0-rc.0", + "@tanstack/convex-db-collection": "1.0.0-rc.0", + "@tanstack/db": "1.0.0-rc.0", + "db-collections": "0.0.0", + "@tanstack/db-ivm": "1.0.0-rc.0", + "@tanstack/db-sqlite-persistence-core": "1.0.0-rc.0", + "@tanstack/electric-db-collection": "1.0.0-rc.0", + "@tanstack/electron-db-sqlite-persistence": "1.0.0-rc.0", + "@tanstack/expo-db-sqlite-persistence": "1.0.0-rc.0", + "@tanstack/expo-db-sqlite-persistence-e2e-app": "0.0.49", + "@tanstack/node-db-sqlite-persistence": "1.0.0-rc.0", + "@tanstack/offline-transactions": "1.1.4", + "@tanstack/powersync-db-collection": "1.0.0-rc.0", + "@tanstack/preact-db": "1.0.0-rc.0", + "@tanstack/query-db-collection": "1.4.8", + "@tanstack/react-db": "1.0.0-rc.0", + "@tanstack/react-native-db-sqlite-persistence": "1.0.0-rc.0", + "@tanstack/react-router-with-db": "1.0.0-rc.0", + "@tanstack/rxdb-db-collection": "1.0.0-rc.0", + "@tanstack/solid-db": "1.0.0-rc.0", + "@tanstack/svelte-db": "1.0.0-rc.0", + "@tanstack/tauri-db-sqlite-persistence": "1.0.0-rc.0", + "@tanstack/tauri-db-sqlite-persistence-e2e-app": "0.0.49", + "@tanstack/trailbase-db-collection": "1.0.0-rc.0", + "@tanstack/vue-db": "1.0.0-rc.0" + }, + "changesets": [ + "bright-cups-infer", + "browser-sqlite-separate-wasm", + "convex-collection", + "db-query-identity-laws", + "defer-term-behind-queued-reads", + "electric-cleanup-stream-listeners", + "first-rc", + "gc-waits-for-persisting-transactions", + "keep-live-query-through-failed-replay", + "on-demand-network-first", + "opt-in-operators", + "ordered-loader-superseded-failure", + "persisted-reload-keeps-status", + "preact-db-adapter", + "react-db-strip-dev-warnings", + "react-db-unhashable-throws", + "reserved-commit-defers-every-hydration", + "resume-independent-subscription-demand", + "skip-complete-collection-setup", + "solid-js-peer-cap", + "sortedmap-merge-restore", + "source-health-channel", + "sqlite-connection-closed-error", + "svelte-db-query-identity", + "sync-metadata-session-reads", + "tidy-opfs-close", + "vue-db-window-binding" + ] +} diff --git a/.changeset/preact-db-adapter.md b/.changeset/preact-db-adapter.md new file mode 100644 index 0000000000..d9429c4be5 --- /dev/null +++ b/.changeset/preact-db-adapter.md @@ -0,0 +1,5 @@ +--- +'@tanstack/preact-db': minor +--- + +Add `@tanstack/preact-db`, a Preact adapter with `useLiveQuery` and `useLiveInfiniteQuery`. Both compare query identity derived from the query IR on each render, require a `queryKey` for functional query variants, and pass the shared live-query and infinite-query conformance suites. Queries read concrete source Collections; `DbProvider` and collection descriptors are not supported yet. diff --git a/.changeset/react-db-strip-dev-warnings.md b/.changeset/react-db-strip-dev-warnings.md new file mode 100644 index 0000000000..3285cf853a --- /dev/null +++ b/.changeset/react-db-strip-dev-warnings.md @@ -0,0 +1,5 @@ +--- +'@tanstack/react-db': patch +--- + +Production builds no longer include the development warnings of `useLiveQuery`. A typical app's bundle is about 1.6 kB smaller before compression and 0.66 kB smaller after gzip. diff --git a/.changeset/react-db-unhashable-throws.md b/.changeset/react-db-unhashable-throws.md new file mode 100644 index 0000000000..d5782ecf19 --- /dev/null +++ b/.changeset/react-db-unhashable-throws.md @@ -0,0 +1,7 @@ +--- +'@tanstack/react-db': minor +--- + +**Breaking (1.0):** a live query whose IR has no stable identity, such as one using `fn.where`, `fn.select`, or `fn.having`, now throws `UnhashableQueryIRError` unless it supplies a nonempty `queryKey`. It previously warned in development and kept its first compilation. An empty `queryKey` now counts as none, as it already did for a `DbClient` preload, so a hook and a preload agree on the query's identity. A dependency array does not replace `queryKey`: in that mode the hook checks identity and claims source Collection IDs each time it binds a live query, including one that `useLiveSuspenseQuery` takes from its cache. A reused source Collection ID now throws `SourceCollectionIdReuseError`. + +A rejected render now releases every sync start it deferred for a `DbClient` descriptor, so a later hook or `startSyncImmediate()` can start that source. A render that never commits, because a sibling threw or it suspended, releases them when its live query is reclaimed. diff --git a/.changeset/recover-interleaved-electric-source-commits.md b/.changeset/recover-interleaved-electric-source-commits.md new file mode 100644 index 0000000000..567a350ab0 --- /dev/null +++ b/.changeset/recover-interleaved-electric-source-commits.md @@ -0,0 +1,7 @@ +--- +'@tanstack/db': patch +'@tanstack/db-sqlite-persistence-core': patch +'@tanstack/electric-db-collection': patch +--- + +Recover eager Electric collections from a fresh snapshot when interleaved source commits leave persisted rows inconsistent with the resume cursor. Keep cached rows visible while recovery restores the source state. diff --git a/.changeset/reserved-commit-defers-every-hydration.md b/.changeset/reserved-commit-defers-every-hydration.md new file mode 100644 index 0000000000..24a1f7499a --- /dev/null +++ b/.changeset/reserved-commit-defers-every-hydration.md @@ -0,0 +1,13 @@ +--- +'@tanstack/db-sqlite-persistence-core': patch +'@tanstack/db': patch +'@tanstack/electric-db-collection': patch +--- + +Keep a sync transaction that reserved its commit turn from failing when a persisted hydration starts while it is open. Before, only a subset load admitted during the reservation waited for the commit. A subset load admitted earlier, a reload or reset from a peer tab's notice, sequence-gap recovery, or a hydration that a shared scheduler held past its turn advanced the hydration cycle, and the commit then rejected with `a persisted sync transaction cannot cross a hydration cycle`. Electric reserves every stream transaction, so a deploy that made several tabs refetch at once could put their Collections into `error`. Each of these hydrations now waits outside the apply mutex until the reserved transaction commits, then starts. While it waits, readers keep the rows from before it. The `reserveCommitTurn` contract now covers every persisted hydration. + +A peer reset now applies in arrival order with peer commit notices, so a commit from before the reset can no longer publish a row that the reset removed. + +Electric now abandons its open transaction when its stream stops for good, so hydrations that wait for that transaction can run. + +An abandoned reservation now releases the subset loads that waited for it, as a commit does, so they run. Before, a load admitted while the turn was reserved rejected with `SyncTransactionAbortedError` when the source abandoned the transaction. diff --git a/.changeset/resume-independent-subscription-demand.md b/.changeset/resume-independent-subscription-demand.md new file mode 100644 index 0000000000..2afee066f3 --- /dev/null +++ b/.changeset/resume-independent-subscription-demand.md @@ -0,0 +1,18 @@ +--- +'@tanstack/db': patch +'@tanstack/react-db': patch +'@tanstack/svelte-db': patch +'@tanstack/react-router-with-db': patch +--- + +Start the required Collection sync run when an ordinary subscriber follows an abandoned suspended render. Deferred acquisition still starts no idle source sync run or acquisition attempt. + +Ordinary subscription admission and deferred acquisition resumption throw during cleanup, including automatic GC. Effects created then return disposed handles. Failed setup releases partial ownership, and throwing callbacks cannot prevent healthy observers receiving cleanup status. Subscribers surviving cleanup retain demand for an explicit restart. + +SSR captures each reader's rows and Collection status. Hydration preserves that view through browser preloads, runtime errors, sibling commits, and delayed streamed records. Missing records reject when transport ends. Known pending results survive normal record-stream close. A new stream after client cleanup starts independently of prior failures. + +Empty loading SSR needs no preload. Rendering rows or readiness requires matching query preparation or explicit source Collection preparation. Streaming Suspense prepares its result. Unsupported preparation throws with guidance. Prepared non-streaming Suspense remains supported. The DbClient query hash includes an infinite query's initial limit and offset. + +Apps without `DbClient` exclude its SSR implementation. Its API stays unchanged. + +Svelte roots initialized with `hydrate()` must set `hydrating` on `DbProvider`. Nested providers inherit it. Each provider clears initial hydration after mount so new children read live rows. diff --git a/.changeset/skip-complete-collection-setup.md b/.changeset/skip-complete-collection-setup.md new file mode 100644 index 0000000000..4ca3961c97 --- /dev/null +++ b/.changeset/skip-complete-collection-setup.md @@ -0,0 +1,7 @@ +--- +'@tanstack/db-sqlite-persistence-core': patch +--- + +Reloads no longer repeat setup that is already complete. A reopened persisted collection checks its tables, indexes, triggers, and bookkeeping rows in the same query that reads its registration, and runs its nine setup statements only when something is missing. Each adapter checks database-wide setup with one query and runs it only when a table, index, or migrated column is missing, instead of about 19 statements on every load. The write-only global `schema_version` row, which adapters with different schema versions overwrote on every load, is no longer created or written. + +A tab no longer rereads all of a collection's rows after its own write. It used to record the write's stream position before that write's commit notice arrived, so the notice looked like a gap and triggered `pullSince` and a full row scan. diff --git a/.changeset/solid-js-peer-cap.md b/.changeset/solid-js-peer-cap.md new file mode 100644 index 0000000000..a3d7c5460a --- /dev/null +++ b/.changeset/solid-js-peer-cap.md @@ -0,0 +1,5 @@ +--- +'@tanstack/solid-db': patch +--- + +Cap the `solid-js` peer range at `^1`. The previous open-ended `>=` range would accept Solid 2.0.0 once it is released, but these packages are built against Solid 1 and do not work with Solid 2. diff --git a/.changeset/sortedmap-merge-restore.md b/.changeset/sortedmap-merge-restore.md new file mode 100644 index 0000000000..7e308e8d1d --- /dev/null +++ b/.changeset/sortedmap-merge-restore.md @@ -0,0 +1,12 @@ +--- +'@tanstack/db': minor +'@tanstack/db-ivm': patch +--- + +Breaking: Collection keys now always match the declared key type, a string or a number. + +- A grouped live query that groups by one field now gives a `null`, `undefined`, boolean, or bigint group value a string key. Before, the key was the raw value, so `$key` was `null` for the `null` group. Read a group's key from its `$key`. String and number group values keep their keys. +- `sync.write` throws `UndefinedKeyError` or `InvalidKeyError` when `getKey` returns a key that is not a string or a number. Optimistic mutations already threw these errors. For a bigint key, the development message now shows the key instead of failing with a `TypeError`. +- `@tanstack/db` no longer exports `SortedMap`, and `SortedMap` no longer has `forEach`. `SortedMap` is the internal ordered map of Collection state. +- When one publication applies 512 or more sync operations, the Collection now merges the changed keys into the existing order. Before, it sorted all rows again. With 50,000 rows, a `compare` option, and 600 operations in each sync transaction, the load takes 181 ms instead of 1,589 ms. +- Keyed `MultiSet.consolidate` groups entries in nested maps. It no longer encodes each value as a string. diff --git a/.changeset/source-health-channel.md b/.changeset/source-health-channel.md new file mode 100644 index 0000000000..2ae77bce79 --- /dev/null +++ b/.changeset/source-health-channel.md @@ -0,0 +1,43 @@ +--- +'@tanstack/db': minor +'@tanstack/db-sqlite-persistence-core': minor +'@tanstack/electric-db-collection': minor +'@tanstack/powersync-db-collection': minor +'@tanstack/query-db-collection': minor +'@tanstack/rxdb-db-collection': minor +'@tanstack/trailbase-db-collection': minor +'@tanstack/react-db': minor +'@tanstack/preact-db': minor +'@tanstack/vue-db': minor +'@tanstack/solid-db': minor +'@tanstack/svelte-db': minor +'@tanstack/angular-db': minor +--- + +Collections now answer two questions separately: can the data be shown (`status`), and is it still kept up to date (`health`). Once a query has shown data, a network or backend failure never takes it off the screen. + +- `status` gains `crashed`: local storage broke (a closed IndexedDB connection, a failed SQLite persistence write or restore, a throwing sync function, or an invariant failure). Nothing recovers it in place, and `reload()` rejects. To recover, call `cleanup({ dependents: true })`, then start the Collection again (for example with `preload()`); it starts with no rows. It reaches every live query and Effect above it. `error` now means only "the first load failed". +- Every Collection has `collection.health`, a health node with `status`, `failureReason` (the adapter is still trying), `error` (the adapter stopped, or the first load failed), `reloading`, and `hasErrors`. A live query's `health` has one node per source alias, in query order, and keeps object identity while unchanged. `health:change` fires when it changes. `findProblems(health)` lists the nodes that have a problem, in query order. Each problem (`HealthProblem`) shows its newest failure in `message`, `cause`, and `at`, and lists every failure still open in `errors`, oldest first: each `reportError()` of the sync run until `reportHealthy()`, or each failed request for more data on a live query's node. One error value is one failure: reporting it again changes nothing, and `reportHealthy(error)` removes just that failure, so a query Collection whose failing Query recovers removes only that Query's failures. After a live query is ready, a failed request for more data (a page, a joined row, or a source reload's replay of its data) is an `error` on the query's own node; the source stays healthy. +- `collection.reload()` starts a new sync run and keeps the rows, status, and error on screen until the reload completes: when the new run's snapshot (`begin({ snapshot: true })`) or `truncate()` applies, or at its `markReady()`, which keeps the rows when no snapshot came. On a live query it reloads every Collection below it that shows an `error`, through nested live queries, then the query itself when its own node has an `error`, including a failed request for more data. It rejects on a `crashed` Collection. When the cleanup of the sync run that a reload retired rejects, the Collection crashes, also if the rejection comes later. A query in `error` stays there until its `reload()`. +- `cleanup()` rejects with `CollectionHasSubscribersError` while a live query with subscribers or an Effect reads the Collection. `cleanup({ dependents: true })` ends them first. `collection.subscribers` lists every dependent. +- A local-only Collection's `reload()` keeps its rows, including the user's writes: `initialData` loads only when the Collection starts, or starts again after cleanup. +- Persisted Collections have one status. With `initialRender: { strategy: 'network-first' }`, restored rows make the Collection `ready` once the deadline passes or the remote fails; an empty restore stays `loading`. Only a local storage failure crashes a persisted Collection: a failed remote subset request is a failure reason, which the next successful remote request clears. + +Breaking changes and migration: + +| Before | After | +| ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | +| hook `isError` | `isLoadingError` (first load failed) or `isCrashed` | +| — | `isSyncError`: `ready` and the health tree has a problem | +| `status: 'error'` after a crash | `status: 'crashed'` | +| adapter `markError(error)` | `fail(error)`. After readiness it sets the node's `error` and keeps the rows. Later calls throw. | +| `cleanup()` then `preload()` to recover | `reload()` | +| adapter `begin({ direct: true })` | `begin()`: every write applies on the rows the Collection holds, also during a reload | +| a reload's first commit replaced every row | `begin({ snapshot: true })` or `truncate()` in the new run replaces them | +| adapter `markReady()` after `fail()` threw | it does nothing | +| `cleanup()` while live queries or Effects read the Collection | rejects; unmount first or pass `{ dependents: true }` | +| `persistedStatus`, `isPersistedReady`, `persistedError`, `preloadForInitialRender()`, `PERSISTED_READINESS` | `status` and `health` | +| query-db `utils.lastError`, `utils.isError`, `utils.errorCount`, `utils.clearError()` | `collection.health`, `reload()` | +| infinite-query hooks' `error` (React, Preact, Vue, Svelte) | the root node's `error` in `health`; `fetchNextPage()` still resolves | + +Custom sync adapters: keep the run's readiness in your `sync` function. A reload calls `sync` again while `collection.status` still shows the previous run's status and the Collection still holds the previous run's rows. The new run's writes apply on those rows; it replaces every row only with a snapshot, a transaction begun with `begin({ snapshot: true })`, or with its own `truncate()`. The reload completes when that replacement applies, or at the new run's `markReady()`, which keeps the rows when no snapshot came. So send the new run's full state as a snapshot, or write against the kept rows: an `insert` of a key the Collection holds throws `DuplicateKeySyncError`, during a reload as at any time, so write `update` for a kept row, also from a replayed load after the reload completed. A reload keeps every row its new run does not rewrite: core never deletes kept rows on its own, so removing a row the backend deleted is the adapter's job, through a snapshot, a `delete`, or row ownership ([#2269](https://github.com/TanStack/db/issues/2269)). An on-demand `loadSubset` result is partial and is never a snapshot. A live query's own reload keeps its rows and its window until its new run's result is complete. `reportError()` and `reportHealthy()` throw before the first `markReady()` and after `fail()`. `markReady()` after the run is ready, or after its `fail()`, does nothing, so an adapter needs no flag of its own to guard it. A second `fail()` in the same sync run does nothing. A first-ready callback that throws while `sync` runs is reported uncaught, and the Collection stays ready. `reload()` called from inside `sync` or a `loadSubset` call rejects, unless a reload is already in flight, whose promise it returns. Calls after a crash do nothing. For an on-demand Collection, a reload aborts the retired run's `loadSubset` calls and asks the new run's `loadSubset` for the current demand as soon as `sync` returns. Honor the `signal`: when core aborts a load, that load is over for core, and its later resolve or reject decides nothing, holds no readiness, and no replay waits for it. A `loadSubset` that throws synchronously is a rejection: a failed load, which `requestSnapshot()` and `setWindow()` report without throwing. A truncate's or reload's replay decides every request error of the query's demand: its success clears them all, and its failure replaces them with the replay's failures. `useLiveSuspenseQuery` now throws an Error for a failed first load or a crash: the cause itself when it is an Error, otherwise an Error with the problem's message whose `cause` holds the reported value. diff --git a/.changeset/sqlite-connection-closed-error.md b/.changeset/sqlite-connection-closed-error.md new file mode 100644 index 0000000000..dfcfc04c29 --- /dev/null +++ b/.changeset/sqlite-connection-closed-error.md @@ -0,0 +1,6 @@ +--- +'@tanstack/db-sqlite-persistence-core': patch +'@tanstack/browser-db-sqlite-persistence': patch +--- + +Add `SQLiteConnectionClosedError`. A browser OPFS connection now rejects requests made after close starts with this error instead of `InvalidPersistedCollectionConfigError`. diff --git a/.changeset/svelte-db-query-identity.md b/.changeset/svelte-db-query-identity.md new file mode 100644 index 0000000000..e60be451c1 --- /dev/null +++ b/.changeset/svelte-db-query-identity.md @@ -0,0 +1,7 @@ +--- +'@tanstack/svelte-db': minor +--- + +**Breaking (1.0):** a `useLiveQuery` backed by a `DbClient` matches queries by identity hash, so it now throws `UnhashableQueryIRError` for a query without a stable IR identity unless it supplies a nonempty `queryKey`. It previously compiled the query without a hydration key. While such a hook is mounted, a different source Collection under a Collection ID it already read throws `SourceCollectionIdReuseError`, and every result property then throws instead of returning the previous query's state. Queries without a `DbClient` are unchanged. + +A rejected client-backed query now releases every sync start it deferred, so `startSyncImmediate()` or another hook can still start that source. A setup that throws after the hook returns releases them when its live query is reclaimed. `useLiveInfiniteQuery` now uses the shared window binding from `@tanstack/db`. diff --git a/.changeset/sync-metadata-session-reads.md b/.changeset/sync-metadata-session-reads.md new file mode 100644 index 0000000000..d0d355e49d --- /dev/null +++ b/.changeset/sync-metadata-session-reads.md @@ -0,0 +1,9 @@ +--- +'@tanstack/db': patch +'@tanstack/db-sqlite-persistence-core': patch +'@tanstack/query-db-collection': patch +--- + +Fix sync metadata reads that missed committed sync writes while an optimistic transaction held them. A sync source now reads its own writes in commit order: `metadata.row.get`, `metadata.collection.get`, and `list()` include committed transactions that have not applied yet, then the open transactions. A transaction begun inside an open one is independent, and a hold no longer changes commit order. A source insert over a different source row is a duplicate even when a seed or a hold is present. + +Persisted collections treat cached rows as seeds, so a later `DbClient` seed can replace them, also after another tab's notice makes the collection reload from storage. A reload from another tab's notice keeps a seed accepted during a pending transaction, keeps row metadata of rows that do not exist, and keeps a seed's row metadata unless the notice names it. An update over a `DbClient` seed now stores the whole row the collection shows, so a reload or restart keeps the seed's fields. With `rowUpdateMode: 'full'`, an update now replaces its stored row, so a reload or restart no longer brings back fields the update dropped. A reload deletes rows that storage lacks also while a pending transaction holds them. After a notice that requires a full reload, a later notice that names its rows keeps a seed's row metadata again. A schema reset from another tab clears every row and its metadata; in on-demand mode it keeps the `DbClient` seeds the collection holds and still replays demand. Rows that storage holds after a reset keep their metadata, and an equal re-insert of one keeps it too. A commit whose signal aborts before the persisted wrapper applies it no longer takes a `DbClient` seed's place. An insert in the persisted wrapper replaces the row it lands on and writes an `insert` mutation to storage. An equal re-insert keeps the row's metadata without writing it, and the wrapper no longer slows down quadratically with the size of a transaction. diff --git a/.changeset/tidy-opfs-close.md b/.changeset/tidy-opfs-close.md new file mode 100644 index 0000000000..00e386700c --- /dev/null +++ b/.changeset/tidy-opfs-close.md @@ -0,0 +1,5 @@ +--- +'@tanstack/browser-db-sqlite-persistence': patch +--- + +Reject pending OPFS requests when closing the SQLite connection, including when the worker reports a close error. Close now stops accepting requests when it starts, and concurrent `close()` calls share one outcome. The OPFS worker handles one request at a time, so close waits for earlier queries instead of failing while one waits for the OPFS access handle. diff --git a/.changeset/vue-db-window-binding.md b/.changeset/vue-db-window-binding.md new file mode 100644 index 0000000000..136a0feb96 --- /dev/null +++ b/.changeset/vue-db-window-binding.md @@ -0,0 +1,5 @@ +--- +'@tanstack/vue-db': patch +--- + +`useLiveInfiniteQuery` now uses the shared window binding from `@tanstack/db`. A window collection it creates starts syncing when the query compiles, as in the other adapters, and an update that changes nothing keeps the current controller. diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000000..5883c8c69d --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,27 @@ +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "Edit|MultiEdit|Write", + "hooks": [ + { + "type": "command", + "command": "node \"$CLAUDE_PROJECT_DIR/scripts/agent-lint-feedback.mjs\" --format=claude --event=edit", + "timeout": 30 + } + ] + } + ], + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "node \"$CLAUDE_PROJECT_DIR/scripts/agent-lint-feedback.mjs\" --format=claude --event=stop", + "timeout": 120 + } + ] + } + ] + } +} diff --git a/.codex/hooks.json b/.codex/hooks.json new file mode 100644 index 0000000000..7e2f706b19 --- /dev/null +++ b/.codex/hooks.json @@ -0,0 +1,27 @@ +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "apply_patch", + "hooks": [ + { + "type": "command", + "command": "node \"$(git rev-parse --show-toplevel)/scripts/agent-lint-feedback.mjs\" --format=codex --event=edit", + "timeout": 30 + } + ] + } + ], + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "node \"$(git rev-parse --show-toplevel)/scripts/agent-lint-feedback.mjs\" --format=codex --event=stop", + "timeout": 120 + } + ] + } + ] + } +} diff --git a/.gemini/settings.json b/.gemini/settings.json new file mode 100644 index 0000000000..c83f4593b0 --- /dev/null +++ b/.gemini/settings.json @@ -0,0 +1,29 @@ +{ + "hooks": { + "AfterTool": [ + { + "matcher": "write_file|replace", + "hooks": [ + { + "type": "command", + "name": "oracle-lint-edit", + "command": "node \"$GEMINI_PROJECT_DIR/scripts/agent-lint-feedback.mjs\" --format=gemini --event=edit", + "timeout": 30000 + } + ] + } + ], + "AfterAgent": [ + { + "hooks": [ + { + "type": "command", + "name": "oracle-lint-turn", + "command": "node \"$GEMINI_PROJECT_DIR/scripts/agent-lint-feedback.mjs\" --format=gemini --event=stop", + "timeout": 120000 + } + ] + } + ] + } +} diff --git a/.github/SSR_RELEASE_PLAN.md b/.github/SSR_RELEASE_PLAN.md new file mode 100644 index 0000000000..8df2f4fbbf --- /dev/null +++ b/.github/SSR_RELEASE_PLAN.md @@ -0,0 +1,69 @@ +# TanStack DB SSR Release Plan + +## Release Goal + +Ship TanStack DB SSR as a single coherent story: + +- explicit collection-row hydration and live-query result snapshots through + `DbClient` +- React and Svelte provider and descriptor resolution +- derived live query identity with `queryKey` only when necessary +- backwards-compatible dependency arrays with dev warnings until 1.0 +- a working TanStack Start demo and E2E proof + +## Pre-release Validation + +- Run `pnpm --filter @tanstack/db test`. +- Run `pnpm --filter @tanstack/react-db test`. +- Run `pnpm --filter @tanstack/svelte-db test`. +- Run `pnpm --filter @tanstack/react-router-with-db test` (includes type + tests). +- Run `pnpm --filter @tanstack/query-db-collection test`. +- Run `pnpm --filter @tanstack/db-sqlite-persistence-core test`. +- Run `pnpm --filter @tanstack/db-example-react-start-ssr-e2e test:e2e`. +- Run `pnpm --filter @tanstack/db-example-react-next-ssr-e2e test:e2e`. +- Run `pnpm test:docs`. +- Run `pnpm test:sherif`. +- Run `pnpm build`. + +## Demo + +- Live URL: https://tanstack-db-ssr-demo.netlify.app/ssr-db +- Deploy `examples/react/start-ssr-e2e` to an SSR-capable host. +- Verify the deployed `/ssr-db` route serves SSR HTML with hydrated rows. +- Verify browser hydration succeeds without console/page errors. +- Verify the streamed collection chunk updates the live query. +- Verify `/ssr-db-stream` streams a projected result, omits source-only data, + and hands off to browser sync. +- Run `PLAYWRIGHT_BASE_URL=https://tanstack-db-ssr-demo.netlify.app pnpm --filter @tanstack/db-example-react-start-ssr-e2e test:e2e:hosted`. +- Add the live URL to the PR description and release notes. + +## Docs + +- Publish the [SSR and Hydration guide](../docs/guides/ssr.md). +- Link the guide from overview, quick start, live queries, and React overview. +- Regenerate API reference docs in a dedicated docs-maintenance pass if broad + TypeDoc output churn is acceptable. +- Confirm docs explain when `queryKey` is necessary and when it should be + omitted. +- Confirm docs say dependency arrays warn now and are removed in 1.0. + +## Migration Messaging + +- Lead with: explicit collection preloads transport normalized rows; live-query + preloads transport only their result snapshot. +- Emphasize that existing apps keep working. +- State that `createCollection(...)` remains available, but SSR apps should use + `collectionOptions(...)` plus `DbClient`. +- Explain that React dependency arrays are deprecated with a 1.0 removal path. +- Show `queryKey` only for opaque functional query logic or hot render paths. + +## Announcement Checklist + +- PR description includes high-level summary, migration cheat sheet, and test + commands. +- Release notes include a "No removals in this release" compatibility section. +- Discord announcement links the SSR guide and live demo. +- Example migration diff is available from the Start SSR demo. +- Follow-up issues are filed for the remaining framework adapters and API + reference generation if they are not part of the shipping PR. diff --git a/.github/hooks/oracle-lint.json b/.github/hooks/oracle-lint.json new file mode 100644 index 0000000000..3e4218d927 --- /dev/null +++ b/.github/hooks/oracle-lint.json @@ -0,0 +1,20 @@ +{ + "version": 1, + "hooks": { + "postToolUse": [ + { + "type": "command", + "matcher": "edit|create", + "bash": "node scripts/agent-lint-feedback.mjs --format=copilot --event=edit", + "timeoutSec": 30 + } + ], + "agentStop": [ + { + "type": "command", + "bash": "node scripts/agent-lint-feedback.mjs --format=copilot --event=stop", + "timeoutSec": 120 + } + ] + } +} diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index d8734b0901..14a0149f81 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -2,6 +2,12 @@ +## 🔬 Oracle Coverage + + + + + ## ✅ Checklist - [ ] I have tested this code locally with `pnpm test`. diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 0000000000..26d5ae08ab --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,95 @@ +name: Benchmark + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + paths: + - 'packages/db/**' + - 'packages/db-ivm/**' + - 'scripts/bench/**' + - '.github/workflows/benchmark.yml' + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + benchmark: + name: Incremental Update Benchmark + # Skip draft PRs; the benchmark runs once a PR is marked ready for review + # (and on subsequent pushes to it). + if: github.event_name == 'workflow_dispatch' || github.event.pull_request.draft == false + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + persist-credentials: false + - name: Setup Tools + uses: tanstack/config/.github/setup@e4b48f16568324f76f467aa4c2aac2f05db632c3 + - name: Run benchmark on PR + run: | + pnpm --filter @tanstack/db-ivm build + pnpm exec tsx scripts/bench/incremental-update.ts \ + --iterations=100 --warmup=20 \ + --outFile="$RUNNER_TEMP/candidate.json" + - name: Run benchmark on base + env: + BASE_REF: ${{ github.event.pull_request.base.ref || 'main' }} + run: | + HEAD_SHA=$(git rev-parse HEAD) + BASE_SHA=$(git merge-base "origin/$BASE_REF" HEAD) + echo "Benchmarking base commit $BASE_SHA" + git checkout --force "$BASE_SHA" + # Run the exact same benchmark code on both sides + git checkout "$HEAD_SHA" -- scripts/bench + pnpm install --frozen-lockfile + pnpm --filter @tanstack/db-ivm build + pnpm exec tsx scripts/bench/incremental-update.ts \ + --iterations=100 --warmup=20 \ + --outFile="$RUNNER_TEMP/base.json" + - name: Compare results + run: | + pnpm exec tsx scripts/bench/compare-incremental-update.ts \ + --base="$RUNNER_TEMP/base.json" \ + --candidate="$RUNNER_TEMP/candidate.json" \ + --outFile="$RUNNER_TEMP/comparison.md" + cat "$RUNNER_TEMP/comparison.md" >> "$GITHUB_STEP_SUMMARY" + - name: Upload reports + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: benchmark-reports + path: | + ${{ runner.temp }}/base.json + ${{ runner.temp }}/candidate.json + ${{ runner.temp }}/comparison.md + - name: Comment on PR + if: github.event_name == 'pull_request' + # PRs from forks get a read-only token; the job summary and artifacts + # still carry the results. + continue-on-error: true + env: + GH_TOKEN: ${{ github.token }} + PR_NUMBER: ${{ github.event.pull_request.number }} + REPO: ${{ github.repository }} + run: | + MARKER='' + BODY_FILE="$RUNNER_TEMP/comment.md" + { echo "$MARKER"; cat "$RUNNER_TEMP/comparison.md"; } > "$BODY_FILE" + COMMENT_ID=$(gh api "repos/$REPO/issues/$PR_NUMBER/comments" --paginate \ + --jq ".[] | select(.body | startswith(\"$MARKER\")) | .id" | head -n1) + if [ -n "$COMMENT_ID" ]; then + gh api -X PATCH "repos/$REPO/issues/comments/$COMMENT_ID" -F body=@"$BODY_FILE" > /dev/null + else + gh api "repos/$REPO/issues/$PR_NUMBER/comments" -F body=@"$BODY_FILE" > /dev/null + fi diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 3678cd19f2..486eb1e720 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -5,15 +5,22 @@ on: branches: [main] pull_request: +concurrency: + group: ${{ github.workflow }}-${{ github.event.number || github.ref }} + cancel-in-progress: true + permissions: contents: read jobs: - e2e-tests: - name: Run E2E Tests + e2e-group: + name: E2E (${{ matrix.group }}) + strategy: + fail-fast: false + matrix: + group: [browser, desktop, cloud-mobile] runs-on: ubuntu-latest timeout-minutes: 35 - steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -35,21 +42,42 @@ jobs: run: pnpm install --frozen-lockfile - name: Start Docker services + if: matrix.group == 'browser' run: | cd packages/db-collection-e2e/docker docker compose up -d echo "Waiting for services to be healthy..." timeout 60 bash -c 'until docker compose ps | grep -q "healthy"; do sleep 2; done' - - name: Build packages - run: | - pnpm --filter @tanstack/db-ivm build - pnpm --filter @tanstack/db build - pnpm --filter @tanstack/electric-db-collection build - pnpm --filter @tanstack/offline-transactions build - pnpm --filter @tanstack/query-db-collection build + - name: Build browser dependencies + if: matrix.group == 'browser' + run: >- + pnpm + --filter '@tanstack/react-router-with-db...' + --filter '@tanstack/electric-db-collection...' + --filter '@tanstack/query-db-collection...' + --filter '@tanstack/convex-db-collection...' + --filter '@tanstack/db...' + build + + - name: Build desktop dependencies + if: matrix.group == 'desktop' + run: >- + pnpm + --filter '@tanstack/node-db-sqlite-persistence...' + --filter '@tanstack/electron-db-sqlite-persistence...' + build + + - name: Build cloud and mobile dependencies + if: matrix.group == 'cloud-mobile' + run: >- + pnpm + --filter '@tanstack/cloudflare-durable-objects-db-sqlite-persistence...' + --filter '@tanstack/react-native-db-sqlite-persistence...' + build - name: Run Electric E2E tests + if: matrix.group == 'browser' run: | cd packages/electric-db-collection pnpm test:e2e @@ -61,34 +89,113 @@ jobs: POSTGRES_PASSWORD: password POSTGRES_DB: e2e_test + - name: Run Convex backend E2E tests + if: matrix.group == 'browser' + run: pnpm --filter @tanstack/convex-db-collection test:e2e + - name: Run Query E2E tests + if: matrix.group == 'browser' run: | cd packages/query-db-collection pnpm test:e2e env: ELECTRIC_URL: http://localhost:3000 + - name: Install Playwright browsers + if: matrix.group == 'browser' + run: | + cd examples/react/start-ssr-e2e + pnpm exec playwright install --with-deps chromium + + - name: Install IndexedDB oracle browsers + if: matrix.group == 'browser' + working-directory: packages/db + run: pnpm exec playwright install --with-deps chromium firefox webkit + env: + PLAYWRIGHT_SKIP_BROWSER_GC: '1' + + - name: Run IndexedDB native cross-tab oracles + if: matrix.group == 'browser' + working-directory: packages/db + run: pnpm test:indexed-db:browser + + - name: Run Collection GC native idle oracle + if: matrix.group == 'browser' + working-directory: packages/db + run: pnpm test:collection-gc:browser + + - name: Upload IndexedDB oracle evidence + if: always() && matrix.group == 'browser' + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: indexeddb-oracle-evidence + path: | + packages/db/test-results/indexed-db/ + packages/db/test-results/collection-gc/ + packages/db/test-results/oracles/ + retention-days: 7 + + - name: Run Browser SQLite OPFS fairness E2E tests + if: matrix.group == 'browser' + run: | + cd packages/browser-db-sqlite-persistence + pnpm test:opfs-fairness + + - name: Run Browser SQLite packaged consumer E2E tests + if: matrix.group == 'browser' + # Build browser dependencies already built this package's workspace + # dependencies; test:e2e would rebuild them. + run: | + cd packages/browser-db-sqlite-persistence + pnpm build + pnpm exec vitest --config vitest.e2e.config.ts --run + + - name: Run Browser SQLite OPFS with live Electric E2E tests + if: matrix.group == 'browser' + run: | + cd packages/browser-db-sqlite-persistence + pnpm test:opfs-electric + env: + ELECTRIC_URL: http://localhost:3000 + + - name: Run React Start SSR E2E tests + if: matrix.group == 'browser' + run: | + cd examples/react/start-ssr-e2e + pnpm exec playwright test + + - name: Run Next.js SSR E2E tests + if: matrix.group == 'browser' + run: | + cd examples/react/next-ssr-e2e + pnpm exec playwright test + - name: Run Node SQLite persisted collection E2E tests + if: matrix.group == 'desktop' run: | cd packages/node-db-sqlite-persistence - pnpm test:e2e + pnpm exec vitest --config vitest.e2e.config.ts --run - name: Run Electron SQLite persisted collection E2E tests + if: matrix.group == 'desktop' run: | cd packages/electron-db-sqlite-persistence - pnpm test:e2e + pnpm exec vitest --config vitest.e2e.config.ts --run - name: Run Cloudflare Durable Object persisted collection E2E tests + if: matrix.group == 'cloud-mobile' run: | cd packages/cloudflare-durable-objects-db-sqlite-persistence - pnpm test:e2e + pnpm exec vitest --config vitest.e2e.config.ts --run - name: Run React Native/Expo persisted collection E2E tests + if: matrix.group == 'cloud-mobile' run: | cd packages/react-native-db-sqlite-persistence - pnpm test:e2e + pnpm exec vitest --config vitest.e2e.config.ts --run - name: Run React Native/Expo runtime E2E lane + if: matrix.group == 'cloud-mobile' run: | is_fork_pr=false require_runtime_lane=${TANSTACK_DB_REQUIRE_MOBILE_RUNTIME_LANE:-0} @@ -114,14 +221,14 @@ jobs: fi cd packages/react-native-db-sqlite-persistence - pnpm test:e2e:runtime + TANSTACK_DB_MOBILE_REQUIRE_RUNTIME_FACTORY=1 pnpm exec vitest --config vitest.e2e.config.ts --run env: TANSTACK_DB_MOBILE_SQLITE_FACTORY_MODULE: ${{ vars.TANSTACK_DB_MOBILE_SQLITE_FACTORY_MODULE }} TANSTACK_DB_MOBILE_SQLITE_FACTORY_EXPORT: ${{ vars.TANSTACK_DB_MOBILE_SQLITE_FACTORY_EXPORT }} TANSTACK_DB_REQUIRE_MOBILE_RUNTIME_LANE: ${{ vars.TANSTACK_DB_REQUIRE_MOBILE_RUNTIME_LANE }} - name: Stop Docker services - if: always() + if: always() && matrix.group == 'browser' run: | cd packages/db-collection-e2e/docker docker compose down -v @@ -130,6 +237,17 @@ jobs: if: failure() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: - name: test-results + name: test-results-${{ matrix.group }} path: packages/db-collection-e2e/junit/ retention-days: 7 + + e2e-tests: + name: Run E2E Tests + if: always() + needs: [e2e-group] + runs-on: ubuntu-latest + steps: + - name: Require every E2E group to succeed + env: + E2E_RESULT: ${{ needs.e2e-group.result }} + run: test "$E2E_RESULT" = success diff --git a/.github/workflows/oracle-nightly.yml b/.github/workflows/oracle-nightly.yml new file mode 100644 index 0000000000..b93bf13492 --- /dev/null +++ b/.github/workflows/oracle-nightly.yml @@ -0,0 +1,35 @@ +name: Nightly oracles + +# Longer oracle campaigns than PR CI can afford. The generated demand +# coverage campaign runs with ten times its PR budget, and every hostile +# mutant in scripts/demand-coverage-mutants.mjs must fail a named test. +on: + schedule: + - cron: '17 6 * * *' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + demand-coverage: + name: Demand coverage campaigns and mutants + runs-on: ubuntu-latest + timeout-minutes: 240 + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Setup Tools + uses: tanstack/config/.github/setup@e4b48f16568324f76f467aa4c2aac2f05db632c3 + - name: Run the generated campaign with a larger budget + env: + TANSTACK_DB_ORACLE_RUNS_MULTIPLIER: '10' + run: pnpm --filter @tanstack/db-sqlite-persistence-core exec vitest run --coverage.enabled=false tests/persisted-demand-coverage-campaign-oracle.test.ts + - name: Run the demand coverage mutants + run: node scripts/demand-coverage-mutants.mjs diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 48f12e07c8..7b3eef4329 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -10,31 +10,110 @@ concurrency: group: ${{ github.workflow }}-${{ github.event.number || github.ref }} cancel-in-progress: true -env: - NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - permissions: contents: read jobs: - test: - name: Test + lint: + name: Lint runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: + # The run-budget report compares changed files with the merge-base. fetch-depth: 0 persist-credentials: false - name: Setup Tools uses: tanstack/config/.github/setup@e4b48f16568324f76f467aa4c2aac2f05db632c3 - - name: Get base and head commits for `nx affected` - uses: nrwl/nx-set-shas@3e9ad7370203c1e93d109be57f3b72eb0eb511b1 # v4.4.0 + - name: Check lint + run: pnpm run lint:check + - name: Check release versioning + run: pnpm run test:release + - name: Typecheck doc and skill examples + run: pnpm run test:doc-snippets + - name: Check oracle lint without inline directives + run: pnpm run lint:oracles + - name: Report lowered oracle run budgets + run: node scripts/check-oracle-run-counts.mjs --format=github + - name: Test oracle lint rules + run: pnpm run test:lint-rules + minified-db: + name: Minified DB public API + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - main-branch-name: main - - name: Run Checks - run: pnpm run build && pnpm run test + persist-credentials: false + - name: Setup Tools + uses: tanstack/config/.github/setup@e4b48f16568324f76f467aa4c2aac2f05db632c3 + - name: Check private-member mangle cache + run: pnpm run check:mangle + - name: Build DB packages + run: pnpm --filter @tanstack/db-ivm --filter @tanstack/db --filter @tanstack/react-db build + - name: Check minified public API + run: pnpm run test:minified-db + - name: Check client-only query bundles + run: pnpm run test:ssr-bundle + - name: Check production error text + run: pnpm run test:production-errors + - name: Check DB tests against built dist + run: pnpm --filter @tanstack/db test:dist + test-db: + name: Test (${{ matrix.group }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + group: [db-1, db-2, db-replay] + env: + TEST_GROUP: ${{ matrix.group }} + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Setup Tools + uses: tanstack/config/.github/setup@e4b48f16568324f76f467aa4c2aac2f05db632c3 + - name: Build dependencies + run: node scripts/ci-tests.mjs build "$TEST_GROUP" + - name: Run tests + run: node scripts/ci-tests.mjs test "$TEST_GROUP" + - name: Upload DB report + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: db-report-${{ matrix.group }} + path: packages/db/.vitest-reports/*.json + include-hidden-files: true + if-no-files-found: error + retention-days: 1 + test-packages: + name: Test (${{ matrix.group }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + group: + [ivm-offline, sqlite-core, sqlite-adapters, frameworks, collections] + env: + TEST_GROUP: ${{ matrix.group }} + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Setup Tools + uses: tanstack/config/.github/setup@e4b48f16568324f76f467aa4c2aac2f05db632c3 + - name: Build dependencies + run: node scripts/ci-tests.mjs build "$TEST_GROUP" + - name: Typecheck Test Sources + if: matrix.group == 'ivm-offline' + run: pnpm run typecheck:tests + - name: Run tests + run: node scripts/ci-tests.mjs test "$TEST_GROUP" - name: Check dependency versions + if: matrix.group == 'ivm-offline' run: | if ! pnpm run test:sherif; then echo "" @@ -48,6 +127,32 @@ jobs: echo "==========================================" exit 1 fi + test: + name: Test + if: always() + needs: [lint, test-db, test-packages] + runs-on: ubuntu-latest + steps: + - name: Require lint and every test group to succeed + env: + LINT_RESULT: ${{ needs.lint.result }} + DB_RESULT: ${{ needs.test-db.result }} + PACKAGES_RESULT: ${{ needs.test-packages.result }} + run: test "$LINT_RESULT" = success && test "$DB_RESULT" = success && test "$PACKAGES_RESULT" = success + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Setup Tools + uses: tanstack/config/.github/setup@e4b48f16568324f76f467aa4c2aac2f05db632c3 + - name: Download DB reports + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + pattern: db-report-* + path: packages/db/.vitest-reports + merge-multiple: true + - name: Merge DB coverage and test reports + run: pnpm --dir packages/db exec vitest --mergeReports .vitest-reports preview: name: Preview runs-on: ubuntu-latest @@ -66,19 +171,30 @@ jobs: run: pnpm run build - name: Publish Previews run: pnpx pkg-pr-new publish --pnpm --compact './packages/*' --template './examples/*/*' - - name: Compressed Size Action - DB Package - uses: preactjs/compressed-size-action@66325aad6443cb7cf89c4bfcd414aea2367cda94 # 2.9.1 + size: + name: Compressed size + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - repo-token: '${{ secrets.GITHUB_TOKEN }}' - pattern: './packages/db/dist/**/*.{js,mjs}' - comment-key: 'db-package-size' - build-script: 'build:minified' - - name: Compressed Size Action - React DB Package + fetch-depth: 0 + persist-credentials: false + - name: Setup Tools + uses: tanstack/config/.github/setup@e4b48f16568324f76f467aa4c2aac2f05db632c3 + - name: Compare DB and React DB sizes uses: preactjs/compressed-size-action@66325aad6443cb7cf89c4bfcd414aea2367cda94 # 2.9.1 with: repo-token: '${{ secrets.GITHUB_TOKEN }}' - pattern: './packages/react-db/dist/**/*.{js,mjs}' - comment-key: 'react-db-package-size' + pattern: './packages/{db,react-db}/dist/**/*.{js,mjs}' + comment-key: 'db-and-react-package-size' + # The action installs and builds both head and base. Use commands + # already present on the base, including the minified build's deps. + install-script: bash -c "pnpm install --frozen-lockfile && pnpm --filter @tanstack/react-router-with-db... build" build-script: 'build:minified' build-example: name: Build Example Site @@ -92,10 +208,17 @@ jobs: uses: tanstack/config/.github/setup@e4b48f16568324f76f467aa4c2aac2f05db632c3 - name: Build Packages run: pnpm run build + - name: Check action enforcement example + run: | + pnpm --filter @tanstack/db-example-react-action-enforcement test + pnpm --filter @tanstack/db-example-react-action-enforcement lint + pnpm --filter @tanstack/db-example-react-action-enforcement build - name: Build Example Site run: | cd examples/react/todo pnpm build + - name: Build Angular Todo Example + run: pnpm --dir examples/angular/todos build build-starter: name: Build Example Site runs-on: ubuntu-latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6ec135f943..825a0f5f10 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,15 +12,94 @@ env: NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} permissions: - contents: write - id-token: write - pull-requests: write + contents: read jobs: + lint: + name: Lint + if: github.repository_owner == 'TanStack' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Setup Tools + uses: tanstack/config/.github/setup@e4b48f16568324f76f467aa4c2aac2f05db632c3 + - name: Check lint + run: pnpm run lint:check + - name: Check release versioning + run: pnpm run test:release + + test-db: + name: Test (${{ matrix.group }}) + if: github.repository_owner == 'TanStack' + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + group: [db-1, db-2, db-replay] + env: + TEST_GROUP: ${{ matrix.group }} + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Setup Tools + uses: tanstack/config/.github/setup@e4b48f16568324f76f467aa4c2aac2f05db632c3 + - name: Build dependencies + run: node scripts/ci-tests.mjs build "$TEST_GROUP" + - name: Run tests + run: node scripts/ci-tests.mjs test "$TEST_GROUP" + - name: Upload DB report + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: db-report-${{ matrix.group }} + path: packages/db/.vitest-reports/*.json + include-hidden-files: true + if-no-files-found: error + retention-days: 1 + + test-packages: + name: Test (${{ matrix.group }}) + if: github.repository_owner == 'TanStack' + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + group: + [ivm-offline, sqlite-core, sqlite-adapters, frameworks, collections] + env: + TEST_GROUP: ${{ matrix.group }} + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Setup Tools + uses: tanstack/config/.github/setup@e4b48f16568324f76f467aa4c2aac2f05db632c3 + - name: Build dependencies + run: node scripts/ci-tests.mjs build "$TEST_GROUP" + - name: Typecheck Test Sources + if: matrix.group == 'ivm-offline' + run: pnpm run typecheck:tests + - name: Run tests + run: node scripts/ci-tests.mjs test "$TEST_GROUP" + - name: Check dependency versions + if: matrix.group == 'ivm-offline' + run: pnpm run test:sherif + release: name: Release + needs: [lint, test-db, test-packages] if: github.repository_owner == 'TanStack' runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + issues: write + pull-requests: write steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -29,8 +108,16 @@ jobs: persist-credentials: true # release job pushes version/docs changes - name: Setup Tools uses: tanstack/config/.github/setup@e4b48f16568324f76f467aa4c2aac2f05db632c3 - - name: Run Tests - run: pnpm run lint && pnpm run build && pnpm run test + - name: Download DB reports + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + pattern: db-report-* + path: packages/db/.vitest-reports + merge-multiple: true + - name: Merge DB coverage and test reports + run: pnpm --dir packages/db exec vitest --mergeReports .vitest-reports + - name: Build packages + run: pnpm run build - name: Run Changesets (version or publish) id: changesets uses: changesets/action@e0145edc7d9d8679003495b11f87bd8ef63c0cba # v1.5.3 @@ -89,3 +176,6 @@ jobs: uses: tanstack/config/.github/comment-on-release@e4b48f16568324f76f467aa4c2aac2f05db632c3 with: published-packages: ${{ steps.changesets.outputs.publishedPackages }} + env: + # The pinned action reads REPOSITORY but does not set it. + REPOSITORY: ${{ github.repository }} diff --git a/.gitignore b/.gitignore index 4ad9ee25d4..d1bb2024cd 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,10 @@ yarn.lock # builds build coverage +.vitest-reports dist +playwright-report +test-results # misc .DS_Store @@ -19,6 +22,7 @@ dist .env.test.local .env.production.local .next +next-env.d.ts npm-debug.log* yarn-debug.log* @@ -44,3 +48,11 @@ examples/react-native/shopping-list/ios/ vite.config.js.timestamp-* vite.config.ts.timestamp-* tsconfig.vitest-temp.json + +# The part-reach oracle lints scratch programs inside the repository; an +# interrupted run can leave them behind. +/part-reach-scratch-*/ + +# Other checkouts and scratch copies. A plain directory here is not part of +# this checkout's branch, so the oracle lint hooks must not see its files. +/.worktrees/ diff --git a/.prettierignore b/.prettierignore index 9a9a6363b3..20eda14e54 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,3 +7,5 @@ **/snap pnpm-lock.yaml **/routeTree.gen.ts +packages/convex-db-collection/e2e/backend/.convex/ +packages/convex-db-collection/e2e/backend/convex/_generated/ diff --git a/AGENTS.md b/AGENTS.md index a92ff46761..e9dabc568d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,6 +2,198 @@ This guide provides principles and patterns for AI agents contributing to the TanStack DB codebase. These guidelines are derived from PR review patterns and reflect the quality standards expected in this project. +## Think like a scientist + +Treat the code as an implementation of laws about observable behavior. Before +changing a subsystem, articulate the law, its authority, and the conditions +under which it applies. Think hard about whether it is the right law and whether +we fully enforce it. Existing behavior is evidence about the implementation; +it is not, by itself, authority for the promise. + +Use oracles as instruments: a small independent model enacts a law, legal +histories exercise it, and public observations test production against it. +Seek cases that distinguish plausible explanations. A mismatch can expose a +production bug, a wrong model or law, missing information at the production +boundary, or an unsuitable observation checkpoint. A green oracle can still +miss a violation when its histories, path, observation checkpoint, or +comparison are insufficient. +Correctly implemented behavior can also fail the user's need. Use the +[repair-location table](docs/contributing/instruments/adversarial-review.md#locate-the-repair) +after diagnosing which account failed, before changing expectations. + +Challenge the laws themselves when the task warrants it: their suitability, +interactions, and underlying concepts can need revision. Keep proposed design +changes distinct from defects under the accepted contract. Preserve useful +implementation freedom and state what the evidence does and does not establish. + +Before creating or editing an oracle, load +[oracle-authoring](.agents/skills/oracle-authoring/SKILL.md). This applies to +an edit of an oracle or of the production driver, history grammar, or +comparison that it uses. It also applies when the edit fixes a bug or a review +finding. +Before reviewing an oracle, load +[oracle-review](.agents/skills/oracle-review/SKILL.md). Both use the +oracle guide below and a small [instrument library](docs/contributing/instruments/index.md) +that is also available for other design work. Select instruments as needed; +routine work does not require a full instrument sequence. + +## How oracle work is scored + +Every behavior change in an area that an oracle owns has two possible paths. +The maintainer runs hostile auditors on every pull request. The auditors look +for the shortcuts listed below and report each one. Pick the path with this in +mind: a shortcut does not finish the task sooner. It moves the work into +review, where it costs more. + +### The oracle-first path + +1. State the law and its authority. +2. Extend the oracle so that generated histories reach the state where the law + matters. Declare that state with + [`createOracleReach`](packages/db/tests/oracle-reach.ts), and show that the + generated histories reach it. +3. Run the oracle against the unchanged production code. It fails: this is the + RED result, and it proves that the oracle sees the defect. +4. Change production. The oracle gives immediate feedback on each attempt, + including attempts that break a neighboring law. +5. Show that a hostile mutant, such as the original defect, fails the oracle. + +On this path, you know the fix is correct before you push it. Reviewers find +little that is new, so the total work and token spend stay low. Later agents +inherit a clean model of the subsystem that they can extend in place. + +### The shortcut path + +Change production first, then adjust the oracle until it passes: relax a +comparison, add an exclusion, change a model rule to match the new output, or +add one pinned example. The tests pass, but nothing shows that the oracle can +see the defect class. Auditors find the gap. Each finding costs a fix, a new +witness, and another review round, and the next fix often breaks a +neighboring law that the oracle still cannot see. + +In one recorded case, a production-first rewrite of reload handling needed four +more review rounds and about ten follow-up commits. Each round found a defect +in the previous round's fix. A grammar-first pass then found the last defect on +its first run, and the fix that followed made the rule smaller. The result is +usually the same code, after much more work, rework, and maintainer +frustration. + +### Extra credit + +Work earns extra credit for: + +- an oracle improvement that has proof: a declared reach, a RED result on the + parent commit, a GREEN result, and a hostile mutant that fails. +- a model that stays independent of production and short, with each rule tied + to its authority. +- a production change that is clean and light in shipped bytes, especially one + that removes states, branches, or recovery paths. + +### Penalties + +Auditors penalize these shortcuts heavily, and a pull request that contains one +is not ready: + +- **Production first:** a production change in an oracle-owned area without a + RED result from the extended oracle on the code before the change. +- **Model hacking:** a model rule changed to match production output without a + stated authority (a contract document and section, or a maintainer + decision). Production output is evidence. It is not authority. +- **Unreachable claims:** a closure claim for a state that generated histories + do not reach, or a reach that nobody measured. +- **Quiet exclusions:** a skipped, narrowed, or relaxed comparison without an + entry in the [coverage map](docs/contributing/oracle-coverage.md) that names + the owner and the maintainer decision. +- **Pinned-only coverage:** a named example where the law needs a generator + dimension. +- **Unit tests inside oracles:** ordinary examples or concrete type assertions + placed in an oracle file. A fixed regression must replay the shared model, + production driver, and comparison without its own expected answer. +- **"Pre-existing" as a dismissal:** a reachable counterexample that an agent + labels as older than the change, but does not fix, record in the coverage + map with its trace, or bring to the maintainer for a decision. +- **No mutant:** a closure claim without a hostile mutant that fails the + oracle. + +### The evidence block + +Every pull request or commit that changes behavior in an oracle-owned area +includes this block: + +- **Law:** the law and its authority. +- **Reach:** the declared state and how many generated histories reached it. +- **RED:** the command and the failure on the parent commit. +- **GREEN:** the command and the result after the change. +- **Mutant:** the hostile mutant and how the oracle rejected it. +- **Limits:** the states, paths, and observations that the evidence does not + cover. + +### Lint feedback while you work + +The `oracle/` lint rules in `scripts/eslint-plugin-oracle/` report the +shortcuts that a syntactic check can see. The repository's agent hooks +(`.claude/`, `.codex/`, `.gemini/`, and `.github/hooks/`) return the reports +after each edit and at the end of each turn. A hook sees only the edits that +the harness's own edit tools make. After an edit through a shell command, or +in a harness without these hooks, run `pnpm lint:changed` before you finish. + +The end-of-turn hook and `pnpm lint:changed` also report every oracle run +budget that the branch lowered or removed, and CI prints it as a warning. A +budget is removed when its key is in no oracle file, or in a module that an +oracle file imports, any more. A lowered or removed budget can be deliberate. +Restore it, or explain the reason in the pull request. + +Fix a report. Do not turn the rule off. The hooks, `pnpm lint:changed`, the +pre-commit hook, and CI ignore `eslint-disable` comments for these rules. A +true exception needs a maintainer to change the rule in a reviewed pull +request. + +## Required reading: live-query materialization + +Before reading, analyzing, or modifying correlated live-query materialization +code under `packages/db/src/query/live/`, read +`packages/db/src/query/live/ARCHITECTURE.md` in full. Read it before changing +the related includes oracle tests as well. + +Treat that document's component boundaries and normative laws as constraints. +If a change intentionally revises an architectural contract, update the +architecture document in the same pull request. + +## Required reading: executable subsystem models + +Read `docs/contributing/glossary.md` before naming or renaming a subsystem +state, action, boundary, or observation. Production code, executable models, +tests, and design documents must use the same term for the same concept. A +model may stay structurally independent, but it must declare any abstraction +that combines, splits, or does not correspond to production concepts. + +Some oracle files are also the shortest documentation for their subsystem. +Before changing a covered contract or its machinery, use +`docs/contributing/oracle-coverage.md` to find the primary executable owner and +read its stated limits. Do not infer authority from a filename alone. + +Read these stable entry points before the narrower owner: + +- For correlated live-query materialization, read the architecture document + required above. +- For Collection mutation admission, subscription ownership, replay, + publication, or disposal, read + `packages/db/tests/collection-subscription-lifecycle-grammar-oracle.ts`. +- For optimistic snapshots and settlement, read + `packages/db/tests/optimistic-history-oracle.ts`. +- For opaque cursor pagination, read + `packages/query-db-collection/tests/cursor-pagination/model-oracle.ts`. +- For TrailBase lifecycle work, read + `packages/trailbase-db-collection/tests/ORACLE.md`. +- For cross-framework behavior, read the shared contract under + `packages/db/tests/conformance/` and the receiving framework's driver. One + framework's scheduling cut does not prove another's. + +The opening prose states the contract. The small model states the expected +behavior. The production driver and comparison check whether the implementation +refines the model for the exercised histories and observations. If a change +revises one of these contracts, update all three in the same pull request. Do not update only the assertions to match new production output. + ## Table of Contents 1. [Type Safety](#type-safety) @@ -15,6 +207,7 @@ This guide provides principles and patterns for AI agents contributing to the Ta 9. [Modern JavaScript Patterns](#modern-javascript-patterns) 10. [Edge Cases and Corner Cases](#edge-cases-and-corner-cases) 11. [Git and PR Hygiene](#git-and-pr-hygiene) +12. [Code Weight and Fail-Fast Design](#code-weight-and-fail-fast-design) ## Type Safety @@ -230,6 +423,18 @@ When merging predicates or combining queries, ensure the semantics are correct: **Key Principle:** Think carefully about what operations like intersection, union, and subset mean for your specific use case. Consider edge cases with limits, ordering, and predicates. +### Reject Multiple Copies of `@tanstack/db` + +TanStack DB does not support interoperability between multiple copies of +`@tanstack/db` in one runtime. +Each copy has its own transaction stack and IR classes. Do not add cross-copy +expression support or shape-based fallbacks to make the copies interoperate. +Keep the existing duplicate-instance check, which throws +`DuplicateDbInstanceError` for duplicate development browser loads. Do not +replace its error with a warning. SSR and test runners may evaluate the +package twice without exchanging values; do not reject those independent +loads or add per-value tracking solely to detect unsupported cross-copy use. + ## Abstraction Design ### Avoid Leaky Abstractions @@ -349,9 +554,45 @@ const dependentBuilders = [] // Accurately describes dependents ## Testing Requirements +### Required reading: oracle tests + +Before designing, changing, or reviewing an oracle or generated-history test, +read [Oracle Testing Rubric](docs/contributing/oracle-rubric.md). +Use the [coverage map](docs/contributing/oracle-coverage.md) to find an existing +owner and its limits before adding another model. The guide explains testing +methods; it does not authorize new product behavior or retire existing laws. + +### Write Oracle Tests as Literate Programs + +Write every oracle and generated-history test as a literate program. +Combine explanatory prose with executable code so the file teaches the +subsystem contract. Follow +[ORC-003](docs/contributing/oracle-rubric.md#orc-003-distinguishable-oracle-responsibilities) +and the guide's +[literate-oracle guidance](docs/contributing/oracle-rubric.md#write-the-oracle-as-executable-subsystem-documentation). + +Start with the promised law and its limits before introducing test mechanics. +Explain the central law beside the independent model. Describe the legal +history grammar, production path, public observations, and comparison checkpoint +beside the relevant code. Keep the contract, model, history grammar, production +driver, and refinement check distinguishable. Place their explanations close +enough for a reviewer to compare them directly. + +Use the project glossary's terms and clear, direct prose. Explain why each +observation follows from the model. Update the prose when the executable law +changes. Code, headings, or a separate review report alone do not satisfy this +requirement. + +Keep the prose proportional to the law. A compact oracle can use one opening +comment and short explanations beside its code. A focused regression may remain +short, but it does not waive this requirement for an oracle or replace +applicable oracle coverage. + ### Always Add Tests for Bugs -**Key Principle:** If you're fixing a bug, add a unit test that reproduces the bug before fixing it. This ensures: +**Key Principle:** Reproduce a bug in a test before fixing it, then follow the +[applicable oracle coverage requirements](#require-applicable-oracle-coverage). +The evidence must show: - The bug is actually fixed - The bug doesn't regress in the future @@ -361,13 +602,157 @@ const dependentBuilders = [] // Accurately describes dependents ```typescript // Found a bug with fetchSnapshot resolving after up-to-date message -// Should add a test: +// First isolate the failure in a focused regression: test('ignores snapshot that resolves after up-to-date message', async () => { // Reproduce the corner case // Verify it's handled correctly }) + +// Then extend the applicable primary oracle so the law is exercised across +// nearby legal histories. ``` +### Treat Every Review Bug as a Test Gap + +When a reviewer agent confirms a bug, it must also ask why the existing tests +did not catch it. The finding should name the missing test law, state +transition, generator dimension, adapter boundary, or assertion. If a test or +oracle should already have caught the bug, identify the false-green model, +classifier, fixture, or assertion that let it pass. Use that analysis to suggest +the smallest test or oracle improvement that would catch the same class of bug, +not only the reported example. + +### Close the Declared Bug Class + +A passing reproduction fixes one trace; it does not establish that the bug +class is closed. Before claiming closure, name the violated product law and +bound the claim by legal histories, production paths, and public observations +at specific checkpoints. + +Extend the primary oracle so it reaches the reported trace and nearby legal +histories that distinguish the repair from plausible wrong designs. Use a +separate oracle owner when another boundary needs a different model. Show that +the check fails on the original implementation or a hostile mutant at the +intended checkpoint, then passes with the fix. + +At closeout, state what the evidence covers. Record remaining in-scope +histories or paths in the oracle coverage map with an owner and needed witness. +A reachable in-scope counterexample keeps the class open. Passing random runs +is not a universal proof. A bug-fix pull request is not ready while a known +in-scope part of the declared bug class remains open. + +Every bug-fix pull request must show how it evaluated and closed the declared +bug class. State the violated law and its authority. Name the legal histories, +production paths, and public observation checkpoints that the oracle exercises. +Include the original or hostile-mutant RED result and the repaired GREEN result. +State any remaining limits. A passing reproduction for one trace does not +satisfy this requirement. + +### Keep Oracles Independent + +An oracle is useful only when its expected result comes from a source independent +of the implementation under test. Do not translate production branches, state +machines, classifiers, or helper functions into a second implementation and call +that an oracle. Both copies can encode the same wrong assumption. + +- Derive expected behavior from public contracts, documented prior behavior, + mathematical laws, or a separately specified reference model. +- Keep the reference model structurally different from production. Do not import + the production helper or reuse its classifications to compute expected results. +- Preserve existing contract tests unless a product or design decision explicitly + changes the contract. Rewriting a passing expectation to match new production + behavior is a design review, not routine test maintenance. +- When production work suggests an oracle change, compare the old and new + semantics with counterexamples before editing the oracle. +- Use hostile mutants to prove the oracle rejects plausible wrong designs, + including the mistake production currently makes. A green oracle without a + demonstrated kill is weak evidence. +- Use process grammar to explore lifecycle paths, and design grammar to challenge + the oracle's reference semantics. More generated traces cannot repair a wrong + reference model. + +### Require Applicable Oracle Coverage + +An oracle that checks general laws across generated states and histories is a +stronger form of coverage than a unit test for one specific example. Every +production change that adds, removes, or changes observable behavior must +extend the primary oracle when that oracle can express the governing law. Add +the missing model rule, generator dimension, state transition, or observable +assertion; adding more pinned examples alone does not generalize the oracle. Do +not submit only an isolated unit test for behavior owned by an applicable +oracle. An internal refactor does not require new oracle coverage when existing +applicable oracles already exercise the unchanged contract. + +Applicability follows the contract ownership recorded in +`docs/contributing/oracle-coverage.md`, not the limits of the owner's current +histories, production paths, or observations. A missing history, path, or +observation is a coverage gap in the primary owner. Add a separate oracle owner +only when the boundary requires a different independent judgment. Add a +receiving witness when a claim crosses an adapter, framework, provider, or other +integration boundary. + +For a bug fix, use a focused regression to isolate and shrink the failure, then +keep it as a replay example for the broader oracle. Verify that the expanded +oracle fails without the fix and passes with it. Keep valuable unit tests as +supplemental coverage, but do not treat them as a substitute for applicable +oracle coverage. If no existing oracle applies, create one when the behavior +admits a practical independent model. For a change that is not a bug fix, a +focused test alone is acceptable only when an oracle is not practical for the +behavior. The pull request must identify the relevant coverage-map entry and +explain why extending its owner, creating a new independent oracle, or adding a +receiving witness is not practical. Bug fixes cannot use this focused-test-only +exception. They must provide evidence that closes the entire declared bug class. + +### Keep Unit Tests Out of Oracles + +Oracle files contain reusable laws, independent models, input or history +grammars, shared comparisons, and their execution. Do not put ordinary unit +tests or concrete type assertions in an oracle file. A filename, coverage-map +entry, contract comment, or table of fixtures does not turn examples into an +oracle. + +Keep a fixed regression as input to the same model, production driver, and +comparison that the generated campaign uses. It must not carry a separate +expected answer or its own assertions. Checker calibration may supply a wrong +observation to that same comparison. Put supplemental unit tests in ordinary +test files; they do not discharge an oracle-owned coverage obligation. + +For compile-time contracts, generate legal TypeScript inputs and check their +public types against a reusable independent type law. A list of +`expectTypeOf` calls or `@ts-expect-error` examples is not an oracle. Negative +checks derived by the shared law from generated inputs are permitted. + +### Name Oracle Files for Discovery + +Include `oracle` in filenames that own an independent reference computation, +state model, differential or metamorphic comparison, or reusable law checker. +Modules that define the model, checker, or its history grammar also qualify. +Identify the actual mechanism before renaming a file. A coverage-map entry, +contract comment, or collection of fixed assertions is not sufficient evidence. + +An oracle file holds only oracle tests. It declares its check, model, and history +grammar with `declareOracle` from `packages/db/tests/oracle-declaration.ts`. A +type-level oracle names its model types in `typeModel`. Each +part is optional. Every test in the file must use a declared part, directly +or through a driver, or run a generated property that states its law inline. +A test that compares production with a hand-written value is a unit test: +put it in an ordinary test file. The `oracle/no-unit-test` lint rule reports +a test that does neither. + +Ordinary example tests, type assertions, fixtures, registration wrappers, and +runner utilities keep their ordinary names. A module that only drives production +or records observations is not an oracle definition. Report oracle owners and +definitions separately from supporting files; a file count is not an oracle count. +Preserve runner suffixes and update imports, commands, replay selectors, and +current documentation whenever an oracle file moves. + +### Name Tests After Behavior + +Test names should state the behavior they prove. Do not put issue or pull +request numbers in test names; those references become stale and make the test +suite harder to read. When an external report contains essential context that +the test cannot express, link it in a nearby comment instead. + ### Test Corner Cases Common corner cases to consider: @@ -643,6 +1028,74 @@ return allDone **Key Principle:** When refactoring code, update or remove comments that reference old function names or outdated logic. +## Code Weight and Fail-Fast Design + +Treat production code as a continuing cost. Bug fixes and features should aim +for a net-negative production diff. Pay for new behavior by +refactoring the existing code it touches: merge duplicated logic and parallel +structures, delete indirection, and remove branches that the change makes +redundant. Refactoring existing code is the expected way to fix a bug or add a +feature. Do not graft new code onto a design and leave the old code in place. + +Measure production code weight in shipped bytes, not lines. Run +`pnpm size:compare` (add `--files` for a per-file breakdown). It builds the +changed packages with the same minified build that the Compressed size CI +check uses, bundles each one as an app's production build would, and +compares gzip and brotli bytes with the merge-base on `origin/main`. +Development-only code, such as full error messages, does not count. +A package bundle keeps every export, so `size:compare` also reports +scenarios in `scripts/size-scenarios/`, each tree-shaken as an app ships it. +`app` is the typical app: a query collection, a filtered live query, and the +React adapter. Its weight reaches nearly every user, so weigh it most. Each +other scenario adds one feature to `app`, and its feature-cost column shows +what that feature adds. Comments, names, and formatting cost nothing in that measure, so +keep useful comments and clear names. Never delete a comment, shorten a name, +or reformat code to improve a code-weight number. Reduce states, branches, +helpers, and recovery paths. + +When a change still grows production bytes after that work, state why. Name +the refactors you tried and the evidence that ruled them out. + +Tests and contract documentation can grow to prove the behavior. Report their +weight separately from production code. + +Use the test-first and bug-class guidance above to reproduce the failure and +identify the violated contract. Strengthen or simplify existing control flow +before adding state or recovery machinery. + +### Separate Valid Edge Cases from Contract Contradictions + +A rare but valid operation is not an invariant violation. The implementation +must support it. + +An impossible internal state or a contradictory collaborator signal is an +invariant violation. Throw or reject immediately at the boundary. Check the +invariant before the code releases established state or publishes success. + +Do not convert an invariant violation into false readiness, partial success, +or a silent fallback. In library code, "crash" means a synchronous throw or a +rejected promise. It does not require process termination. + +### Recovery Must Earn Its Code Weight + +Add retries, generations, queues, fallback states, or rollback paths only when +all these conditions are true: + +- The condition can occur during valid operation. +- The public contract defines recovery behavior. +- Recovery protects user-visible behavior. +- A test or oracle proves the recovery law. + +If a condition requires a programming error or contract breach, detect it and +fail loudly. Do not build a second lifecycle to recover from it. + +Prefer a small change to the current abstraction over a replacement state +machine. A new state machine requires an explicit architectural reason and an +oracle law that the existing design cannot express. + +For reviewer-proposed defensive machinery based only on contradictory mocks, +add an invariant witness that fails before mutation instead. + ## General Principles 1. **Question Optionality**: If a property is optional, understand why. Often it should be required. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..9f8344aa03 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,189 @@ +# Contributing + +## Questions + +If you have questions about implementation details, help, or support, please use our dedicated community forum at [GitHub Discussions](https://github.com/TanStack/db/discussions). **PLEASE NOTE:** If you choose to open an issue for your question instead, your issue may be closed and redirected to the forum. + +## Reporting issues + +If you have found what you think is a bug, please [file an issue](https://github.com/TanStack/db/issues/new/choose). **PLEASE NOTE:** Issues that are identified as implementation questions or non-issues may be closed and redirected to [GitHub Discussions](https://github.com/TanStack/db/discussions). + +## Suggesting new features + +If you are here to suggest a feature, first create an issue if it does not already exist. From there, we can discuss use cases for the feature and how it could be implemented. + +## Development + +If you have been assigned to fix an issue or develop a new feature, please follow these steps to get started: + +- Fork this repository. +- Use the Node.js version mentioned in `.nvmrc`. + + ```bash + nvm use + ``` + +- Enable [Corepack](https://nodejs.org/api/corepack.html) so the [pnpm](https://pnpm.io/) version mentioned in `package.json` is used. + + ```bash + corepack enable + ``` + +- Install dependencies. + + ```bash + pnpm install + ``` + +- Build all packages. + + ```bash + pnpm build + ``` + +- Run tests. + + ```bash + pnpm test + ``` + +- Run linting. + + ```bash + pnpm lint + ``` + + `pnpm lint:changed` runs only the `oracle/` rules, on the oracle files that + your branch changes. It does not replace `pnpm lint`. If you work with a + coding agent, the repository's hooks return oracle lint reports to it as it + edits. Codex and Gemini CLI run project hooks only after you + trust them in `/hooks`; review them again when they change. In a linked Git + worktree, Codex reads project hooks from the main checkout, not from the + worktree, so the hooks run there only after the main checkout has them. + +- Implement your changes and tests in the relevant package or example. +- For a production change that adds, removes, or changes observable behavior, + extend the primary oracle when it can express the governing law. A focused + unit test can supplement the oracle but cannot replace it. Internal refactors + need no new oracle coverage when existing oracles exercise the unchanged + contract. For a change that is not a bug fix, a focused test alone is + acceptable only when an oracle is not practical. Identify the relevant + coverage-map entry and explain why you cannot extend its owner, create an + independent oracle, or add a receiving witness. See the + [oracle coverage map](docs/contributing/oracle-coverage.md) and + [Oracle Testing Rubric](docs/contributing/oracle-rubric.md). +- For a bug fix, state how the pull request evaluated and closed the declared + bug class. Include the violated law, exercised histories, production paths, + observation checkpoints, RED/GREEN evidence, and remaining limits. A passing + reproduction for one trace is not sufficient. The focused-test-only exception + does not apply to bug fixes. Resolve known in-scope gaps before submitting. +- Document your changes in the appropriate doc page. +- Git stage your required changes and commit them. +- Submit a PR for review. + +### Editing the docs locally and previewing changes + +The documentation for all TanStack projects is hosted on [tanstack.com](https://tanstack.com), which is a TanStack Start application (https://github.com/TanStack/tanstack.com). You need to run this app locally to preview your changes in the `TanStack/db` docs. + +> [!NOTE] +> The website fetches doc pages from GitHub in production, and searches for them at `../db/docs` in development. Your local clone of `TanStack/db` needs to be in the same directory as the local clone of `TanStack/tanstack.com`. + +You can follow these steps to set up the docs for local development: + +1. Make a new directory called `tanstack`. + +```sh +mkdir tanstack +``` + +2. Enter that directory and clone the [`TanStack/db`](https://github.com/TanStack/db) and [`TanStack/tanstack.com`](https://github.com/TanStack/tanstack.com) repos. + +```sh +cd tanstack +git clone git@github.com:TanStack/db.git +# We probably don't need all the branches and commit history +# from the `tanstack.com` repo, so let's just create a shallow +# clone of the latest version of the `main` branch. +# Read more about shallow clones here: +# https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/#user-content-shallow-clones +git clone git@github.com:TanStack/tanstack.com.git --depth=1 --single-branch --branch=main +``` + +> [!NOTE] +> Your `tanstack` directory should look like this: +> +> ```text +> tanstack/ +> | +> +-- db/ (<-- this directory cannot be called anything else!) +> | +> +-- tanstack.com/ +> ``` + +3. Enter the `tanstack/tanstack.com` directory, install the dependencies, and run the app in dev mode. + +```sh +cd tanstack.com +pnpm i +# The app will run on http://localhost:3000 by default +pnpm dev +``` + +4. Visit http://localhost:3000/db/latest/docs/overview in the browser and see the changes you make in `tanstack/db/docs` there. + +> [!WARNING] +> You will need to update `docs/config.json` if you add a new documentation page. + +### Running examples + +- Make sure you've installed dependencies in the repo's root directory. + + ```bash + pnpm install + ``` + +- If you want to run an example against your local changes, run the relevant package build/watch command from the repo root if needed. Otherwise, examples may run against the latest published TanStack DB release. + +- Run the example from the selected example directory. + + ```bash + pnpm dev + ``` + +#### Note on standalone execution + +If you want to run an example without installing dependencies for the whole repo, follow the instructions from the example's README.md file. It will then run against the latest TanStack DB release. + +## Changesets + +This repo uses [Changesets](https://github.com/changesets/changesets) to automate releases. If your PR should release a new package version (patch, minor, or major), please run `pnpm changeset` and commit the generated file. If your PR affects docs, examples, styles, etc., you probably don't need to generate a changeset. + +During the 1.0 release candidate period, use the same Changeset types. For a +package at `1.0.0-rc.1`, a patch produces `1.0.0-rc.1.1`, and another patch +produces `1.0.0-rc.1.2`. A minor or major change advances the candidate number: +`1.0.0-rc.1.2` becomes `1.0.0-rc.2`. Packages already on stable 1.x versions, +including `@tanstack/query-db-collection` and +`@tanstack/offline-transactions`, continue to use normal semantic versioning +and publish under npm's `latest` tag. Release candidates also publish under +`latest`, per the maintainer's 2026-10-29 decision. Existing versions are not +republished to move a tag: use `npm dist-tag add @ latest`. +This preserves any existing `rc` alias. + +## Pull requests + +Maintainers merge pull requests by squashing all commits and editing the commit message if necessary using the GitHub user interface. + +Use an appropriate commit type. Be especially careful with breaking changes. + +## Releases + +For each new commit added to `main`, a GitHub Workflow is triggered which runs the [Changesets Action](https://github.com/changesets/action). This generates a preview PR showing the impact of all changesets. When this PR is merged, the package will be published to npm. + +The first 1.0 candidate Changeset enters Changesets prerelease mode through +`pnpm run changeset:version`. Leave `.changeset/pre.json` in prerelease mode +for subsequent candidate releases; the normal release workflow handles each +patch or minor Changeset. When the candidates are ready for stable 1.0, run +`pnpm changeset pre exit` and commit the resulting `.changeset/pre.json` change. +The next Version Packages PR promotes the candidate packages to `1.0.0`. +Already stable packages keep their versions unless a pending Changeset +explicitly releases them. Merge that PR to publish 1.0. diff --git a/README.md b/README.md index 01e50bf5c1..24ce3cdf1a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,19 @@
- + + + + TanStack DB +

@@ -29,8 +43,9 @@
- -### [Become a Sponsor!](https://github.com/sponsors/tannerlinsley/) + +### [Become a Sponsor!](https://github.com/sponsors/tannerlinsley/) +
# TanStack DB diff --git a/_artifacts/skill_tree.yaml b/_artifacts/skill_tree.yaml index abbfac7dfa..62a0098ea5 100644 --- a/_artifacts/skill_tree.yaml +++ b/_artifacts/skill_tree.yaml @@ -163,6 +163,23 @@ skills: - 'TanStack/db:packages/react-db/src/useLiveInfiniteQuery.ts' - 'TanStack/db:packages/react-db/src/usePacedMutations.ts' + - name: 'Preact DB' + slug: 'preact-db' + type: 'framework' + domain: 'framework-integration' + path: 'skills/preact-db/SKILL.md' + package: 'packages/preact-db' + description: > + Preact bindings for TanStack DB. useLiveQuery and useLiveInfiniteQuery + hooks with identity derived from query IR; functional variants need + queryKey. + requires: + - 'db-core' + sources: + - 'TanStack/db:docs/framework/preact/overview.md' + - 'TanStack/db:packages/preact-db/src/useLiveQuery.ts' + - 'TanStack/db:packages/preact-db/src/useLiveInfiniteQuery.ts' + - name: 'Vue DB' slug: 'vue-db' type: 'framework' diff --git a/docs/collections/convex-collection.md b/docs/collections/convex-collection.md new file mode 100644 index 0000000000..da2740d7eb --- /dev/null +++ b/docs/collections/convex-collection.md @@ -0,0 +1,344 @@ +--- +title: Convex Collection +--- + +`@tanstack/convex-db-collection` subscribes to a Convex query and keeps its rows in a TanStack DB collection. Use DB for local filters, joins, and optimistic writes. Convex still runs your server functions and handles authentication, reconnects, and access control. + +## Install + +```sh +npm install @tanstack/convex-db-collection @tanstack/db convex +``` + +## Subscribe to a query + +Use a public Convex query that returns an array of documents. The adapter infers document and argument types from your generated function reference. + +```ts +import { createCollection } from '@tanstack/db' +import { convexCollectionOptions } from '@tanstack/convex-db-collection' +import { ConvexClient } from 'convex/browser' +import { api } from './convex/_generated/api' +import type { FunctionReturnType } from 'convex/server' + +type Task = FunctionReturnType[number] + +const client = new ConvexClient('https://your-deployment.convex.cloud') + +export const tasks = createCollection( + convexCollectionOptions({ + id: 'tasks', + client, + query: api.tasks.list, + queryArgs: { projectId }, + getKey: (task: Task) => task._id, + }), +) + +await tasks.preload() +``` + +An existing `ConvexReactClient`, including the client from `useConvex()`, also works. The adapter imports neither React nor the Convex runtime. Keep the client and collection stable across renders. + +Create fresh adapter options for each direct `createCollection` call. Reusing the same options, including a shallow copy, throws. A `collectionOptions` descriptor can still materialize independent collections in separate `DbClient` instances. + +Each result replaces the subscribed query's rows. Missing documents are deleted, and missing fields are removed. An empty array is a ready, empty collection. Keys must be unique strings or finite numbers. Duplicate keys reject the complete result and preserve the last valid snapshot. + +`convexCollectionOptions` uses eager sync. DB filters, ordering, and limits apply locally to the subscribed result. They do not change your Convex query or fetch documents outside its result. To change the query arguments, create a new collection with a distinct ID. + +## Write through Convex mutations + +Supply handlers for the operations your application supports. Await mutations on the same client used for the subscription. + +```ts +import { createCollection } from '@tanstack/db' +import { convexCollectionOptions } from '@tanstack/convex-db-collection' + +const tasks = createCollection( + convexCollectionOptions({ + id: 'tasks', + client, + query: api.tasks.list, + queryArgs: { projectId }, + getKey: (task: Task) => task._id, + onUpdate: async ({ transaction }) => { + await Promise.all( + transaction.mutations.map(({ original, changes }) => + client.mutation(api.tasks.update, { + id: original._id, + ...changes, + }), + ), + ) + }, + onDelete: async ({ transaction }) => { + await Promise.all( + transaction.mutations.map(({ original }) => + client.mutation(api.tasks.remove, { id: original._id }), + ), + ) + }, + }), +) + +const taskId: string = selectedTask._id +const transaction = tasks.update(taskId, (task) => { + task.completed = true +}) +await transaction.when('settled') +``` + +DB applies optimistic writes immediately. Successful Convex mutation promises wait for the client's queries to catch up. Before a handler settles, the adapter reads the latest cached result and waits for DB to accept its sync writes. It does not wait for visibility held by that same optimistic transaction. If a wrapping persistence layer rejects source acceptance, that sync run stays failed. Call `collection.reload()` before retrying replay. After readiness, a provider query error can recover when a later valid result arrives. A failed first load requires `reload()`. A rejected handler rolls back its optimistic writes. + +Awaiting an HTTP mutation, an action, a different client's mutation, or a detached promise does not provide this query synchronization guarantee. Avoid applying Convex optimistic updates to the same query, because the adapter would receive those local results as source data. + +A DB transaction does not make several Convex calls atomic. For atomic batches, send the complete batch through one Convex mutation. That mutation defines argument validation, authorization, and server write behavior. + +### Inserts and generated IDs + +Convex assigns `_id` on the server. DB needs a stable key before an optimistic insert. For optimistic inserts, add an application key such as `clientId` to your schema and documents. Use that key in `getKey` and send it through your insert mutation. Use `select` to omit server-only fields from the local row type. + +```ts +convexCollectionOptions({ + client, + query: api.tasks.list, + queryArgs: { projectId }, + select: (tasks) => tasks.map((task) => ({ + clientId: task.clientId, + projectId: task.projectId, + title: task.title, + })), + getKey: (task: { clientId: string; projectId: string; title: string }) => task.clientId, + onInsert: async ({ transaction }) => { + await Promise.all( + transaction.mutations.map(({ modified }) => + client.mutation(api.tasks.create, { + clientId: modified.clientId, + projectId: modified.projectId, + title: modified.title, + }), + ), + ) + }, +}) +``` + +Insert a local row with `tasks.insert({ clientId: crypto.randomUUID(), projectId, title })`. The server query must return the same `clientId`. + +Alternatively, call `client.mutation(api.tasks.create, args)` directly. The subscription adds the resulting document with its server ID. The adapter does not invent Convex IDs or silently replace temporary keys. + +## Project query results + +For an envelope or a nullable document, supply `select`. Its output is the complete set of rows for that collection. + +```ts +convexCollectionOptions({ + client, + query: api.tasks.listWithMetadata, + queryArgs: { projectId }, + select: (result) => result.tasks, + getKey: (task: Task) => task._id, +}) + +convexCollectionOptions({ + client, + query: api.tasks.get, + queryArgs: { id: taskId }, + select: (task) => (task === null ? [] : [task]), + getKey: (task: Task) => task._id, +}) +``` + +Keep projections synchronous and treat query results as immutable. A projection can change the row shape. A standard schema can validate local writes through the collection's `schema` option. + +## Complete eager pagination + +Use `convexPaginatedCollectionOptions` for a complete eager mirror of a finite query that returns Convex's `PaginationResult`. The adapter supplies `paginationOpts`, loads every page automatically, and keeps the full result and its page subscriptions in memory. `preload()` waits for the whole result. Set a server-side scope such as a project or workspace before using it with a large table. + +```ts +import { createCollection } from '@tanstack/db' +import { convexPaginatedCollectionOptions } from '@tanstack/convex-db-collection' + +const tasks = createCollection( + convexPaginatedCollectionOptions({ + id: 'project-tasks', + client, + query: api.tasks.paginated, + queryArgs: { projectId }, + pageSize: 100, + maxRows: 5_000, + getKey: (task: Task) => task._id, + }), +) + +await tasks.preload() +``` + +Your server query must pass `paginationOpts` through to the same ordered Convex database query on every call. Use Convex's `paginationOptsValidator` for argument validation. + +The adapter keeps each loaded cursor range subscribed. Fixed `endCursor` boundaries prevent gaps when rows move or are deleted. Convex split hints replace a range with two subscriptions. The adapter publishes a replacement only after every range is available and the final page reports exhaustion. `preload()` waits for this complete initial result. During later page acquisition, the last complete snapshot stays visible. + +`pageSize` defaults to 100 and must be a positive safe integer. It sets the initial request size. Reactive ranges can contain more rows after inserts. Both supported clients use their public query subscription APIs, without experimental pagination methods. + +`maxRows` defaults to 10,000 and must be a positive safe integer. It caps the accepted complete result. An oversized result rejects with `RangeError` before the adapter publishes it or continues past a known oversized prefix. Initial `preload()` rejects; later growth preserves the last accepted snapshot and reports the error through `onError` and `getSyncError()`. Raise the finite cap only for a source size your application can hold. + +The cap does not limit the bytes in a server response or guarantee peak memory use. A single reactive page can already exceed it. `pageSize` does not bound total memory or initial load time. Initial acquisition can inspect earlier cached pages repeatedly as it extends the cursor chain. + +In eager mode, `select` receives the complete row array. DB applies local filters, joins, ordering, and limits to that mirror. + +## Lazy pagination for a fixed query + +Set `syncMode: 'on-demand'` on the same paginated factory to acquire pages as an ordered DB query needs them. Declare the server query's leading numeric order with `queryOrder`. The server function must always paginate that same query in that direction. + +```ts +import type { FunctionReference, PaginationOptions, PaginationResult } from 'convex/server' + +type RankedTask = { _id: string; rank: number } +const paginatedByRank: FunctionReference< + 'query', + 'public', + { projectId: string; paginationOpts: PaginationOptions }, + PaginationResult +> = api.tasks.paginatedByRank + +const tasks = createCollection( + convexPaginatedCollectionOptions({ + client, + query: paginatedByRank, + queryArgs: { projectId }, + syncMode: 'on-demand', + queryOrder: { field: 'rank', direction: 'asc' }, + autoIndex: 'eager', + defaultIndexType: BTreeIndex, + pageSize: 100, + getKey: (task) => task._id, + }), +) + +const window = createLiveQueryCollection((q) => + q.from({ task: tasks }) + .orderBy(({ task }) => task.rank) + .orderBy(({ task }) => task._id) + .select(({ task }) => task) + .limit(25), +) +await window.preload() +await window.utils.setWindow({ offset: 25, limit: 25 }) +``` + +Import `createLiveQueryCollection` and `BTreeIndex` from `@tanstack/db`. The leading DB order must match `queryOrder`; trailing order terms run locally. Each load acquires a contiguous prefix with every row tied at its leading boundary. This may load more rows than the displayed window. Offset windows acquire the preceding prefix too. An unlimited source request, including `tasks.preload()`, loads the whole query. With no demand or a zero limit, no page is started. + +The explicit query type above states the server contract. Convex's generated API normally supplies that type, so an app can pass `api.tasks.paginatedByRank` directly. + +The initial profile supports finite numeric leading fields, aligned ordered windows, core boundary-tie requests, and forward continuations. It rejects other source filters and differently ordered requests. Put application filters in the fixed server query's arguments. `select` on the collection options is unavailable in on-demand mode; use DB query projections as shown above. This API does not translate general DB queries into Convex functions. + +Loaded ranges stay subscribed until collection cleanup. Later inserts, deletes, moves, splits, and reconnects repair the active windows. While more coverage is pending, a public live query retains its last complete window. A successful subset load waits for its accepted writes to become visible, including when optimistic writes hold them. `utils.awaitSync()` waits for source acceptance and remains safe inside mutation handlers. + +`maxRows` also caps the retained lazy prefix. It does not bound requests, network bytes, scans, or peak memory. A boundary tie or a whole-source request can still require the complete query. Use server-side scoping and a suitable finite cap for your application. + +## Durable offline writes + +Use the existing durable outbox with the Convex bridge: + +```sh +npm install @tanstack/offline-transactions +``` + +Start from a stable Collection that uses an application key such as `clientId`. Keep its ID, registry key, and named mutation function stable across reloads. + +```ts +import { IndexedDBAdapter, startOfflineExecutor } from '@tanstack/offline-transactions' +import { convexMutationCodec, convexOfflineMutation } from '@tanstack/convex-db-collection/offline' + +type Task = { clientId: string; title: string; completed: boolean } + +const executor = startOfflineExecutor({ + mutationCodec: convexMutationCodec, + collections: { tasks }, + storage: new IndexedDBAdapter('project-task-outbox'), + mutationFns: { + saveTask: convexOfflineMutation({ + client, + mutation: api.tasks.saveIdempotently, + collections: [tasks], + getArgs: ({ transaction }) => { + const task = transaction.mutations[0].modified as Task + return { clientId: task.clientId, title: task.title, completed: task.completed } + }, + }), + }, +}) + +await executor.waitForInit() + +const addTask = executor.createOfflineAction({ + mutationFnName: 'saveTask', + onMutate: (task) => { tasks.insert(task) }, +}) + +const transaction = addTask({ + clientId: crypto.randomUUID(), + title: 'Works after a reload', + completed: false, +}) + +void transaction.when('settled').catch(reportError) +``` + +Use the offline action to create durable writes. Calling `tasks.insert()` directly uses its normal handler and does not enter the durable outbox. The example's `tasks` Collection must project the same `Task` shape and use `clientId` as its key. + +The opt-in codec preserves Convex BigInt, binary data, special floats, and undefined fields in mutation changes. Keep the same codec configured across reloads. Missing or incompatible codecs stop replay and retain the stored work. Transaction metadata keeps the outbox’s existing JSON/Date/Temporal behavior. + +The outbox records the transaction before sending it. After a reload, the executor restores pending optimistic writes and replays their named mutation functions. The bridge supplies the original `idempotencyKey`, waits for the Convex mutation, then waits for each configured Collection to accept its source snapshot. It does not wait for visibility held by that same optimistic transaction. If a wrapping persistence layer rejects source acceptance, that sync run stays failed. Call `collection.reload()` before retrying replay. After readiness, a provider query error can recover when a later valid result arrives. A failed first load requires `reload()`. + +Convex handles WebSocket interruptions without rejecting the mutation promise. Argument-construction errors and rejected mutation calls are terminal failures. The bridge turns them into `NonRetriableError`, so the executor rolls back instead of retrying invalid writes indefinitely. A later source-sync failure can retry with the original key. + +### Deduplicate on the server + +Your Convex mutation must accept `idempotencyKey: v.string()`. In that same mutation transaction: + +1. Look up the key in an indexed receipts table. +2. If a receipt exists, return its saved result. +3. Apply the complete write or batch. +4. Store the key and result in the receipts table. + +Convex executes these writes atomically. A client can lose an acknowledgement after the server commits, so SDK request deduplication alone cannot replace this durable receipt. Keep receipts for at least the full replay lifetime. Scope receipt keys to the authenticated owner, and apply authorization before returning a saved result. + +The [real backend test fixture](https://github.com/TanStack/db/blob/main/packages/convex-db-collection/e2e/backend/convex/tasks.ts) shows an idempotent write mutation. It is an isolated test backend without application authorization, so add your own access checks. + +For atomic batches, `getArgs` must include every operation in the offline transaction and your server mutation must apply them together. The example handles one insert per action. Supply the platform's storage and coordination adapters when using the executor outside a browser. See the [offline transactions guide](../guides/offline-transactions.md) for leadership, storage failures, retry policy, and disposal. + +The outbox stores pending writes, not a full offline read cache. Pair the Collection with DB's persistence support when it must show previously synced rows after a reload without network access. + +## Errors and cleanup + +```ts +convexCollectionOptions({ + client, + query: api.tasks.list, + queryArgs: { projectId }, + getKey: (task: Task) => task._id, + onError: (error) => reportError(error), +}) +``` + +An initial subscription or projection error rejects `preload()` with the original error and ends that sync run. Call `collection.reload()` to try again. Later recoverable errors preserve existing rows, set `collection.health.failureReason`, and call `onError`. A valid query result clears that failure reason. `collection.utils.getSyncError()` returns the latest error, but this getter is not reactive. A rejected source acceptance stops the run and sets its health error; it also requires `reload()`. + +Collection cleanup rejects while live queries or Effects read it. Unmount those consumers first, or use `collection.cleanup({ dependents: true })`. Cleanup unsubscribes and invalidates callbacks from that sync run. It does not close the shared Convex client. After a restart, the first result replaces retained data, including when the result is empty. Close the client separately when your application no longer needs it. + +## Scope + +This adapter uses the supported `onUpdate` and `watchQuery` APIs, tested with Convex 1.47.0 and a real isolated local Convex backend. It does not access private client fields or implement a second Convex protocol. + +Convex only exposes data through your server functions. The adapter cannot bypass authorization, subscribe directly to an arbitrary table, or translate arbitrary DB predicates into Convex functions. Queries remain subject to Convex read and result limits. A paginated query result describes one page, not a complete table. Automatic reactive pagination uses `convexPaginatedCollectionOptions`. Arbitrary server predicate pushdown is not implemented. + +Convex manages connection recovery. Use the offline bridge for durable writes. A full read cache and an SSR snapshot fetch remain separate concerns. Reuse your existing Convex authentication setup. For SSR, use request-scoped `DbClient` instances and the DB hydration APIs. A module-scoped descriptor can wrap `convexCollectionOptions` with `collectionOptions`; each client receives fresh adapter state. Supply an appropriate Convex client for each request and close it when finished. + +## Run the backend tests + +```sh +pnpm --filter @tanstack/convex-db-collection... build +pnpm --filter @tanstack/convex-db-collection test:e2e +``` + +The test command starts an anonymous local Convex backend, deploys the fixture, runs both client integrations and generated process-crash histories, then stops the backend. It needs network access on the first run to download Convex's backend binary. It does not use your cloud deployment or deploy key. diff --git a/docs/collections/electric-collection.md b/docs/collections/electric-collection.md index f5a475bb9e..a7bc97cc5a 100644 --- a/docs/collections/electric-collection.md +++ b/docs/collections/electric-collection.md @@ -2,8 +2,6 @@ title: Electric Collection --- -# Electric Collection - Electric collections provide seamless integration between TanStack DB and ElectricSQL, enabling real-time data synchronization with your Postgres database through Electric's sync engine. ## Overview @@ -30,7 +28,7 @@ const todosCollection = createCollection( shapeOptions: { url: '/api/todos', }, - getKey: (item) => item.id, + getKey: (item: { id: string }) => item.id, }) ) ``` @@ -60,7 +58,7 @@ Handlers are called before mutations to persist changes to your backend: - `onUpdate`: Handler called before update operations - `onDelete`: Handler called before delete operations -Each handler should return `{ txid }` to wait for synchronization. For cases where your API can not return txids, use the `awaitMatch` utility function. +Each handler should call `await collection.utils.awaitTxId(txid)` to wait for synchronization. For cases where your API cannot return txids, use the `awaitMatch` utility function. ## Persistence Handlers & Synchronization @@ -81,22 +79,23 @@ const todosCollection = createCollection( params: { table: 'todos' }, }, - onInsert: async ({ transaction }) => { + onInsert: async ({ transaction, collection }) => { const newItem = transaction.mutations[0].modified const response = await api.todos.create(newItem) - // Return txid to wait for sync - return { txid: response.txid } + // Wait for txid to sync + await collection.utils.awaitTxId(response.txid) }, - onUpdate: async ({ transaction }) => { + onUpdate: async ({ transaction, collection }) => { const { original, changes } = transaction.mutations[0] const response = await api.todos.update({ where: { id: original.id }, data: changes }) - return { txid: response.txid } + // Wait for txid to sync + await collection.utils.awaitTxId(response.txid) } }) ) @@ -129,7 +128,7 @@ const todosCollection = createCollection( message.headers.operation === 'insert' && message.value.text === newItem.text }, - 5000 // timeout in ms (optional, defaults to 3000) + 5000 // timeout in ms (optional, defaults to 15000) ) } }) @@ -144,7 +143,7 @@ For quick prototyping or when you're confident about timing, you can use a simpl const todosCollection = createCollection( electricCollectionOptions({ id: 'todos', - getKey: (item) => item.id, + getKey: (item: { id: string }) => item.id, shapeOptions: { url: '/api/todos', params: { table: 'todos' }, @@ -305,21 +304,26 @@ The collection provides these utility methods via `collection.utils`: ### `awaitTxId(txid, timeout?)` -Manually wait for a specific transaction ID to be synchronized: +Wait for a specific transaction ID to be synchronized: ```typescript // Wait for specific txid await todosCollection.utils.awaitTxId(12345) -// With custom timeout (default is 5 seconds) +// With custom timeout (default is 15 seconds) await todosCollection.utils.awaitTxId(12345, 10000) ``` This is useful when you need to ensure a mutation has been synchronized before proceeding with other operations. +For a collection wrapped with SQLite persistence, `awaitTxId` observes the +transaction in the Electric stream. It can resolve before the local SQLite +write completes. Do not use it as a local durability acknowledgment: if the +page closes in that gap, reopening may need Electric to recover the row. + ### `awaitMatch(matchFn, timeout?)` -Manually wait for a custom match function to find a matching message: +Wait for a custom match function to find a matching message: ```typescript import { isChangeMessage } from '@tanstack/electric-db-collection' @@ -335,6 +339,119 @@ await todosCollection.utils.awaitMatch( ) ``` +### Cleanup and resume safety + +Transaction evidence and pending `awaitTxId`/`awaitMatch` calls belong to one +collection lifecycle. Explicit cleanup and automatic garbage collection reject +pending waits with `StreamAbortedError`; callbacks from the retired stream cannot +settle waits in a restarted collection. Observe these promises even when the +component or collection may be disposed before they settle. + +An on-demand Collection with managed SQLite persistence can also replace its +Electric provider session within the same Collection sync run when its cached +resume evidence becomes invalid or its cache claim expires. This replacement +rejects pending `awaitTxId` and `awaitMatch` waits with `StreamAbortedError`. +A mutation handler waiting on one of them rejects; a later handler can wait for +evidence from the replacement session. The replacement reloads demanded +subsets and does not treat acknowledgements from the retired session as current. + +Before constructing a replacement `ShapeStream`, Electric releases the +adapter-owned resources of the retired provider session, including its +subscriptions and host lifecycle listeners. The replacement starts after cache +rotation completes. An HTTP request that ignores cancellation may still finish +later; its response cannot publish rows or settle waits for the replacement +session. + +Persisted resumes wait for the cached row baseline to finish hydrating. If the +persistence wrapper cannot verify hydration completion, Electric starts a fresh +snapshot instead of using the saved offset and handle. It warns once per options +descriptor; update the persistence adapter alongside Electric to enable safe resume. +Fresh eager snapshots wait for hydration, then replace the cached rows at the +snapshot's commit boundary. Rows omitted from that snapshot do not survive in +the collection or its persisted cache, even when the fresh snapshot is empty. + +Tag membership is kept in memory, not restored from cached row headers. A cold +restart cannot use a saved cursor that needs tags or comes from an older version +that did not record whether tags were used. A changed shape, malformed saved +state, or incomplete key-set evidence also invalidates the saved cursor. +Untagged shapes with complete resume evidence can still resume from their saved +offset. + +An eager persisted resume also needs compatible source progress when its adapter +supplies that capability. Missing or incompatible progress requires a fresh +snapshot, even with an explicit offset or handle. Those options do not certify +the cached rows. +An interrupted eager recovery keeps its durable reset marker. The next start +also requires a fresh snapshot, even with an explicit offset or handle. + +Eager and progressive recovery fetch a full snapshot. Cached rows remain visible +until that replacement completes; a partial batch or subset completion cannot +publish it early. An on-demand collection with the current SQLite persistence +wrapper instead retires the persisted cache generation and clears the source +Collection's uncertified rows. A run with a valid claim can continue using the +retired generation; new runs claim an empty generation. That local clear does +not mark the Collection ready. Electric source evidence supplies readiness. It +starts a changes-only stream at the current position and +requests snapshots for active subset demands. Each demand completes after its +snapshot rows apply, including an empty snapshot. Later demands cannot rehydrate +old cache rows without a fresh source snapshot. With no active demand, recovery +does not read all cached rows or download the full shape. The durable reset marker +remains until a complete baseline can justify a global resume cursor. A cache +generation created after claim expiry can lack that marker; its incompatible +key-set evidence also requires scoped recovery on the next cold start, even +when the caller supplies an explicit Electric offset or handle. Those source +cursor options do not certify cached rows. A wrapper +without scoped recovery support continues to use full-shape recovery. An +interrupted recovery keeps the reset marker for the next start. +Until a complete baseline is certified, each later cold start repeats scoped +recovery. Uncertified cached rows from earlier starts are not shown offline. +Retired new-format storage is reclaimed by the next cache-claim collection +operation after its claims release or expire; clock passage alone does not +run SQLite work. A paused run whose claim expires must reload its demanded +subsets from the source when it resumes. Pre-upgrade on-demand storage is +isolated but needs a separate cross-version cleanup policy while older tabs +may still use it. Scoped recovery +does not certify the new generation for offline reuse. + +With `initialRender: { strategy: 'network-first' }`, an on-demand or +progressive Collection can render a demand offline from SQLite after that exact +demand settled online in an earlier session. Each demand keeps its own +coverage in the current persisted cache generation. Scoped recovery, cache-claim +expiry, and a must-refetch end it, so after any of them a demand waits for the +network again. A demand that loads during the rest of a scoped-recovery sync +run records no coverage. See [On-demand Collections offline](../guides/sqlite-persistence.md#on-demand-collections-offline). + +During scoped recovery, coordinator notifications about SQLite changes do not +hydrate, truncate, or replace public source rows. The Electric change stream +and demanded source snapshots own those rows until the sync run ends. A change +made in another tab may therefore become public when Electric delivers it, +rather than when its durable-cache notification arrives. Outside scoped +recovery, coordinator invalidation retains its normal behavior. + +An eager or progressive resume cannot apply a partial update to an unknown row. +The adapter rejects that batch, stops the stream, and records a reset so the +next sync starts from a full snapshot. Before the Collection is ready, this +fails the first load (`status: 'error'`); after it, the rows stay and the +source node shows an `error` in `collection.health` and in each live query's +`health` tree. This does not silently retry the failed stream; +`collection.reload()` refetches. + +While the Electric stream retries a failed request, the Collection reports +nothing: it stays `loading` before its first up-to-date, and keeps its health tree +unchanged afterward. When the stream gives up, it calls `fail()`: a failed first load +before readiness, or the source node's `error` after it. Complete updates from an explicit `replica: 'full'` stream remain valid. +On-demand streams can observe updates outside their loaded subsets; unknown +partial rows are ignored, while transaction acknowledgement evidence is retained. +Outside scoped recovery, complete rows published by persistence reloads or +another tab are valid baselines for subsequent partial updates. Uncertified +cached rows in scoped recovery are not. Pending deletions and resets still take +precedence over an older row that remains publicly visible. + +Reusing Electric collection options, including a spread of those options, does +not share transaction waiters or tag visibility between collections. Tag state +survives a compatible resume of the same collection and clears on a fresh +snapshot or `must-refetch`. + ### Helper Functions The package exports helper functions for use in custom match functions: @@ -358,7 +475,7 @@ const matchFn = (message) => { ### Common Issue: awaitTxId Stalls or Times Out -A frequent issue developers encounter is that `awaitTxId` (or the transaction's `isPersisted.promise`) stalls indefinitely, eventually timing out with no error messages. The data persists correctly to the database, but the optimistic mutation never resolves. +A frequent issue developers encounter is that `awaitTxId` (or the transaction's `when('settled')`) stalls indefinitely, eventually timing out with no error messages. The data persists correctly to the database, but the optimistic mutation never resolves. **Root Cause:** This happens when the transaction ID (txid) returned from your API doesn't match the actual transaction ID of the mutation in Postgres. This mismatch occurs when you query `pg_current_xact_id()` **outside** the same transaction that performs the mutation. diff --git a/docs/collections/indexed-db-collection.md b/docs/collections/indexed-db-collection.md new file mode 100644 index 0000000000..32106014ef --- /dev/null +++ b/docs/collections/indexed-db-collection.md @@ -0,0 +1,326 @@ +--- +title: IndexedDB Collection +--- + +**Reactive local data that survives a reload.** + +Build notes, drafts, reading lists, and offline workspaces with TanStack DB's live queries and optimistic mutations. IndexedDB Collections save your rows between visits and share persisted changes with other active tabs and dedicated workers. + +- **Keep editing without a network connection.** Local reads and writes use the browser's built-in database, with no backend service required. +- **Keep the UI current.** Optimistic mutations update local state immediately. Live queries filter, sort, aggregate, and join your Collections as their data changes. +- **Keep data between sessions.** A new Collection restores saved rows from the same database and object store. +- **Share changes across tabs.** Active same-origin Collections receive persisted changes through `BroadcastChannel`. +- **Keep your data typed.** Use TypeScript and optional Standard Schema validation. IndexedDB stores supported structured values, including dates and binary data, without JSON serialization. + +For example, a reading app can save articles and reading progress locally. A live query can show unread articles, while another joins articles with tags. Marking an article as read updates local query results immediately, persists the edit, and notifies other active tabs. + +## When to Choose IndexedDB + +Choose IndexedDB Collections when the browser owns the data and your app needs reactive queries over it. This works for a standalone local app or for local data alongside server-synced Collections. + +| Your data needs | Choose | +| -------------------------------------------------------------------------------- | -------------------------------------------------------- | +| Temporary UI state that lasts only for the current session | [LocalOnly Collection](./local-only-collection.md) | +| Small preferences stored through the Web Storage API | [LocalStorage Collection](./local-storage-collection.md) | +| Local records with asynchronous persistence, structured values, and live queries | IndexedDB Collection | +| Persistence around an existing server sync adapter | [SQLite Persistence](../guides/sqlite-persistence.md) | + +IndexedDB Collections load their entire object store into memory. Choose a dataset that fits your app's memory budget. Local data access works offline once the app is loaded. Caching the app for offline startup and synchronizing data across devices require separate setup. Browser storage remains subject to quota, eviction, and user deletion. + +## Installation + +IndexedDB Collections are included in the core package: + +```sh +npm install @tanstack/db +``` + +The examples use `@tanstack/db`. Framework packages such as `@tanstack/react-db` also export `createCollection` and `createTransaction`. + +## Basic Usage + +Open one database with the stores your app needs, then share that database instance between Collections. Each Collection uses one store. + +```typescript +import { + createCollection, + createTransaction, + createIndexedDB, + indexedDBCollectionOptions, +} from '@tanstack/db' + +type Todo = { + id: string + text: string + completed: boolean +} + +const db = await createIndexedDB({ + name: 'my-app', + version: 1, + stores: ['todos'], +}) + +const todos = createCollection( + indexedDBCollectionOptions({ + db, + name: 'todos', + getKey: (todo) => todo.id, + }), +) + +await todos.preload() +``` + +Create the database in a browser page or dedicated worker where IndexedDB is available. `preload()` restores the store's rows and resolves when the Collection is ready. A failed initial read rejects `preload()` and sets the Collection status to `error`. + +## Direct Mutations + +Call `insert`, `update`, and `delete` directly. Mutation handlers are optional. The Collection applies optimistic changes and persists accepted mutations to IndexedDB. + +```typescript +const id = crypto.randomUUID() + +const insert = todos.insert({ + id, + text: 'Write a draft', + completed: false, +}) +await insert.when('settled') + +const update = todos.update(id, (draft) => { + draft.completed = true +}) +await update.when('settled') + +const remove = todos.delete(id) +await remove.when('settled') +``` + +Await `when('settled')` to observe persistence success or failure. The returned transaction itself is not a Promise. + +If you provide `onInsert`, `onUpdate`, or `onDelete`, the handler runs before persistence. A rejected handler leaves durable rows unchanged and rolls back its optimistic changes. Each accepted Collection batch writes rows and metadata in one IndexedDB transaction. A failed write aborts the batch. + +## Live Queries over Persisted Data + +Use these Collections with TanStack DB's query API. This query keeps an alphabetized list of unfinished todos: + +```typescript +import { createLiveQueryCollection, eq } from '@tanstack/db' + +const unfinishedTodos = createLiveQueryCollection((q) => + q + .from({ todo: todos }) + .where(({ todo }) => eq(todo.completed, false)) + .orderBy(({ todo }) => todo.text, 'asc') + .select(({ todo }) => ({ id: todo.id, text: todo.text })), +) + +await unfinishedTodos.preload() +console.log(unfinishedTodos.toArray) +``` + +An insert adds a matching todo to the result. Marking it complete removes it. Received changes from another active tab update the same query. TanStack DB's framework bindings can render these results in your UI. + +The query runs over Collection rows in memory. IndexedDB handles persistence, while TanStack DB maintains the query result. You can also join these rows with other Collections, including Collections supplied by a server sync adapter. See [Live Queries](../guides/live-queries.md) for joins, aggregates, and framework examples. + +## Configuration + +### Database Options + +`createIndexedDB` accepts: + +| Option | Required | Description | +| ------------ | -------- | ----------------------------------------------------------------------- | +| `name` | Yes | IndexedDB database name. | +| `version` | Yes | Database version. Increase it when adding stores. | +| `stores` | Yes | Store names to create during a version upgrade. | +| `idbFactory` | No | Custom `IDBFactory`, such as a factory from `fake-indexeddb` for tests. | + +Store creation is additive. Omitting an existing store from `stores` preserves that store and its data. The `_versions` store is reserved for adapter metadata. + +### Collection Options + +`indexedDBCollectionOptions` accepts: + +| Option | Required | Description | +| ---------------------------------- | -------- | ----------------------------------------------------------------------------- | +| `db` | Yes | Instance returned by `createIndexedDB`. | +| `name` | Yes | An existing object store with out-of-line keys (`keyPath: null`). | +| `getKey` | Yes | Extracts a stable string or number key from each row. | +| `id` | No | Collection identifier. Defaults to `indexed-db-collection::`. | +| `schema` | No | A Standard Schema compatible schema for mutation and import validation. | +| `onInsert`, `onUpdate`, `onDelete` | No | Application handlers that must succeed before the adapter persists mutations. | + +Collection stores must use out-of-line keys (`keyPath: null`) because persistence passes the value returned by `getKey` as an explicit IndexedDB key. `createIndexedDB` creates stores this way. If you reuse an existing store, check its key mode first. The inline-key examples in the lower-level [`createObjectStore` reference](../reference/functions/createObjectStore.md) are not compatible with Collection persistence. + +Use values supported by IndexedDB's [structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm). This includes dates, Maps, Sets, and binary data. A row that contains a function, a symbol, a DOM node, or another value that cannot be cloned is rejected. Its write rejects with `SerializationError`, nothing in its batch is written, and its optimistic change rolls back. An automatic write's `onInsert` or `onUpdate` handler runs before persistence, so it can run even if the row is then rejected. A class instance is stored as its own data, so a restored row is a plain object without the class's methods. + +Choose a consistent key type for each Collection. + +## Schema Validation + +A schema supplies the row type and can apply defaults or transformations. This example uses Zod, which requires the `zod` package: + +```typescript +import { z } from 'zod' + +const todoSchema = z.object({ + id: z.string(), + text: z.string(), + completed: z.boolean().default(false), +}) + +const validatedTodos = createCollection( + indexedDBCollectionOptions({ + db, + name: 'todos', + schema: todoSchema, + getKey: (todo) => todo.id, + }), +) + +await validatedTodos.utils.importData([{ id: 'draft', text: 'Write a draft' }]) +``` + +This example replaces the store with one row whose `completed` value is `false`. Use the schema configuration instead of the explicit `Todo` type parameter. See [Schemas](../guides/schemas.md) for more details. + +## Manual Transactions + +For a manual transaction, call `utils.acceptMutations` from its mutation function. The utility persists only mutations owned by the receiving Collection. + +```typescript +const manual = createTransaction({ + autoCommit: false, + mutationFn: async ({ transaction }) => { + await todos.utils.acceptMutations(transaction) + }, +}) + +manual.mutate(() => { + todos.insert({ + id: crypto.randomUUID(), + text: 'Save this draft together with other edits', + completed: false, + }) +}) + +await manual.commit() +``` + +For a transaction involving several Collections, call each Collection's acceptance utility. Separate acceptance calls do not form one atomic IndexedDB transaction across Collections. + +Automatic writes in one Collection persist in mutation order, even when their +handlers finish out of order. Later writes wait for earlier handlers and +persistence to settle before reporting `when('settled')`. Rejection contributes no +durable write and allows the next mutation to proceed. Do not await a later +automatic write from an earlier handler in the same Collection: each would wait +for the other. This rule does not order separate Collections, manual acceptance, +imports, or clears; explicitly order those operations when your application +requires it. + +## Utilities + +The Collection exposes these methods through `collection.utils`: + +| Method | Behavior | +| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `exportData()` | Returns the durable rows as an array. | +| `importData(rows)` | Validates inputs and atomically replaces this store. Rejects duplicate keys. Failed validation or persistence preserves the previous rows. | +| `clearObjectStore()` | Removes this store's durable rows and publishes an empty source snapshot. Other stores remain intact. | +| `getDatabaseInfo()` | Returns the database name, version, and actual object store names. `estimatedSize`, when available, is origin-wide storage usage, including other databases and caches. | +| `acceptMutations(transaction)` | Persists the receiving Collection's mutations from a manual transaction. | + +For a Collection whose schema output is also valid schema input, export and restore a snapshot: + +```typescript +const backup = await todos.utils.exportData() +await todos.utils.importData(backup) +``` + +`exportData()` returns stored schema output; `importData()` accepts and validates +schema input. A transforming schema may require an explicit conversion before +import. For example, a string-to-Date transform exports `Date` values, which must +be converted back to input strings. Arbitrary transforms have no general inverse; +`importData()` is not an unchecked stored-output restore API. + +To delete the entire database, use the exported administrative function: + +```typescript +import { deleteDatabase } from '@tanstack/db' + +await deleteDatabase('my-app') +``` + +This operation targets the database name at its turn in the native request queue. +It is not bound to a previously opened descriptor's database lifetime. Its success +receipt does not publish Collection rows or send row notifications. Retained +Collections stay errored with their snapshots; fresh Collections restore the +resulting storage. Use `clearObjectStore()` to publish a live replacement for one +store while the connection remains open. + +`importData` copies the validated rows before it waits for storage. Later changes to caller-owned objects do not alter the imported snapshot. +Updates follow the core callback-return snapshot rules for supported values, including native buffers and views. Treat public Collection rows as immutable. + +## Cross-Tab Synchronization + +Pages and dedicated workers on the same origin use the same database and store names to access the same data. After persistence, the adapter sends a `BroadcastChannel` notification. Active receiving Collections read the changes from IndexedDB and update their public snapshots. + +If `BroadcastChannel` is unavailable, local persistence still works, but active tabs do not receive change notifications. This adapter does not synchronize data to a server or define a conflict-resolution policy for simultaneous writes to the same key from different Collections or tabs. + +## Connection Lifecycle and Blocked Operations + +The app owns the shared database connection. Collection cleanup releases that Collection's sync resources. Close the database after every Collection that uses it finishes cleanup: + +```typescript +await todos.cleanup() +db.close() +``` + +Connections created by `createIndexedDB` close automatically on native +`versionchange`. Unexpected native connection closure also notifies their managed Collections. In both cases, active Collections crash: they enter `crashed` even after they were ready, and so does every live query and Effect over them. +The descriptor's `db.close()` method has the same local effect. Their last +published rows remain available. Recreate affected Collections with a new +instance using the current database version. Calling the raw `db.db.close()` +bypasses managed Collection notification. + +Closure prevents new native transactions. Already admitted writes can commit or +abort; their callers receive that actual outcome. Committed writes finish their +Collection confirmations, including clear/import replacements. Sync transactions +accepted before closure still publish when their optimistic transactions settle. +These publications keep the Collection in `crashed`; late startup and notification +reads cannot publish or make it ready again. There is no automatic restart. + +Connections opened outside `createIndexedDB` can still block upgrades or deletion. Their owner must close them. The operation's promise remains pending until the native request succeeds or fails. + +Use `onBlocked` to report a native blocker without changing the request outcome: + +```typescript +const db = await createIndexedDB({ + name: 'my-app', + version: 2, + stores: ['todos'], + onBlocked: (event) => { + console.info('Close older connections', event.oldVersion, event.newVersion) + }, +}) + +await deleteDatabase('my-app', undefined, (event) => { + console.info('Deletion waits for open connections', event.oldVersion) +}) +``` + +The low-level `openDatabase` function accepts the same callback as its fifth argument. +Native deletion reports `newVersion: null`. The callback does not cancel the operation or settle its promise. +Dedicated workers receive the same persistence and connection notifications while running. This does not promise service-worker background delivery or recovery after worker termination. + +These operations have no deadline or cancellation option. A blocked operation does not switch the Collection to in-memory storage. + +## Learn More + +- [IndexedDB API Reference](../reference/functions/indexedDBCollectionOptions.md) +- [LocalStorage Collection](./local-storage-collection.md) +- [LocalOnly Collection](./local-only-collection.md) +- [Mutations](../guides/mutations.md) +- [Live Queries](../guides/live-queries.md) +- [SQLite Persistence](../guides/sqlite-persistence.md) diff --git a/docs/collections/local-only-collection.md b/docs/collections/local-only-collection.md index 17bf51ef4b..00005dbd7c 100644 --- a/docs/collections/local-only-collection.md +++ b/docs/collections/local-only-collection.md @@ -2,8 +2,6 @@ title: LocalOnly Collection --- -# LocalOnly Collection - LocalOnly collections are designed for in-memory client data or UI state that doesn't need to persist across browser sessions or sync across tabs. ## Overview @@ -62,11 +60,35 @@ The `localOnlyCollectionOptions` function accepts the following options: ### Optional Options - `schema`: [Standard Schema](https://standardschema.dev) compatible schema (e.g., Zod, Effect) for client-side validation -- `initialData`: Array of items to populate the collection with on creation +- `initialData`: Array of items to populate the collection with on creation. It loads when the Collection starts empty: at creation, or after cleanup. `reload()` keeps the rows, including your writes; the rows are the only copy of them. - `onInsert`: Optional handler function called before confirming inserts - `onUpdate`: Optional handler function called before confirming updates - `onDelete`: Optional handler function called before confirming deletes +## Storable Values + +A local-only Collection keeps rows in memory and never serializes them. A row +can hold values that a persisted Collection cannot store, such as functions. +The Collection returns such a value unchanged, with the same identity, after +inserts and updates. See [Mutations](../guides/mutations.md) for how drafts +copy plain objects and keep class instances by reference. + +```typescript +const handlers = createCollection( + localOnlyCollectionOptions({ + getKey: (item) => item.id, + }), +) + +handlers.insert({ id: 'save', run: () => console.log('saved') }) +handlers.get('save')?.run() // logs "saved" +``` + +Nothing survives a page reload. If you might move these rows to +[LocalStorageCollection](./local-storage-collection.md) or +[IndexedDB](./indexed-db-collection.md) later, keep rows as plain data and look +up behavior outside the row. Those Collections reject values they cannot store. + ## Initial Data Populate the collection with initial data on creation: @@ -192,10 +214,12 @@ export const modalStateCollection = createCollection( // Use in component function UserProfileModal() { - const { data: modals } = useLiveQuery((q) => - q.from({ modal: modalStateCollection }) - .where(({ modal }) => eq(modal.id, 'user-profile')) - ) + const { data: modals } = useLiveQuery({ + query: (q) => + q + .from({ modal: modalStateCollection }) + .where(({ modal }) => eq(modal.id, 'user-profile')), + }) const modalState = modals[0] @@ -248,10 +272,12 @@ export const formDraftsCollection = createCollection( // Use in component function CreatePostForm() { - const { data: drafts } = useLiveQuery((q) => - q.from({ draft: formDraftsCollection }) - .where(({ draft }) => eq(draft.id, 'new-post')) - ) + const { data: drafts } = useLiveQuery({ + query: (q) => + q + .from({ draft: formDraftsCollection }) + .where(({ draft }) => eq(draft.id, 'new-post')), + }) const currentDraft = drafts[0] @@ -315,10 +341,13 @@ LocalOnly collections are perfect for: | Survives page reload | No | Yes | | Performance | Fastest | Fast | | Size limits | Memory limits | ~5-10MB | +| Row values | Any JavaScript value | Values the configured `parser` can store (JSON by default) | | Best for | Temporary UI state | User preferences | ## Learn More +- [IndexedDB Collection](./indexed-db-collection.md) + - [Optimistic Mutations](../guides/mutations.md) - [Live Queries](../guides/live-queries.md) - [LocalStorage Collection](./local-storage-collection.md) diff --git a/docs/collections/local-storage-collection.md b/docs/collections/local-storage-collection.md index 171e5cb9b4..dd22529352 100644 --- a/docs/collections/local-storage-collection.md +++ b/docs/collections/local-storage-collection.md @@ -2,15 +2,16 @@ title: LocalStorage Collection --- -# LocalStorage Collection - -LocalStorage collections store small amounts of local-only state that persists across browser sessions and syncs across browser tabs in real-time. +LocalStorage collections store small amounts of local-only state that persists +across browser sessions and syncs between Collections in one tab and across +browser tabs. ## Overview The `localStorageCollectionOptions` allows you to create collections that: - Persist data to localStorage (or sessionStorage) - Automatically sync across browser tabs using storage events +- Synchronize active same-tab Collections sharing one storage object and key - Support optimistic updates with automatic rollback on errors - Store all data under a single localStorage key - Work with any storage API that matches the localStorage interface @@ -66,13 +67,14 @@ The `localStorageCollectionOptions` function accepts the following options: - `schema`: [Standard Schema](https://standardschema.dev) compatible schema (e.g., Zod, Effect) for client-side validation - `storage`: Custom storage implementation (defaults to `localStorage`). Can be `sessionStorage` or any object with the localStorage API - `storageEventApi`: Event API for subscribing to storage events (defaults to `window`). Enables custom cross-tab, cross-window, or cross-process synchronization +- `parser`: Object with `parse` and `stringify` methods for storage (defaults to `JSON`). Use a library such as superjson to store values that JSON cannot represent - `onInsert`: Optional handler function called when items are inserted - `onUpdate`: Optional handler function called when items are updated - `onDelete`: Optional handler function called when items are deleted -## Cross-Tab Synchronization +## Synchronization -LocalStorage collections automatically sync across browser tabs in real-time: +LocalStorage collections sync across browser tabs through storage events: ```typescript const settingsCollection = createCollection( @@ -87,6 +89,85 @@ const settingsCollection = createCollection( // This works automatically via storage events ``` +Two active Collections created with fresh options also sync in the same tab +when they use the same `storage` object and `storageKey`. A successful automatic +write, manual acceptance, or `clearStorage()` updates both public snapshots +without waiting for a browser event. A Collection's pending optimistic rows +remain visible until their own mutations settle. The same-tab mechanism does +not connect distinct custom Storage wrapper objects, even if those wrappers +access the same underlying bytes. + +The default JSON parser keeps a value authored by the writer in its original +JavaScript form until that Collection restores or another write replaces that +row. A disjoint peer write does not change the authored value. For example, a +writer can still hold a `Date`, while a peer and a freshly restored Collection +see its JSON string. With a custom parser, an authored row is read back after a +successful write so the writer also sees any value that parser normalized. + +With the default parser (the built-in `JSON` object), an insert or update must +contain only values that a restore can return. The Collection rejects a row +that contains any of these: + +- a function or symbol, or an enumerable property with a symbol key +- `NaN`, `Infinity`, or `-Infinity`, including as a `Number` object +- `undefined` or a hole as an array entry, or an array with properties other + than its elements +- a `Map`, `Set`, `WeakMap`, `WeakSet`, `RegExp`, `Promise`, `ArrayBuffer`, + `SharedArrayBuffer`, or typed array + +The rejected mutation's `when('settled')` promise rejects with +`SerializationError`. Nothing is written to storage and the optimistic row +rolls back. An automatic write's `onInsert` or `onUpdate` handler does not run; +a manual `acceptMutations` call rejects with the same error. + +Some values are accepted and normalized. A value with a `toJSON` method, such +as `Date`, is stored as that method's result, and the checks above apply to +that result. A property whose value (or `toJSON` result) is `undefined` is +stored as an absent property, and `-0` is stored as `0`. Non-enumerable +properties are not row data and are not stored. A class instance is stored as +its own enumerable properties, so a restored row is a plain object without the +class's methods. A custom `parser`, such as one that supports `Map`, defines +its own value rules; only its `stringify` errors reject a write. This includes +a parser object that wraps `JSON.stringify`. + +Automatic mutations in one Collection persist in mutation order, even if their +optional handlers finish in another order. A handler that rejects does not write +its mutation. Other tabs see accepted changes when their storage events arrive. +Without a handler or earlier pending write, a direct mutation writes to +localStorage before the mutation method returns. A later handler failure rejects +its transaction promptly, even if an earlier handler is still pending. + +A handler can start another mutation on the same Collection, then return without +awaiting that mutation's `when('settled')` promise. Do not await that promise or a +later `acceptMutations()` call inside the earlier handler. Those writes wait for +the earlier handler to return, so awaiting them would leave both pending. + +Each write stores a complete storage snapshot under one storage key. A +write preserves disjoint peer rows already present in storage, even when that +peer's event has not arrived yet. Truly simultaneous writes from separate tabs +are not an atomic transaction; localStorage has no compare-and-swap operation. + +Startup requires a valid stored snapshot. Malformed JSON or a row missing its +version information puts the Collection in an error state and leaves the stored +bytes intact. Each stored row needs an object value, a string version token, +and a storage key that decodes to the same identity as `getKey` for its data. +New writes encode string and number keys with a type prefix; restore also +accepts legacy unprefixed string keys. Two entries that decode to one identity +are malformed. Repair or remove +the invalid value, then restart the Collection. A Collection restoring rows can +receive a same-tab peer write from a subscriber during that restore; the peer's +accepted row remains visible when the Collection becomes ready. +`utils.clearStorage()` removes the stored snapshot and immediately publishes +removal of accepted rows to active same-tab Collections. A pending optimistic +mutation can remain visible and later persist. Edit the Collection through its +mutation methods; +direct same-tab edits to its storage key do not produce browser storage events. +Use an IndexedDB Collection when several tabs need stronger write coordination. + +If storage cannot be read, the mutation rejects and existing stored rows remain. +A failed storage-event read leaves current Collection rows unchanged until a +later event can read storage successfully. + ## Using SessionStorage You can use `sessionStorage` instead of `localStorage` for session-only persistence: @@ -186,7 +267,7 @@ const preferencesCollection = createCollection( ## Manual Transactions -When using LocalStorage collections with manual transactions (created via `createTransaction`), you must call `utils.acceptMutations()` to persist the changes: +When using LocalStorage collections with manual transactions (created via `createTransaction`), call `utils.acceptMutations()` in `mutationFn`. Manual acceptance joins the same write order, and the transaction's persistence receipt waits for the storage write even if the returned Promise is not awaited. Await it when later work in `mutationFn` depends on the storage write: ```typescript import { createTransaction } from '@tanstack/react-db' @@ -220,7 +301,7 @@ const tx = createTransaction({ ) // After server mutations succeed, persist local collection mutations - localData.utils.acceptMutations(transaction) + await localData.utils.acceptMutations(transaction) }, }) @@ -233,6 +314,11 @@ tx.mutate(() => { await tx.commit() ``` +Create fresh `localStorageCollectionOptions()` for each direct +`createCollection()` call. One options object contains state owned by one +Collection and throws if used to create a second. The same `DbClient` +collection descriptor can still materialize fresh options for separate clients. + ## Complete Example ```typescript @@ -263,10 +349,12 @@ export const userPreferencesCollection = createCollection( // Use in component function SettingsPanel() { - const { data: prefs } = useLiveQuery((q) => - q.from({ pref: userPreferencesCollection }) - .where(({ pref }) => eq(pref.id, 'current-user')) - ) + const { data: prefs } = useLiveQuery({ + query: (q) => + q + .from({ pref: userPreferencesCollection }) + .where(({ pref }) => eq(pref.id, 'current-user')), + }) const currentPrefs = prefs[0] @@ -307,6 +395,8 @@ LocalStorage collections are perfect for: ## Learn More +- [IndexedDB Collection](./indexed-db-collection.md) + - [Optimistic Mutations](../guides/mutations.md) - [Live Queries](../guides/live-queries.md) - [LocalOnly Collection](./local-only-collection.md) diff --git a/docs/collections/powersync-collection.md b/docs/collections/powersync-collection.md index c8ddbabbbe..472a7e6926 100644 --- a/docs/collections/powersync-collection.md +++ b/docs/collections/powersync-collection.md @@ -2,8 +2,6 @@ title: PowerSync Collection --- -# PowerSync Collection - PowerSync collections provide seamless integration between TanStack DB and [PowerSync](https://powersync.com), enabling automatic synchronization between your in-memory TanStack DB collections and PowerSync's SQLite database. This gives you offline-ready persistence, real-time sync capabilities, and powerful conflict resolution. ## Overview @@ -57,8 +55,8 @@ const db = new PowerSyncDatabase({ ### 3. (optional) Configure Sync with a Backend ```ts -import { - AbstractPowerSyncDatabase, +import type { + CommonPowerSyncDatabase, PowerSyncBackendConnector, PowerSyncCredentials, } from "@powersync/web" @@ -69,11 +67,11 @@ class Connector implements PowerSyncBackendConnector { /** Upload local changes to the app backend. * - * Use {@link AbstractPowerSyncDatabase.getCrudBatch} to get a batch of changes to upload. + * Use {@link CommonPowerSyncDatabase.getCrudBatch} to get a batch of changes to upload. * * Any thrown errors will result in a retry after the configured wait period (default: 5 seconds). */ - uploadData: (database: AbstractPowerSyncDatabase) => Promise + uploadData: (database: CommonPowerSyncDatabase) => Promise } // Configure the client to connect to a PowerSync service and your backend @@ -454,21 +452,21 @@ await documents.insert( timestamp: Date.now(), }, } -).isPersisted.promise +).when('settled') // Update with metadata await documents.update( - docId, + String(docId), { metadata: { reason: "typo-fix", editor: "user-456" } }, (doc) => { doc.name = "Report Q4 (Updated)" } -).isPersisted.promise +).when('settled') // Delete with metadata await documents.delete(docId, { metadata: { deletedBy: "user-789", reason: "duplicate" }, -}).isPersisted.promise +}).when('settled') ``` #### Accessing Metadata During Upload @@ -476,12 +474,12 @@ await documents.delete(docId, { The metadata is available in PowerSync `CrudEntry` records when processing uploads in the connector: ```typescript -import { CrudEntry } from "@powersync/web" +import type { CommonPowerSyncDatabase } from "@powersync/web" class Connector implements PowerSyncBackendConnector { // ... - async uploadData(database: AbstractPowerSyncDatabase) { + async uploadData(database: CommonPowerSyncDatabase) { const batch = await database.getCrudBatch() if (!batch) return @@ -570,7 +568,7 @@ batchTx.mutate(() => { await batchTx.commit() // Wait for the changes to be persisted -await batchTx.isPersisted.promise +await batchTx.when('settled') ``` This approach allows you to: @@ -1099,4 +1097,4 @@ const liveQuery = createLiveQueryCollection({ completed: todo.completed, })), }) -``` \ No newline at end of file +``` diff --git a/docs/collections/query-collection.md b/docs/collections/query-collection.md index 73f3511edc..9a6f73791f 100644 --- a/docs/collections/query-collection.md +++ b/docs/collections/query-collection.md @@ -2,8 +2,6 @@ title: Query Collection --- -# Query Collection - Query collections provide seamless integration between TanStack DB and TanStack Query, enabling automatic synchronization between your local database and remote data sources. ## Overview @@ -25,22 +23,26 @@ npm install @tanstack/query-db-collection @tanstack/query-core @tanstack/db ```typescript import { QueryClient } from "@tanstack/query-core" -import { createCollection } from "@tanstack/db" +import { DbClient, collectionOptions } from "@tanstack/db" import { queryCollectionOptions } from "@tanstack/query-db-collection" const queryClient = new QueryClient() +const db = new DbClient({ queryClient }) -const todosCollection = createCollection( +const todosCollection = collectionOptions("todos", (client) => queryCollectionOptions({ + id: "todos", queryKey: ["todos"], queryFn: async () => { const response = await fetch("/api/todos") return response.json() }, - queryClient, + queryClient: client.requireDependency("queryClient"), getKey: (item) => item.id, }) ) + +const todos = db.collection(todosCollection) ``` ## Configuration Options @@ -54,27 +56,225 @@ The `queryCollectionOptions` function accepts the following options: - `queryClient`: TanStack Query client instance - `getKey`: Function to extract the unique key from an item +### Request-scoped QueryClient + +`queryCollectionOptions` needs a `queryClient`. In SSR, TanStack Start, tests, +or multi-tenant apps, that client is request-local rather than module-global. +Put it on `DbClient`, then resolve it inside the collection descriptor factory: + +```typescript +import { QueryClient } from "@tanstack/query-core" +import { DbClient, collectionOptions } from "@tanstack/db" +import { queryCollectionOptions } from "@tanstack/query-db-collection" + +interface Todo { + id: string + title: string +} + +export const todoCollection = collectionOptions("todos", (client) => + queryCollectionOptions({ + id: "todos", + queryKey: ["todos"], + queryFn: async () => { + const response = await fetch("/api/todos") + return response.json() as Promise> + }, + queryClient: client.requireDependency("queryClient"), + getKey: (todo) => todo.id, + }) +) + +export function createRequestClients() { + const queryClient = new QueryClient() + const dbClient = new DbClient({ queryClient }) + return { queryClient, dbClient } +} +``` + +`dbClient.collection(todoCollection)` memoizes one collection instance for that +descriptor and client. A second `DbClient` materializes fresh adapter state and +uses its own `QueryClient`. + +Passing `queryClient` directly to `queryCollectionOptions` remains supported for +`createCollection(...)` and existing apps. When a descriptor is materialized, +an explicit `DbClient` dependency takes precedence; the configured +`queryClient` is the backwards-compatible fallback. + +### Business-Scoped Collection Factories + +A tenant, project, account, or route parameter can define a **business scope**: +the server resource that a collection represents. Include the scope in the +descriptor id, Query key, and `queryFn`. This extends the +[request-scoped QueryClient pattern](#request-scoped-queryclient) with an +explicit scope parameter: + +```typescript +interface Todo { + id: string + title: string + projectId: string +} + +async function fetchProjectTodos(projectId: string): Promise> { + const response = await fetch(`/api/projects/${projectId}/todos`) + return response.json() +} + +function createProjectTodosDescriptor( + projectId: string, +) { + return collectionOptions(`project:${projectId}:todos`, (client) => + queryCollectionOptions({ + id: `project:${projectId}:todos`, + queryKey: ["projects", projectId, "todos"], + queryFn: () => fetchProjectTodos(projectId), + queryClient: client.requireDependency("queryClient"), + getKey: (todo) => todo.id, + }) + ) +} +``` + +The scope is part of the descriptor identity. `DbClient` resolves separately +created descriptors with the same id to the same collection, so a React hook +can create the descriptor from its current parameters: + +```typescript +export function useProjectTodos(projectId: string) { + return useDbClient().collection(createProjectTodosDescriptor(projectId)) +} +``` + +Only the first descriptor for an id is materialized. Include every scope value +that changes the collection in both its descriptor id and Query key. Call +`await dbClient.cleanup()` when the client scope ends. + +A business scope is separate from a **relational subset** requested by a live query. With `syncMode: "on-demand"`, `LoadSubsetOptions` describes predicates, ordering, limits, and offsets within one business-scoped collection. These options reach `queryFn` through `ctx.meta.loadSubsetOptions` and determine the subset Query keys. See [QueryFn and Predicate Push-Down](#queryfn-and-predicate-push-down). + +Do not create a collection for each `where`, `orderBy`, or `limit`. Reuse the business-scoped collection and let on-demand loading represent those subsets. Create separate collections only for distinct server resources. + ### Query Options -- `select`: Function that lets extract array items when they're wrapped with metadata +Query Collections use TanStack Query internally and expose supported Query observer options as top-level `queryCollectionOptions` fields. + +The following top-level Query Collection options are forwarded to the underlying Query observer: + +- `select`: Function that extracts the row array TanStack DB materializes from a wrapped Query response - `enabled`: Whether the query should automatically run (default: `true`) -- `refetchInterval`: Refetch interval in milliseconds (default: 0 — set an interval to enable polling refetching) +- `refetchInterval`: Refetch interval in milliseconds - `retry`: Retry configuration for failed queries - `retryDelay`: Delay between retries - `staleTime`: How long data is considered fresh -- `meta`: Optional metadata that will be passed to the query function context +- `gcTime`: How long unused query data stays in the Query cache +- `refetchOnWindowFocus`: Whether to refetch when the window regains focus +- `refetchOnReconnect`: Whether to refetch when the network reconnects +- `refetchOnMount`: Whether to refetch when the observer mounts +- `networkMode`: Query network mode +- `initialData`: Initial Query response for eager collections +- `initialDataUpdatedAt`: Timestamp used by TanStack Query to determine initial data freshness +- `meta`: Metadata passed to the query function context. Query Collections may add `loadSubsetOptions` for on-demand queries. + +```ts +const todosCollection = createCollection( + queryCollectionOptions({ + queryKey: ["todos"], + queryFn: fetchTodos, + queryClient, + getKey: (todo) => todo.id, + refetchOnWindowFocus: true, + refetchOnReconnect: true, + refetchOnMount: "always", + networkMode: "online", + }) +) +``` + +Top-level `meta` is always merged by Query Collection so it can add on-demand `loadSubsetOptions`. Other supported top-level Query options are only passed to TanStack Query when you define them. If you omit them, `QueryClient.defaultOptions` can still apply. + +Some fields are owned or reinterpreted by the collection adapter rather than treated as ordinary Query option pass-through: + +- `queryKey`: Identifies the Query cache entry and, in on-demand mode, may be built from load-subset options. +- `queryFn`: Fetches the complete collection state or the requested on-demand subset. +- `select`: Extracts array rows from wrapped responses before they are stored in the collection. This is not the same contract as TanStack Query's `select` option. +- `queryClient`: Supplies the Query client instance used by the collection. +- `syncMode`: Controls whether the collection syncs eagerly or on demand. +- `getKey`: Extracts each row's stable TanStack DB key. +- Mutation handlers such as `onInsert`, `onUpdate`, and `onDelete`. + +Some TanStack Query fields are owned or reinterpreted by Query Collection and are intentionally not exposed as ordinary Query observer options: + +- `queryKey`, `queryFn`, and `queryClient` +- `select` (Query Collection uses this for row extraction, not TanStack Query's observer-level `select` contract) +- `meta` (merged by Query Collection so on-demand `loadSubsetOptions` can be included) +- `subscribed` (Query Collection owns the observer subscription lifecycle) +- `structuralSharing` and `notifyOnChangeProps` (managed by Query Collection synchronization) + +`placeholderData` is intentionally unsupported. TanStack Query treats placeholder data as observer-local presentation state rather than cached Query data. Materializing it would expose temporary UI data as collection-wide normalized rows. Render placeholders in the consuming UI instead. + +### Request Cancellation with `QueryFunctionContext.signal` + +TanStack Query passes an `AbortSignal` to `queryFn` through the query function +context. Forward `ctx.signal` to `fetch` or another abortable client to make the +request cancellable: + +```typescript +const todosCollection = createCollection( + queryCollectionOptions({ + queryKey: ["todos"], + queryFn: async (ctx) => { + const response = await fetch("/api/todos", { + signal: ctx.signal, + }) + + if (!response.ok) { + throw new Error("Failed to fetch todos") + } + + return response.json() as Promise> + }, + queryClient, + getKey: (todo) => todo.id, + }), +) +``` + +Explicit collection cleanup cancels each exact Query key the collection is +currently tracking before removing it from the Query cache: + +```typescript +await todosCollection.cleanup() +``` + +The underlying request is aborted only when its client consumes `ctx.signal`. +A client that ignores the signal may continue its request even though the +collection has been cleaned up. + +An unloaded on-demand subset is no longer tracked. A later explicit collection +cleanup does not revisit its Query key. + +Query cache entries are shared within a `QueryClient`. Explicit cleanup can +affect other consumers using the same exact Query keys. + +On-demand subset unloading does not explicitly call +`queryClient.cancelQueries()`. It removes the subset's Query observer. If this +was the final observer and the query function consumed `ctx.signal`, TanStack +Query aborts the request. If the signal was ignored, or another observer still +uses the same exact Query key, the request may finish and remain cached until +`gcTime`. ### Using with `queryOptions(...)` -If your app already uses TanStack Query's `queryOptions` helper (e.g. from `@tanstack/react-query`), you can spread those options into `queryCollectionOptions`. Note that `queryFn` must be explicitly provided since query collections require it both in types and at runtime: +If your app already uses TanStack Query's `queryOptions` helper (e.g. from `@tanstack/react-query`), you can spread compatible top-level options into `queryCollectionOptions`. Note that `queryFn` must be explicitly provided since query collections require it both in types and at runtime, and Query Collection's `select` option is for row extraction rather than TanStack Query observer-level selection: ```typescript import { QueryClient } from "@tanstack/query-core" -import { createCollection } from "@tanstack/db" +import { DbClient, collectionOptions } from "@tanstack/db" import { queryCollectionOptions } from "@tanstack/query-db-collection" import { queryOptions } from "@tanstack/react-query" const queryClient = new QueryClient() +const db = new DbClient({ queryClient }) const listOptions = queryOptions({ queryKey: ["todos"], @@ -84,18 +284,122 @@ const listOptions = queryOptions({ }, }) -const todosCollection = createCollection( +const todosCollection = collectionOptions("todos", (client) => queryCollectionOptions({ + id: "todos", ...listOptions, queryFn: (context) => listOptions.queryFn!(context), - queryClient, + queryClient: client.requireDependency("queryClient"), getKey: (item) => item.id, }), ) + +const todos = db.collection(todosCollection) ``` If `queryFn` is missing at runtime, `queryCollectionOptions` throws `QueryFnRequiredError`. +### Initial Data + +Eager Query Collections support TanStack Query's `initialData` and +`initialDataUpdatedAt` options. Initial data has the original Query response +shape, is stored in the Query cache, and is immediately materialized as +normalized collection rows. TanStack Query uses `initialDataUpdatedAt` together +with `staleTime` to decide whether to fetch. + +```typescript +const serverRenderedAt = Date.now() +const initialTodos = [ + { id: "1", title: "Write documentation" }, + { id: "2", title: "Ship initial data support" }, +] + +const todosCollection = createCollection( + queryCollectionOptions({ + queryKey: ["todos"], + queryFn: fetchTodos, + queryClient, + getKey: (todo) => todo.id, + initialData: initialTodos, + initialDataUpdatedAt: serverRenderedAt, + staleTime: 60_000, + }), +) +``` + +An existing cached or hydrated Query response takes precedence over +`initialData`. Query keys remain the cache identity: two collections using the +same QueryClient and exact Query key observe one shared Query document, and a +later collection's initializer does not replace it. Use distinct Query keys for +independent documents. + +Initial data is supported only for eager collections. A collection-wide value +cannot establish row membership for arbitrary on-demand predicates, ordering, +limits, and offsets. For `syncMode: "on-demand"`, seed or hydrate the exact +derived Query cache entries instead. + +If a stale initial response triggers a fetch, the initial rows remain available +while it is in flight. A successful response reconciles them through the normal +row ownership pipeline; an error retains the initial rows. Direct writes patch +the eager Query cache in place when its row array can be updated. Collections +sharing that Query project the same patched cache. On-demand direct writes patch existing cached +row keys on a best-effort basis, as described below. + +### Selecting Rows from Wrapped Responses + +Many APIs return rows inside a response envelope that also contains metadata such as pagination cursors, totals, or request information. Use `select` to extract the row array that TanStack DB should materialize: + +```typescript +interface TodosResponse { + items: Array<{ id: string; title: string }> + nextCursor?: string + total: number +} + +const todosCollection = createCollection( + queryCollectionOptions({ + queryKey: ["todos"], + queryFn: async (): Promise => { + const response = await fetch("/api/todos") + return response.json() + }, + initialData: { + items: [{ id: "1", title: "Initial todo" }], + nextCursor: undefined, + total: 1, + }, + select: (response) => response.items, + queryClient, + getKey: (item) => item.id, + }), +) +``` + +`select` is a query-db-collection row extraction hook. It tells TanStack DB which rows to materialize while the TanStack Query cache keeps the original query response shape. In the example above, `queryClient.getQueryData(["todos"])` still returns the full `TodosResponse`, including `nextCursor` and `total`. + +The same projection applies to `initialData`: provide the complete response +envelope, and Query Collection materializes the rows returned by `select` while +preserving the envelope in the Query cache. + +This differs from TanStack Query's observer-level `select`: query-db-collection uses this option to bridge Query's response object into DB's normalized row store. + +In eager mode, direct write utilities such as `writeInsert`, `writeUpdate`, and `writeDelete` make a best-effort attempt to update the matching row array inside wrapped Query cache entries while preserving wrapper metadata. Collections using that exact Query key project the patched cache, including a newly inserted row. In on-demand mode, direct writes patch rows already present in active raw arrays or arrays exposed as a direct response property by `select`. They remove inactive entries under the Collection's query-key prefix unless another Collection or observer retains them. They do not reconstruct a scoped result from the full collection. A retained outside observer can therefore keep an inactive cache entry with old rows. + +In both modes, a direct cache patch leaves Query's error, invalidation, and +freshness state intact. A direct write is not a successful fetch: an eager +query that failed still reports that error, and a stale eager query may fetch +on remount. Use `utils.refetch({ throwOnError: true })` to retry after an +error, or `utils.refetch()` when you need fresh server results. + +This works automatically when `select` returns the wrapper's array property itself, as in these simple wrappers: + +- `{ data: [...] }` +- `{ items: [...] }` +- `{ results: [...] }` + +Derived projections, such as `select: (response) => response.edges.map((edge) => edge.node)`, are read-side row extraction only. query-db-collection cannot generally reconstruct the original response envelope from updated rows, so direct writes leave that cached envelope unchanged. The Collection still receives the direct write, and resubscribing does not replay the unchanged envelope over it. Refetch or invalidate the query if the wrapped cache must reflect that write. +This also applies to derived arrays such as `select: (response) => response.data.filter(Boolean)`. + ### Collection Options - `id`: Unique identifier for the collection @@ -113,6 +417,27 @@ If `queryFn` is missing at runtime, `queryCollectionOptions` throws `QueryFnRequ The `meta` option allows you to pass additional metadata to your query function. By default, Query Collections automatically include `loadSubsetOptions` in the meta object, which contains filtering, sorting, and pagination options for on-demand queries. +`loadSubsetOptions` is request-scoped. Query functions can read it through +`ctx.meta`, but it is excluded when Query metadata is serialized for SSR. +Hydrated Query metadata therefore does not contain the server request options; +new on-demand requests supply their own options. Other enumerable `meta` +properties retain TanStack Query's normal dehydration behavior. + +Treat `ctx.meta.loadSubsetOptions` and its nested request data as read-only. +Do not edit expression nodes, ordering options, Dates, byte arrays, or membership +arrays. Build separate API parameters instead. Core retains request data without +cloning it; changing submitted data can make the request disagree with its cache +key. To change a query constant, supply a new value rather than mutating the old +one. Cancellation through the request's `AbortSignal` remains supported. +Use `ctx.signal` for the provider call. A sole acquisition's first fetch keeps +its supplied `loadSubsetOptions.signal`. Once acquisitions or Collections share +a Query, later fetches expose the Query fetch signal there because the +acquisition that installed the query function can release first. If a peer +joins during a fetch and that original signal aborts, a provider rejection with +its abort reason or an `AbortError` gets one retry with the live Query signal +while the peer still owns the Query. An unrelated `AbortError` raised after the +same abort has no distinguishable cause and can also get that one retry. + ### Type-Safe Meta Access The `ctx.meta.loadSubsetOptions` property is automatically typed as `LoadSubsetOptions` without requiring any additional imports or type assertions: @@ -231,34 +556,30 @@ const productsCollection = createCollection( ## Persistence Handlers -You can define handlers that are called when mutations occur. These handlers can persist changes to your backend and control whether the query should refetch after the operation: +Handlers persist changes to your backend and explicitly synchronize the +confirmed state before returning. They return `Promise`: ```typescript const todosCollection = createCollection( - queryCollectionOptions({ + queryCollectionOptions<{ id: string; text: string; completed: boolean }>({ queryKey: ["todos"], queryFn: fetchTodos, queryClient, getKey: (item) => item.id, - - onInsert: async ({ transaction }) => { - const newItems = transaction.mutations.map((m) => m.modified) - await api.createTodos(newItems) - // Returning nothing or { refetch: true } will trigger a refetch - // Return { refetch: false } to skip automatic refetch + onInsert: async ({ transaction, collection }) => { + await api.createTodos(transaction.mutations.map((m) => m.modified)) + await collection.utils.refetch() }, - - onUpdate: async ({ transaction }) => { - const updates = transaction.mutations.map((m) => ({ + onUpdate: async ({ transaction, collection }) => { + await api.updateTodos(transaction.mutations.map((m) => ({ id: m.key, changes: m.changes, - })) - await api.updateTodos(updates) + }))) + await collection.utils.refetch() }, - - onDelete: async ({ transaction }) => { - const ids = transaction.mutations.map((m) => m.key) - await api.deleteTodos(ids) + onDelete: async ({ transaction, collection }) => { + await api.deleteTodos(transaction.mutations.map((m) => m.key)) + await collection.utils.refetch() }, }) ) @@ -266,44 +587,98 @@ const todosCollection = createCollection( ### Controlling Refetch Behavior -By default, after any persistence handler (`onInsert`, `onUpdate`, or `onDelete`) completes successfully, the query will automatically refetch to ensure the local state matches the server state. - -You can control this behavior by returning an object with a `refetch` property: +Mutation handlers do not automatically refetch. Call and await +`collection.utils.refetch()` to read back server state, or await the direct-write +utilities to apply the confirmed server response without a request. If a handler +does neither, its optimistic changes revert to the previous synced rows when the +transaction settles. A successful server write alone does not update those rows. + +The `{ refetch: false }` return value has been removed. Handlers return +`Promise` and do not control synchronization through their return value. +Query's configured polling, focus, reconnect, and invalidation triggers still +operate independently of mutation handlers. + +#### Explicit Refetch Inside a Handler + +When no user mutation is persisting and no mutation handler is active, the +`refetch()` promise resolves after every accepted result from that call has been +applied to Collection rows. This is also the application barrier when the +result has no row diff and therefore emits no change event. Rejection is +fail-fast: with several tracked Queries, one failure may reject the call while +independent work remains pending. + +While a user mutation is persisting or a Collection mutation handler is active, +normal application is queued behind that transaction. To avoid a circular wait +or an unrelated persistence delay, all overlapping `refetch()` calls resolve at +the Query fetch boundary. This rule applies whether the mutation or the refetch +starts first. It includes calls through the handler parameter, captured or +external Collection references, manual transaction mutation functions, and +`createOptimisticAction` mutation functions. Code can await the mutation's +`when('settled')` when it needs the complete transaction and its queued +Collection application. Utilities that refetch inherit the same phase +boundary. + +The handler's `collection` parameter and every matching +`transaction.mutations[n].collection` alias preserve the external Collection +identity. Strict equality, `Map`, and `WeakMap` lookups therefore continue to +work in shared persistence handlers. Phase tracking changes the refetch wait +boundary without replacing the Collection, transaction, or mutation objects. + +At the application boundary, `throwOnError` applies to both Query fetch errors +and errors applying an accepted result to Collection rows. With +`throwOnError: true`, application failure or cancellation rejects the call; +cancellation uses an `AbortError`. With `throwOnError: false` (the default), +`refetch()` returns its Query results instead. At the mutation-phase fetch +boundary, `throwOnError` applies only to the Query fetch because application +happens after that call settles. A later application failure is recorded by the +Collection's error utilities. + +#### Applying a Server Response Without Refetching + +Use direct-write utilities when the server returns the confirmed rows or another +source delivers them. Await the write before the handler returns: ```typescript -onInsert: async ({ transaction }) => { - await api.createTodos(transaction.mutations.map((m) => m.modified)) - - // Skip the automatic refetch - return { refetch: false } +onInsert: async ({ transaction, collection }) => { + const rows = await api.createTodos(transaction.mutations.map((m) => m.modified)) + await collection.utils.writeUpsert(rows) } ``` -This is useful when: - -- You're confident the server state matches what you sent -- You want to avoid unnecessary network requests -- You're handling state updates through other mechanisms (like WebSockets) - ## Utility Methods The collection provides these utility methods via `collection.utils`: -- `refetch(opts?)`: Manually trigger a refetch of the query - - `opts.throwOnError`: Whether to throw an error if the refetch fails (default: `false`) +- `refetch(opts?)`: Refetch every tracked Query and await the applicable fetch or application boundary + - `opts.throwOnError`: Whether Query fetch or Collection application errors reject an application-boundary call (default: `false`); mutation-phase calls cover fetch errors only - Bypasses `enabled: false` to support imperative/manual refetching patterns (similar to hook `refetch()` behavior) - - Returns `QueryObserverResult` for inspecting the result + - Returns the tracked Queries' `QueryObserverResult` values in tracked-key order + - Preserves an `undefined` slot if a tracked Query is removed while the refetch starts + +Failures appear in the health tree. Before the first successful result, a +failed fetch fails the first load (`status: 'error'`). After the Collection is +ready, a failed fetch or result application for any Query it observes sets the +node's `failureReason`, because TanStack Query fetches again, and the next +successful application clears it. Read it from `collection.health` or a live +query's `health` tree. To retry a failed first load, call +`collection.reload()`. ## Direct Writes -Direct writes are intended for scenarios where the normal query/mutation flow doesn't fit your needs. They allow you to write directly to the synced data store, bypassing the optimistic update system and query refetch mechanism. +Direct writes are intended for scenarios where the normal query/mutation flow doesn't fit your needs. They write directly to the synced data store and bypass the optimistic update system. Their Query cache behavior depends on the collection's sync mode. ### Understanding the Data Stores -Query Collections maintain two data stores: +Query Collections combine three layers: -1. **Synced Data Store** - The authoritative state synchronized with the server via `queryFn` -2. **Optimistic Mutations Store** - Temporary changes that are applied optimistically before server confirmation +1. **TanStack Query cache** - The response document for each exact Query key. A successful `queryFn` fetch updates this cache, and Collections that acquire the Query project its rows. +2. **Synced Collection rows** - Rows projected from acquired Query responses, together with accepted direct writes. A direct write can remain visible here when its response envelope cannot be patched in the Query cache. +3. **Optimistic mutations store** - Temporary changes applied before a mutation handler settles. + +The Query cache is the source of truth for Query responses. A Collection also +tracks which subsets own its fetched rows and can hold direct writes that have +not been confirmed by a later fetch. See [Direct Writes and Query Sync](#direct-writes-and-query-sync) +for the rules that order these sources. Normal collection operations (insert, update, delete) create optimistic mutations that are: @@ -312,7 +687,7 @@ Normal collection operations (insert, update, delete) create optimistic mutation - Rolled back automatically if the server request fails - Replaced with server data when the query refetches -Direct writes bypass this system entirely and write directly to the synced data store, making them ideal for handling real-time updates from alternative sources. +Direct writes bypass this system entirely and write directly to the synced data store, making them useful for handling real-time updates from alternative sources. They do not automatically refetch Query results. The caller is responsible for deciding when a scoped result needs fresh server authority. ### When to Use Direct Writes @@ -351,14 +726,21 @@ These operations: - Write directly to the synced data store - Do NOT create optimistic mutations -- Do NOT trigger automatic query refetches -- Update the TanStack Query cache immediately -- Are immediately visible in the UI +- Return a promise that resolves when the sync commit is accepted, including any configured persistence write. Await it in a mutation handler before returning so the server response is stored before the optimistic state drops. +- While a mutation handler is running, wait behind it and become visible when its optimistic transaction settles, in the same update that drops its optimistic state +- In eager mode, update representable full-result TanStack Query cache rows in place without refetching; Collections sharing the exact Query project those edits +- In on-demand mode, patch changed keys already present in active raw or directly selected Query cache arrays when no other Collection owns that Query. Remove inactive matching-prefix entries when no other Collection or observer retains them, without refetching + +The promise does not wait for a separately requested Query refetch. If a handler returns before it writes the server response, its optimistic state drops and the row shows the previous server value until the response arrives. +An explicit `utils.refetch()` after a direct write may cancel an older request for the same Query key. Collection refetch callers follow the replacement, but a caller using `queryClient.fetchQuery()` on that key can receive Query Core's `CancelledError`. Avoid sharing exact Query keys with callers that require their original request to settle normally. +After Collection cleanup starts, direct writes fail with `SyncNotInitializedError` until a new sync run starts. ### Batch Operations The `writeBatch` method allows you to perform multiple operations atomically. Any write operations called within the callback will be collected and executed as a single transaction: +The batch includes writes to this Collection only. A synchronous callback may also write to another Collection, but that write has its own completion promise. + ```typescript todosCollection.utils.writeBatch(() => { todosCollection.utils.writeInsert({ id: "1", text: "Buy milk" }) @@ -368,10 +750,15 @@ todosCollection.utils.writeBatch(() => { }) ``` +Await `writeBatch(...)` to wait for the whole sync commit. The callback must be synchronous; the promises returned by writes inside it settle with the batch. + ### Real-World Example: WebSocket Integration +This example assumes the collection stays loaded for the app's lifetime, so the +listener can live at module scope. + ```typescript -// Handle real-time updates from WebSocket without triggering full refetches +// Apply real-time changes from a WebSocket as direct writes ws.on("todos:update", (changes) => { todosCollection.utils.writeBatch(() => { changes.forEach((change) => { @@ -391,38 +778,70 @@ ws.on("todos:update", (changes) => { }) ``` +- Direct writes do not refetch. In eager mode, they patch a representable full-result + cache, which other Collections sharing the Query project. In on-demand mode, they patch rows already present in active scoped + caches. See [Direct Writes and Query Sync](#direct-writes-and-query-sync). +- A direct write is validated against the synced store after every earlier + sync commit is accepted. `writeUpdate` and `writeDelete` reject their + promise if the row is not in the synced store, and `writeInsert` rejects if + the row is already there. They do not throw. An event can arrive before the + first load finishes, or for a row the collection has not loaded. Handle the + rejection, or use `writeUpsert` when events carry the full row. +- In a persisted collection, a direct write can wait for an earlier sync + commit's durable write, so its rows can appear after the call returns. Await + the returned promise before you read them. + +A direct write returns a promise. Await it to know that the write applied, and +catch its rejection to handle a validation error: + +```typescript +try { + await todosCollection.utils.writeUpdate({ id: change.id, done: true }) +} catch (error) { + if (error instanceof UpdateOperationItemNotFoundError) { + // The row is not in the synced store yet. Refetch, or skip this event. + await todosCollection.utils.refetch() + } else { + throw error + } +} +``` + +Import `UpdateOperationItemNotFoundError`, `DeleteOperationItemNotFoundError` +and `DuplicateKeyInBatchError` from `@tanstack/query-db-collection`, and +`DuplicateKeySyncError` from `@tanstack/db`. + ### Example: Incremental Updates -When the server returns computed fields (like server-generated IDs or timestamps), you can use the `onInsert` handler with `{ refetch: false }` to avoid unnecessary refetches while still syncing the server response: +When the server returns computed fields (like server-generated IDs or timestamps), you can use direct writes to sync the server response without triggering a full refetch: ```typescript const todosCollection = createCollection( - queryCollectionOptions({ + queryCollectionOptions<{ id: string; text: string; completed: boolean }>({ queryKey: ["todos"], queryFn: fetchTodos, queryClient, getKey: (item) => item.id, - onInsert: async ({ transaction }) => { + onInsert: async ({ transaction, collection }) => { const newItems = transaction.mutations.map((m) => m.modified) // Send to server and get back items with server-computed fields const serverItems = await api.createTodos(newItems) // Sync server-computed fields (like server-generated IDs, timestamps, etc.) - // to the collection's synced data store - todosCollection.utils.writeBatch(() => { + // to the collection's synced data store using direct writes + await collection.utils.writeBatch(() => { serverItems.forEach((serverItem) => { - todosCollection.utils.writeInsert(serverItem) + collection.utils.writeInsert(serverItem) }) }) - // Skip automatic refetch since we've already synced the server response + // No need to refetch - we've already synced the server response via direct writes // (optimistic state is automatically replaced when handler completes) - return { refetch: false } }, - onUpdate: async ({ transaction }) => { + onUpdate: async ({ transaction, collection }) => { const updates = transaction.mutations.map((m) => ({ id: m.key, changes: m.changes, @@ -430,37 +849,252 @@ const todosCollection = createCollection( const serverItems = await api.updateTodos(updates) // Sync server-computed fields from the update response - todosCollection.utils.writeBatch(() => { + await collection.utils.writeBatch(() => { serverItems.forEach((serverItem) => { - todosCollection.utils.writeUpdate(serverItem) + collection.utils.writeUpdate(serverItem) }) }) - return { refetch: false } + // No refetch needed since we used direct writes }, }) ) // Usage is just like a regular collection -todosCollection.insert({ text: "Buy milk", completed: false }) +todosCollection.insert({ id: crypto.randomUUID(), text: "Buy milk", completed: false }) ``` -### Example: Large Dataset Pagination +### Server pagination with live queries + +`useLiveInfiniteQuery` in React, Vue, and Svelte grows a local ordered query +window. It does not run TanStack Query's `InfiniteQueryObserver`. Query +Collections use `QueryObserver`, so `queryFn` receives +`meta.loadSubsetOptions`, not `pageParam`. + +`getNextPageParam` is not supported; passing it at runtime throws an error. +`initialPageParam` labels result pages only. It does not set a remote offset +or server cursor. + +For server loading, use `syncMode: 'on-demand'` and make `queryFn` fulfill the +requested filter, order, offset, and limit. Use a deterministic total order +(for example, a timestamp followed by a unique ID). The loader may request a +prefix, a suffix, a tie group, or the full filtered source. A request is not +necessarily one UI page: the hook fetches an extra row to determine +`hasNextPage`. Returning one capped endpoint page can incorrectly make the +query appear exhausted even when the server has more rows. + +#### Endpoints with fixed-size pages + +If your endpoint uses page numbers, drain enough server pages to fulfill each +request. This example assumes a zero-based page API with a fixed size of 50. +The endpoint must apply the supplied filters and sorts **before** pagination, +keep a consistent ordered result while its pages are read, and return +`nextPage: null` only when it has authoritatively exhausted that result. +This example uses offset-based pagination. `api.listPosts` translates the full +`where` expression and `orderBy` options into the endpoint's syntax, and rejects +unsupported expressions. The separate `cursor` hints are deliberately unused; +cursor-based adapters must handle those hints alongside `where`, not treat them +as already included in it. See [QueryFn and Predicate Push-Down](#queryfn-and-predicate-push-down) +for translation helpers. Do not drop predicates or filter after paginating: +either changes the requested window. ```typescript -// Load additional pages without refetching existing data -const loadMoreTodos = async (page) => { - const newTodos = await api.getTodos({ page, limit: 50 }) +import { createCollection } from '@tanstack/db' +import { queryCollectionOptions } from '@tanstack/query-db-collection' - // Add new items without affecting existing ones - todosCollection.utils.writeBatch(() => { - newTodos.forEach((todo) => { - todosCollection.utils.writeInsert(todo) - }) - }) -} +type Post = { id: number; createdAt: number; title: string } +const serverPageSize = 50 + +const postsCollection = createCollection( + queryCollectionOptions({ + queryKey: ['posts'], + queryClient, + syncMode: 'on-demand', + getKey: (post: Post) => post.id, + queryFn: async (ctx): Promise> => { + const { where, orderBy, offset = 0, limit } = ctx.meta?.loadSubsetOptions ?? {} + const skip = offset % serverPageSize + let page: number | null = Math.floor(offset / serverPageSize) + const gathered: Array = [] + + while (page !== null && (limit === undefined || gathered.length < skip + limit)) { + ctx.signal.throwIfAborted() + const response: { rows: Array; nextPage: number | null } = + await api.listPosts({ + page, + pageSize: serverPageSize, + where, + orderBy, + signal: ctx.signal, + }) + gathered.push(...response.rows) + page = response.nextPage + } + + return gathered.slice(skip, limit === undefined ? undefined : skip + limit) + }, + }), +) + +// React example; the collection protocol is the same for Vue and Svelte. +const { data, fetchNextPage, hasNextPage } = useLiveInfiniteQuery( + (q) => q.from({ post: postsCollection }) + .orderBy(({ post }) => post.createdAt) + .orderBy(({ post }) => post.id), + { pageSize: 20 }, +) ``` +Reject failed requests instead of returning partial rows as success. An +unlimited request must drain until the endpoint reports exhaustion. For an +endpoint with opaque cursors, use `createCursorPager` as shown below. The hook +does not maintain remote cursor history on its own. + +Manually appending rows with `writeUpsert` is a separate, lower-level loading +strategy. It does not make an eager `queryFn` incremental: a later successful +refetch still replaces its complete state and can remove appended rows. +`staleTime: Infinity` does not prevent explicit refetch or invalidation. + +#### Endpoints with opaque cursors + +Query Collections ask for arbitrary windows, such as rows 40–59. An +opaque-cursor API cannot jump to row 40. It can only fetch the first page, then +follow the continuation token returned with each page. + +`createCursorPager` bridges those two protocols. It stores complete backend +pages in your existing QueryClient and follows their cursors until it has enough +rows for the requested `offset` and `limit`. A later request can reuse that +fresh run of pages and fetch only the missing suffix. The hook still requests +an extra row to decide `hasNextPage`; the pager just fulfills the resulting +window. + +```typescript +import { createCollection } from '@tanstack/db' +import { createCursorPager, queryCollectionOptions } from '@tanstack/query-db-collection' + +type Post = { id: number; createdAt: number; title: string } + +const postsCollection = createCollection( + queryCollectionOptions({ + queryClient, + queryKey: ['posts', 'rows'], + syncMode: 'on-demand', + getKey: (post: Post) => post.id, + queryFn: async (ctx) => { + const { where, orderBy, offset = 0, limit } = ctx.meta?.loadSubsetOptions ?? {} + + // Translate every filter and sort, or reject unsupported expressions. + // This stable, serializable value identifies one ordered result sequence. + const request = api.translatePostQuery({ where, orderBy }) + + const pager = createCursorPager({ + queryClient, + queryKey: ['posts', 'cursor-pages', request], + staleTime: 60_000, + gcTime: 5 * 60_000, + fetchPage: async (cursor, signal) => { + const response = await api.listPosts({ ...request, cursor, signal }) + + // Only null means that the source is exhausted. + return { rows: response.items, nextCursor: response.nextCursor ?? null } + }, + }) + + return pager.read({ offset, limit }, ctx.signal) + }, + }), +) +``` + +##### Give each result sequence its own key + +The pager's query key identifies one ordered backend result, not one requested +window. Leave `offset` and `limit` out of the key so wider reads can extend the +same cached prefix. Include everything that changes the result—such as the +tenant, source, filters, and order. + +Keep row data and cursor pages under sibling keys: + +```text +['posts', 'rows'] QueryCollection row array +['posts', 'cursor-pages', request] backend pages and cursors +``` + +They cannot share a key because the two cache entries have different shapes. +Do not put cursor pages beneath the row key either: manual collection writes +target entries under the row prefix. A shared resource prefix such as +`['posts']` still lets you cancel or invalidate both kinds of data together. + +The example creates a small pager object for each `queryFn` call. Those objects +still share cached pages and in-flight fetches through QueryClient. Each pager +serializes its own reads. Retain one pager per result sequence only if calls +must also share that queue or you need to call `pager.reset()` later. + +##### What happens when the collection asks for rows + +For fresh data, the pager uses every cached page it can and fetches only the +missing suffix. `nextCursor: null` is the sole exhaustion signal: a short or +empty page with another cursor does not end the sequence. An omitted `limit` +drains the source; a zero limit performs no fetch. Repeated continuation tokens +throw instead of looping forever. + +Query owns freshness, garbage collection, retries, and invalidation. Pass +`staleTime` and `gcTime` to the pager or let it inherit QueryClient defaults. +Configure retries through QueryClient defaults for the page-key prefix; +imperative Query fetches do not retry by default. The pager keeps every page in +its original form, so Query defaults for `maxPages` and `select` do not apply. + +When cached pages are stale, the next read refetches every page loaded so far, +starting with the first. It does this even for a shallow window because old +continuation tokens belong to the old sequence. Staleness does not start a +background timer. The page query has no lasting observer, so its `gcTime` may +expire while the collection's published rows remain visible. + +A failed read rejects rather than returning a partial window. Query keeps the +last successful pages, so a later read can retry. Treat their rows as immutable, +just like other Query data. `read()` returns references from the cached pages, +not detached copies, and does not promise the same array or object identity +across calls. + +##### Refresh or cancel the sequence + +After a mutation—or whenever you need a forced refresh—cancel the shared +resource prefix before invalidating it: + +```typescript +await queryClient.cancelQueries({ queryKey: ['posts'] }) +await queryClient.invalidateQueries({ queryKey: ['posts'] }) +``` + +Cancelling first stops an old page append from completing after invalidation and +marking the old sequence fresh again. It rejects readers waiting on that fetch +with an `AbortError` and prevents a late result from entering the cache. The +backend must honor the signal if you also want to stop its network work. + +Aborting the signal passed to `read()` rejects only that reader; it does not +cancel a page fetch shared with another reader. A cancelling Query refetch moves +waiting readers to the replacement fetch. `collection.utils.refetch()` refreshes +the row queries but may reuse fresh cursor pages. `pager.reset()` removes one +pager's exact page key and cancels its queued reads, but does not refresh the +collection's row query. + +##### Backend contract + +The helper handles cursor traversal only. Your adapter must: + +- translate every requested `where` and `orderBy`, or reject unsupported + expressions; +- apply filters and ordering before pagination; +- use a deterministic total order, including a unique tie-breaker; +- keep that ordered result consistent while its cursor sequence is read; and +- return `nextCursor: null` only when the result is authoritatively exhausted. + +`LoadSubsetOptions.cursor` contains query-expression hints, not the backend's +opaque token. `createCursorPager` therefore addresses the cursor sequence by +offset and deliberately does not consume those hints. No client-side pager can +make a stable snapshot from an endpoint whose ordering changes between pages; +the endpoint must define its own consistency and cursor-expiry rules. + ## Important Behaviors ### Full State Sync @@ -553,13 +1187,74 @@ This pattern allows you to: ### Direct Writes and Query Sync -Direct writes update the collection immediately and also update the TanStack Query cache. However, they do not prevent the normal query sync behavior. If your `queryFn` returns data that conflicts with your direct writes, the query data will take precedence. - -To handle this properly: - -1. Use `{ refetch: false }` in your persistence handlers when using direct writes -2. Set appropriate `staleTime` to prevent unnecessary refetches -3. Design your `queryFn` to be aware of incremental updates (e.g., only fetch new data) +Direct writes update the Collection when their sync commit is accepted, or when +a running mutation handler settles. They do not start a Query refetch. The +following boundaries determine what the Query cache and other Collections see: + +| Event | Query cache | Collection rows | +| --- | --- | --- | +| Successful `queryFn` fetch | Stores the response at its exact Query key. | Collections that acquired that Query project its rows and reconcile subset ownership. | +| Accepted eager direct write | When the full-result response is representable, patches its changed keys and preserves unrelated rows. | The writer receives the edit. Compatible eager Collections sharing that Query, and on-demand peers that already acquired it, project the cache patch. | +| Accepted on-demand direct write | When no other Collection owns the Query, patches changed keys already present in eligible active raw arrays or arrays exposed directly by `select`. Derived projections stay unchanged. | The writer accepts the edit without inferring new subset membership or starting a request. An unacquired peer cannot treat the local cache edit alone as a new server result. | + +Each on-demand Query cache entry may represent a different predicate, order, +limit, or offset. Direct writes remove inactive entries under the Collection's +query-key prefix when no other Collection or observer retains them; unrelated +entries outside that prefix remain. A direct write cannot infer whether an +inserted row belongs in a scoped result or which row replaces a deleted item +in a limited window. A new direct insert has no inferred subset owner and +survives subset unload. + +Fetch order applies per key. A fetch started before an accepted direct write +cannot undo that write's claimed keys; a fetch started later may reconcile or +replace them. While a row remains continuously owned by at least one loaded +subset, if overlapping subset fetches both return that key, the later-started +successful fetch controls its Collection value even when the older request +settles last. Once all owners release the row, reacquiring a subset projects +that Query's retained cache entry under the usual `staleTime` and `gcTime` +rules. A cache patch is not a successful fetch: it does not clear a recorded +Query error, pending invalidation, or staleness. + +A later successful complete, unbounded response for a matching scope can +claim an ownerless direct row or remove it when omitted. A limited, offset, +cursor, failed, or pre-write response cannot prove that omission. The absence +claim belongs to that scope; it does not invalidate another subset's Query +cache entry. For example, suppose on-demand scope A has loaded an empty result, +and scope B overlaps A. Both scopes match row `x`: + +1. `writeInsert(x)` adds an ownerless row. B starts a fetch that will return `x`. +2. An explicit refetch of A starts later and returns a complete empty result. A removes `x`. +3. B's earlier-started fetch then returns `x`. B can project `x` again because A's empty response did not establish global absence across Query entries. + +This is a lower-level API. Use `collection.utils.refetch()` to request current +server rows for tracked scopes, but do not treat one scope's result as a global +snapshot. If an older request for the same Query is still in flight, that +explicit refetch starts a new request. + +Deleting a fetched row ends its existing subset ownership. Inserting or +upserting the same key afterward creates a new direct row without inheriting +those old subset claims; it can survive their unload until a later Query result +claims it or you delete it explicitly. + +An eager Collection that subscribes later can project a representable cached +edit without starting a request, even with `enabled: false`. +An on-demand Collection that has not acquired its scope may instead +revalidate on acquisition; another Collection's local cache edit alone has no +server-result authority for that scope. An unrelated later direct write does +not change fetch order for an earlier written key. + +Dehydrating a patched Query cache does not carry its in-memory direct-write +provenance into another QueryClient. A Collection loading that hydrated cache +can see the local edit as cached data. Refetch after hydration when the new +client needs a server-confirmed result. + +A fresh persisted Collection instance cannot recover the order of a stored +direct write and a retained Query cache row with the same key, whether the row +has a subset owner or not. It refetches that Query when the subset is acquired, +then applies the new server result. The earlier direct write still starts no +request by itself. + +Await direct writes inside persistence handlers to apply confirmed data before settlement. Handlers do not refetch automatically. An on-demand `queryFn` still needs to return the complete current result for its pushed-down predicate, order, limit, and offset when it does run. For robust real-time synchronization, use a collection backed by a sync engine that owns snapshot and change-stream ordering. ## Complete Direct Write API Reference @@ -570,12 +1265,25 @@ All direct write methods are available on `collection.utils`: - `writeDelete(keys)`: Delete one or more items directly - `writeUpsert(data)`: Insert or update one or more items directly - `writeBatch(callback)`: Perform multiple operations atomically -- `refetch(opts?)`: Manually trigger a refetch of the query +- `refetch(opts?)`: Trigger a refetch of the query ## QueryFn and Predicate Push-Down When using `syncMode: 'on-demand'`, the collection automatically pushes down query predicates (where clauses, orderBy, limit, and offset) to your `queryFn`. This allows you to fetch only the data needed for each specific query, rather than fetching the entire dataset. +With a static `queryKey`, an active, successfully applied request for the +entire source can also serve later filtered or windowed live queries without +another `queryFn` call. The full Query stays active until those queries release +their demand. A narrower scope with retained persisted ownership makes an +exact-scope `queryFn` call to revalidate its rows and owners. An unfinished +full request does not provide this coverage, and +`refetch: true` still fetches the exact requested subset. Function-built +`queryKey` values keep their own cache identity and do not use this reuse path. +Your `queryFn` must return the complete requested result, including every row +for an unbounded request. +Refetching a narrower subset does not invalidate a still-active full Query; +refresh the full Query when you need a new source-wide snapshot. + ### How LoadSubsetOptions Are Passed LoadSubsetOptions are passed to your `queryFn` via the query context's `meta` property: @@ -853,26 +1561,26 @@ const comparisons = extractSimpleComparisons(where) ### Using Query Key Builders -Create different cache entries for different filter combinations: +With `syncMode: 'on-demand'`, a static key such as `queryKey: ['products']` +automatically includes the subset demand identity. Filters, ordering, limits, +offsets, and cursor hints distinguish cache entries when they change the +requested data. Prefer a static key when you do not need a custom key structure. + +A function-based `queryKey` replaces that automatic key construction. Include +all options that change the requested data; omitting `offset`, for example, +can make different windows share a cache entry. Use `getLoadSubsetDemandKey` +from `@tanstack/db` to retain the same subset identity as a static key, including +`limit: 0`: ```typescript +import { getLoadSubsetDemandKey } from '@tanstack/db' + const productsCollection = createCollection( queryCollectionOptions({ id: 'products', - // Dynamic query key based on filters queryKey: (opts) => { - const parsed = parseLoadSubsetOptions(opts) - const cacheKey = ['products'] - - parsed.filters.forEach(f => { - cacheKey.push(`${f.field.join('.')}-${f.operator}-${f.value}`) - }) - - if (parsed.limit) { - cacheKey.push(`limit-${parsed.limit}`) - } - - return cacheKey + const demandKey = getLoadSubsetDemandKey(opts) + return demandKey === undefined ? ['products'] : ['products', demandKey] }, queryClient, getKey: (item) => item.id, @@ -888,21 +1596,16 @@ When using a function-based `queryKey`, all derived keys **must extend the base TanStack Query uses prefix matching for cache operations internally. The query collection relies on this to find all cache entries belonging to a collection — including stale entries from destroyed query observers that are still held in cache due to `gcTime`. If derived keys don't share the base prefix, cache updates may silently miss entries, leading to stale data. -```typescript -// ✅ Correct: base key ['products'] is a prefix of all derived keys -queryKey: (opts) => { - if (opts.where) { - return ['products', JSON.stringify(opts.where)] - } - return ['products'] -} +The example above returns `['products']` for `queryKey({})` and keeps it as +the prefix of every derived key. Avoid changing the prefix for subset demands: +```typescript // ❌ Wrong: base key ['products-all'] is NOT a prefix of ['products-filtered', ...] queryKey: (opts) => { - if (opts.where) { - return ['products-filtered', JSON.stringify(opts.where)] - } - return ['products-all'] + const demandKey = getLoadSubsetDemandKey(opts) + return demandKey === undefined + ? ['products-all'] + : ['products-filtered', demandKey] } ``` diff --git a/docs/collections/query-initial-placeholder-data-design.md b/docs/collections/query-initial-placeholder-data-design.md new file mode 100644 index 0000000000..9ee866f325 --- /dev/null +++ b/docs/collections/query-initial-placeholder-data-design.md @@ -0,0 +1,249 @@ +# Query Collection initial and placeholder data semantics + +## Status and scope + +This document defines the semantics for TanStack Query `initialData` and +`placeholderData` at the `@tanstack/query-db-collection` boundary. It is the +design follow-up for [RFC #1643](https://github.com/TanStack/db/issues/1643) +and [issue #346](https://github.com/TanStack/db/issues/346). The accompanying +implementation adds the approved eager `initialData` behavior without changing +the persistence format. + +The adapter connects two different models: + +- TanStack Query owns a document cache and remote-query lifecycle. +- TanStack DB owns normalized rows, local queries, and optimistic writes. +- Query Collection projects a Query response into rows and records which Query + key owns each materialized row. + +`initialData` and `placeholderData` must not be treated as equivalent ways to +provide an array. In Query Core 5.90.20, `initialData` initializes Query cache +state with `status: "success"` and a `dataUpdatedAt` timestamp. By contrast, +`placeholderData` is computed per observer only while Query state is pending; +it produces an observer result with `isPlaceholderData: true` but is not stored +in Query state. + +## Decision + +Support Query-owned `initialData` as an additive, eager-mode option. Materialize +it immediately through the existing row extraction and ownership pipeline. +Do not expose or materialize `placeholderData` in the first implementation. + +Query Collection can already materialize data that was seeded or hydrated into +the QueryClient before the observer is created, and QueryClient defaults can +already provide `initialData` indirectly. That is useful existing behavior, but +it does not close the configuration gap. Applications commonly share one +QueryClient across many Query Collections: a client-wide default is too broad, +while imperative `setQueryData` requires coordinating collection construction, +exact Query keys, and initialization elsewhere. The additive field supplies a +collection-local declaration while leaving Query as the cache authority. + +The minimal additive API is: + +```ts +initialData?: TQueryData | (() => TQueryData) +initialDataUpdatedAt?: number | (() => number | undefined) +``` + +These remain flat top-level fields. Their types describe the original Query +response, not the extracted row array. Consequently, wrapped responses use the +same adapter `select` as network responses: + +```ts +queryCollectionOptions({ + queryKey: ["todos"], + queryFn: fetchTodos, + initialData: { items: serverTodos, nextCursor: null }, + select: (response) => response.items, + // ... +}) +``` + +Query keys still define cache identity. If two Query Collections on the same +QueryClient use the same exact key, they observe one shared Query document; +`initialData` initializes that document only when it does not already exist. +Collection-local configuration does not create collection-local cache data, and +later observers must not replace an existing document with their initializer. +Collections that require independent initial documents must use distinct keys. + +This initial API is limited to `syncMode: "eager"`. A single configuration-level +value cannot lawfully initialize an open-ended family of on-demand subset keys, +and Query's `initialData` function receives no query key or subset context. +Applications that already know data for an exact on-demand key should seed or +hydrate that Query cache entry instead. A future subset-aware initializer would +need an explicit key/subset argument and a separate design. + +`placeholderData` remains Query UI vocabulary. A DB collection has no +observer-local result surface: materializing a placeholder would make it visible +to every DB query and mutation, assign it row ownership, and potentially persist +it. Callers should render placeholders in the consuming UI. A future opt-in +temporary-row feature, if needed, should be a DB feature with explicit provenance +and lifecycle rather than Query's `placeholderData` option. + +## Authority model + +"Authoritative" has two dimensions here. Query owns the authoritative document +for a Query key, while DB owns the current normalized row state. A server result +is the newest remote snapshot, but local optimistic transactions can temporarily +overlay its rows. + +| Phase | Query document authority | Materialized row authority | Consequence | +| --- | --- | --- | --- | +| No cached data | None | Existing DB/persisted rows, if any | The collection waits for Query; absence is not an empty result. | +| Initial data | Query cache `initialData` | Its projected rows, subject to normal local overlays | It is a real cached snapshot, not temporary presentation data. | +| Fetch/refetch in flight | Existing Query document | Existing rows | Loading does not clear rows or ownership. | +| Server success | Returned response replaces the Query document | Projected server rows reconcile the owning Query key | Missing rows lose this Query owner's lease; shared rows remain. | +| Fetch/refetch error | Last successful Query document | Existing rows | An error does not retract initial or previously fetched rows. | +| Placeholder presentation | No Query document | No rows | Placeholder data is never passed to DB. | + +`initialDataUpdatedAt` and `staleTime` remain Query-owned. They decide whether a +fetch starts; the adapter does not reproduce their freshness calculation. An +initial value is therefore a seed snapshot with ordinary Query authority, not a +weaker class of row waiting to be promoted. The first successful server result +reconciles it through the same path as any later refetch. + +## Behavior matrix + +| Concern | `initialData` | `placeholderData` | +| --- | --- | --- | +| Query cache | Stored as successful Query data | Not stored; observer-only | +| Existing cache entry | Existing cached/hydrated data wins; `initialData` is not reapplied | Not applicable | +| DB materialization | Immediate in eager mode | Never | +| Wrapped response | Adapter `select(initialData)` extracts rows; original envelope stays cached | Unsupported | +| Function value | Evaluated by Query once when the Query is created | Not forwarded or evaluated by the adapter | +| Ownership | The Query key owns projected rows exactly like a server success | No ownership | +| Overlapping subsets | Not applicable to the initial eager-only API | Not applicable | +| Ready state | Initial successful result can make the collection ready synchronously | Cannot make the collection ready | +| Refetch success | Reconciles additions, updates, and removals normally | N/A | +| Refetch error | Initial rows and ownership remain; error state is reported | N/A | +| Cancellation/cleanup | Existing ownership cleanup rules apply; a cancelled fetch does not retract the cached seed | No rows to clean up | +| Query cache GC/unload | Existing Query-to-row ownership and persisted-retention rules apply | No effect | +| Query dehydration | Query owns persistence of the initial response | Never persisted | +| DB persistence/hydration | Rows and owner metadata use the existing format; no provenance tag is added | Never persisted or hydrated | +| Direct writes before server success | Allowed under the existing write rules below | No target rows exist | +| QueryClient defaults | Supported for eager `initialData`; see compatibility guard below | Must be suppressed at this adapter boundary | + +## Select and writes + +Adapter `select` remains a one-way row extractor. It is applied identically to +initial and network responses, and TanStack Query retains the original response +shape. Query observer-level `select` remains unsupported. + +Direct writes before the first server result follow the current authority rule: +they update DB immediately and may patch Query cache only when the reverse update +is lawful. A raw array can be replaced. For a wrapped response, the existing +best-effort patch is lawful only when `select` returns an array property of the +cached object by reference, allowing the wrapper to be preserved. A derived +projection such as `response.edges.map(...)` has no general reverse projection; +the adapter must leave that Query document unchanged and rely on invalidate or +refetch. It must never fabricate an envelope around rows. + +The next successful remote response remains authoritative for that Query key and +may overwrite a direct cache patch or normalized row value. Mutation handlers and +optimistic transaction barriers retain their existing semantics. + +## Ownership, persistence, and transitions + +Initial rows use the existing `queryToRows` and `rowToQueries` relationship. No +`seed`, `temporary`, or `placeholder` bit is added to a row. This keeps these +invariants intact: + +1. A successful result, whether initial or fetched, is a complete snapshot for + its Query key. +2. A row is deleted when a snapshot omits it only if no other Query key owns it. +3. Unload, cache GC, and collection cleanup remove only the relevant ownership. +4. A failed or cancelled fetch cannot turn the last successful snapshot into an + empty snapshot. +5. Persistence records only Query data and the existing row ownership metadata; + observer-only presentation state is never persisted. + +The expected transitions are: + +- **Initial, fresh:** materialize and become ready; do not fetch until Query's + normal freshness triggers say to do so. +- **Initial, stale:** materialize and become ready while Query fetches; success + reconciles the same ownership, and error retains the seed. +- **Loading without data:** keep the collection's prior independently owned or + hydrated rows; do not infer an empty result. +- **Placeholder to loading/success/error:** the placeholder is UI-only. DB sees + no transition until success; error leaves DB unchanged. +- **Cleanup before network completion:** existing cancellation and readiness + listener cleanup apply. A late result must not mutate a cleaned-up collection. + +## Defaults and compatibility guard + +Query Collection currently constructs a `QueryObserver`, so QueryClient defaults +can contain semantic fields even when Query Collection does not expose them. +Implementation must explicitly enforce this design after Query defaults are +resolved: + +- reject an explicitly configured `initialData` in on-demand mode; +- prevent default `initialData` from initializing on-demand subset observers; +- prevent explicit or default `placeholderData` from reaching all Query + Collection observers; +- continue to let omitted eager `initialData` and `initialDataUpdatedAt` inherit + QueryClient defaults; +- never copy a function-valued initializer into adapter metadata or persisted + ownership metadata. Query Core may own it as an option and stores only its + evaluated data in Query state. + +Silently materializing default placeholder data would violate the public +compatibility table even before a top-level field is added. The guard is therefore +a correctness fix, not support for placeholder semantics. + +Other Query-owned options keep their current classification. Query key creation, +adapter `select`, subscriptions, `notifyOnChangeProps`, and structural sharing +remain adapter-owned or reinterpreted. No nested `queryOptions`, runtime binding +API, subset deduplication, or lease manager is introduced by this design. + +## Rejected designs + +- **Forward both options mechanically.** `result.isSuccess` is true for a + placeholder observer result, so the current success handler would normalize + presentation-only data and give it durable-looking ownership. +- **Tag placeholder rows and later promote or delete them.** Tags would have to + survive collisions with real rows, overlapping observers, local writes, + unload, GC, persistence, and hydration. Query's observer-local placeholder has + no collection-wide lifetime that can drive those transitions safely. +- **Treat initial rows as unowned temporary rows.** Query considers initial data + real cached data. Bypassing normal ownership would leak rows or allow cleanup + of one Query to remove rows still represented by another. +- **Seed every on-demand key from one value.** A collection-level initializer + cannot prove membership in arbitrary predicate/order/limit subsets. +- **Create a wrapped response from selected rows.** A read projection is not an + inverse. Fabricating metadata, cursors, or edges corrupts Query cache meaning. +- **Persist initializer functions.** Functions are not structured-clone safe and + are runtime configuration, not data. + +## Implementation and test sequence + +Each behavior PR should begin with the named failing or characterization tests. + +1. **Guard the existing boundary.** Add focused tests proving that explicit and + QueryClient-default `placeholderData` never materialize, never mark the + collection ready, and never survive dehydration as data. Characterize current + default `initialData` behavior in eager and on-demand modes. Then suppress + placeholder and on-demand initialization when constructing observers. +2. **Add eager initial data.** Add the two flat typed fields and forward only + defined values so QueryClient defaults remain intact. Test static and function + values, fresh versus stale timestamps, synchronous readiness, fetch error, + cancellation, cleanup, an explicit on-demand configuration error, multiple + collections on one QueryClient, and same-key first-initializer-wins behavior. +3. **Lock projection and writes.** Test raw arrays, direct-property wrapped + responses, and derived projections. Verify the full initial envelope remains + in Query cache, lawful direct writes preserve it, unlawful reverse projection + does not fabricate one, and server success reconciles rows. +4. **Lock ownership.** Test initial-to-server row removal, overlapping ownership + with externally hydrated/persisted rows, GC/unload, remount within `gcTime`, + and late notification after cleanup. Reuse the existing ownership machinery; + do not add a second seed ownership map. +5. **Lock persistence.** Dehydrate and `structuredClone` initial raw and wrapped + data; hydrate Query and DB state; verify ownership reconciliation and that no + function appears in Query metadata or adapter persistence metadata. +6. **Document the shipped API.** Move the settled behavior into the Query Options, + row extraction, direct writes, on-demand, and persistence sections of the user + guide. Update RFC #1643 and close or narrow #346 only after these contracts ship. + +Placeholder materialization, subset-aware initialization, and a lawful general +reverse projection are separate future proposals. None is a prerequisite for the +minimal eager `initialData` API. diff --git a/docs/collections/rxdb-collection.md b/docs/collections/rxdb-collection.md index d139ddc7f4..a1ba038c89 100644 --- a/docs/collections/rxdb-collection.md +++ b/docs/collections/rxdb-collection.md @@ -2,8 +2,6 @@ title: RxDB Collection --- -# RxDB Collection - RxDB collections provide seamless integration between TanStack DB and [RxDB](https://rxdb.info), enabling automatic synchronization between your in-memory TanStack DB collections and RxDB's local-first database. Giving you offline-ready persistence, and powerful sync capabilities with a wide range of backends. diff --git a/docs/collections/trailbase-collection.md b/docs/collections/trailbase-collection.md index 938e714a52..ea1c5ead36 100644 --- a/docs/collections/trailbase-collection.md +++ b/docs/collections/trailbase-collection.md @@ -2,8 +2,6 @@ title: TrailBase Collection --- -# TrailBase Collection - TrailBase collections provide seamless integration between TanStack DB and [TrailBase](https://trailbase.io), enabling real-time data synchronization with TrailBase's self-hosted application backend. ## Overview @@ -194,11 +192,13 @@ export const todosCollection = createCollection( // Use in component function TodoList() { - const { data: todos } = useLiveQuery((q) => - q.from({ todo: todosCollection }) - .where(({ todo }) => not(todo.completed)) - .orderBy(({ todo }) => todo.created_at, 'desc') - ) + const { data: todos } = useLiveQuery({ + query: (q) => + q + .from({ todo: todosCollection }) + .where(({ todo }) => not(todo.completed)) + .orderBy(({ todo }) => todo.created_at, 'desc'), + }) const addTodo = (text: string) => { todosCollection.insert({ diff --git a/docs/community/resources.md b/docs/community/resources.md index 781c11963e..86ed35d53d 100644 --- a/docs/community/resources.md +++ b/docs/community/resources.md @@ -3,8 +3,6 @@ title: Community Resources id: community-resources --- -# Community Resources - This page contains a curated list of community-created packages, tools, and resources that extend or complement TanStack DB. ## Community Packages diff --git a/docs/config.json b/docs/config.json index 09ca7c62ba..1bc477dd48 100644 --- a/docs/config.json +++ b/docs/config.json @@ -26,14 +26,30 @@ { "label": "Guides", "children": [ + { + "label": "Migrate to the 1.0 RC", + "to": "guides/migrate-to-1-0-rc" + }, { "label": "Live Queries", "to": "guides/live-queries" }, + { + "label": "SSR and Hydration", + "to": "guides/ssr" + }, { "label": "Mutations", "to": "guides/mutations" }, + { + "label": "SQLite Persistence", + "to": "guides/sqlite-persistence" + }, + { + "label": "Offline Transactions", + "to": "guides/offline-transactions" + }, { "label": "Schemas", "to": "guides/schemas" @@ -42,6 +58,10 @@ "label": "Error Handling", "to": "guides/error-handling" }, + { + "label": "Error Codes", + "to": "errors" + }, { "label": "Creating Collection Options Creators", "to": "guides/collection-options-creator" @@ -51,6 +71,10 @@ { "label": "Collections", "children": [ + { + "label": "Convex Collection", + "to": "collections/convex-collection" + }, { "label": "Query Collection", "to": "collections/query-collection" @@ -71,6 +95,10 @@ "label": "PowerSync Collection", "to": "collections/powersync-collection" }, + { + "label": "IndexedDB Collection", + "to": "collections/indexed-db-collection" + }, { "label": "LocalStorage Collection", "to": "collections/local-storage-collection" @@ -88,6 +116,10 @@ "label": "React", "to": "framework/react/overview" }, + { + "label": "Preact", + "to": "framework/preact/overview" + }, { "label": "Vue", "to": "framework/vue/overview" @@ -177,6 +209,14 @@ { "label": "powerSyncCollectionOptions", "to": "reference/powersync-db-collection/functions/powerSyncCollectionOptions" + }, + { + "label": "createIndexedDB", + "to": "reference/functions/createIndexedDB" + }, + { + "label": "indexedDBCollectionOptions", + "to": "reference/functions/indexedDBCollectionOptions" } ], "frameworks": [ @@ -193,6 +233,23 @@ } ] }, + { + "label": "preact", + "children": [ + { + "label": "Preact Hooks", + "to": "framework/preact/reference/index" + }, + { + "label": "useLiveQuery", + "to": "framework/preact/reference/functions/useLiveQuery" + }, + { + "label": "useLiveInfiniteQuery", + "to": "framework/preact/reference/functions/useLiveInfiniteQuery" + } + ] + }, { "label": "solid", "children": [ diff --git a/docs/contributing/ci.md b/docs/contributing/ci.md new file mode 100644 index 0000000000..2d7fc63204 --- /dev/null +++ b/docs/contributing/ci.md @@ -0,0 +1,54 @@ +# PR CI + +The PR workflow runs eight test jobs independently. Each job builds its selected +packages and their workspace dependencies before running tests. Builds retain +pnpm's dependency order; package tests run without that ordering, with at most +two package processes and two Vitest workers per process. + +| Group | Scope | +| --- | --- | +| `db-1`, `db-2` | Two Vitest file shards of DB, excluding the replay process suite | +| `db-replay` | The complete `oracle-replay.test.ts` suite | +| `ivm-offline` | IVM and offline transactions; explicit test-source typechecks and dependency version checks | +| `sqlite-core` | SQLite persistence core | +| `sqlite-adapters` | Browser, Node, Electron, Cloudflare, React Native, Expo, Capacitor, and Tauri persistence | +| `frameworks` | React, React Router, Solid, Vue, Svelte, and Angular | +| `collections` | Electric, Query, PowerSync, RxDB, TrailBase, and the shared E2E suite container | + +`scripts/ci-tests.mjs` owns the package assignments. It rejects an unassigned or +multiply assigned package test script. When adding a package with tests, assign +it to a group and keep the workflow matrix in sync if adding a new group. + +Run a group locally after installing the locked dependencies: + +```sh +node scripts/ci-tests.mjs check +node scripts/ci-tests.mjs build db-1 +node scripts/ci-tests.mjs test db-1 +``` + +The DB jobs retain coverage and typechecking. They write distinct Vitest blob +reports, which the final `Test` job merges into the full DB coverage and test +report. That job first requires both test matrices to succeed. Failure, +cancellation, or a skipped matrix cannot produce a successful aggregate check. +Other packages retain their existing coverage and typecheck configurations. +Oracle campaigns and their run budgets are unchanged. + +The E2E workflow has three groups: browser/SSR/Query (including the optional +Electric suites), Node/Electron persistence, and Cloudflare/mobile persistence. +Each builds its dependencies once. Individual suites retain their serial +execution where a shared database requires it. The final `Run E2E Tests` check +requires all groups to succeed. Superseded runs are cancelled per PR. Existing +Electric and mobile-runtime enablement conditions still apply. + +Preview publication and compressed-size comparison run independently. The size +job compares DB and React DB in one pass, building the required dependencies +at both the PR head and base. Its commands must exist on the base revision too; +a build script introduced only by a PR cannot be used for this comparison. + +The target is completion within five minutes, including setup, builds, and +report aggregation. Measure job execution and queue delay separately, and track +both workflow creation-to-completion time and total runner minutes. Before this +split, 30 successful PR runs from October 2–5, 2026 had a Test median of 12:39 +and P95 of 13:25, excluding queueing. These baseline measurements do not prove +the new workflow meets the target; use hosted runs to tune the slowest group. diff --git a/docs/contributing/designs/source-failure-and-health/HEALTH-CANDIDATE.md b/docs/contributing/designs/source-failure-and-health/HEALTH-CANDIDATE.md new file mode 100644 index 0000000000..9478c36095 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/HEALTH-CANDIDATE.md @@ -0,0 +1,160 @@ +# Candidate: the source health channel and live-query health tree + +> **Superseded (2026-10-16).** The error surface was redesigned: status +> `crashed`, node `failureReason`/`error`, `fail()`, `reload()`, and +> `collection.health`. Read `error-surface/README.md` and `error-surface/RULES.md`, +> the shipped `docs/guides/error-handling.md`, and +> `packages/db/src/query/live/ARCHITECTURE.md` laws 14, 16, and 17. + +Status: superseded candidate design, version 1. It came from a Field Lab +design-grammar run. The shipped design differs; read `PLAN.md`, the shipped +guides, and `packages/db/src/query/live/ARCHITECTURE.md` laws 16 and 17. + +## Success standard (frozen, user-confirmed) + +- H1 After the first ready, a sync failure is metadata. Status stays `ready`; + data stays, even if increasingly stale. +- H2 Before the first ready, the gate is unchanged: a final failure is + `markError`, reaches every consumer, and readiness waits reject. +- H3 The framework never heals or retries. Health reports what the source last + said; only the source clears its failure by reporting healthy sync again. +- H4 A live query carries its sources' health, through joins, includes, and + nested live queries. +- H5 Hooks expose health without changing data, `isReady`, or `status`. No + flicker; Suspense does not throw after ready. +- H6 (open) A subset load for new demand that fails after ready is decided + per request: it affects the requesting query, not the source. +- H7 One adapter API reports post-ready failures, replacing today's mix of + ready-to-`error` transitions and adapter utilities. +- H8 query-db's `utils.lastError`, `isError`, and `errorCount` keep working. +- H9 An Effect hears about health changes after ready instead of being + disposed. +- H10 A persisted collection keeps serving local rows when upstream sync fails + after ready; health says upstream is degraded. +- H11 `useLiveQuery` returns an `health` structure describing the tree of + collections it depends on. It always has the same shape, shows whether each + node is in error, and carries detailed error info when in error, so apps + can parse it. + +Prior law (frozen earlier): a source either keeps trying (loading), becomes +ready, or reports a final error for its sync attempt; the framework never +heals an attempt; recovery is app-level (`cleanup()` then `preload()`). + +## Source trace (current code) + +- Status is the only propagated channel: live queries handle sources' + `status:change` (`db/src/query/live/collection-config-builder.ts:1104`); + hooks derive `isError` from status (`react-db/src/useLiveQuery.ts`). +- Electric (`electric.ts:2628, 2708`) and PowerSync (`powersync.ts:826`) + switch a ready collection to `error` after ready; PowerSync then heals with + `markReady` (`powersync.ts:813`). +- query-db exposes `utils.lastError`, `isError`, `errorCount`, reset after a + successful result (`query-db-collection/src/query.ts:325-332`). +- Hooks already expose a parallel metadata channel for persistence: + `persistedStatus`, `persistedError` (`react-db/src/useLiveQuery.ts`). +- A live query's direct sources come from `extractCollectionSources(query)` + (`collection-config-builder.ts:197`); a source can itself be a live-query + Collection. + +## The candidate + +### Two independent axes + +- **Gate (`status`)**: `idle | loading | ready | error | cleaned-up`. Decided + only by the initial sync of an attempt. Unchanged from today except that + `error` is final for the attempt. +- **Health**: `healthy | error`, plus error info. Meaningful after the first + ready. Only the source changes it. + +### Adapter API (sync function params) + +```ts nocheck +sync: ({ begin, write, commit, markReady, markError, reportError, reportHealthy }) => { ... } +``` + +- `markError(error)` before the first ready: the final initial failure (gate). +- `reportError(error)` after the first ready: health becomes `error` with this + cause. Status and rows do not change. +- `reportHealthy()`: the source has synced successfully again; health becomes + `healthy`. +- `markError(error)` after the first ready is forwarded to `reportError(error)` + with a development warning, so existing adapters keep working while they + migrate. +- Calling `markReady()` after `markError()` in one attempt throws (contract + violation). + +### Collection surface + +```ts nocheck +collection.health // { isError: boolean, error: CollectionErrorInfo | null } +collection.on('health:change', (event) => { ... }) + +type CollectionErrorInfo = { + phase: 'initial' | 'sync' | 'request' + name: string + message: string + cause: unknown + at: number // epoch milliseconds +} +``` + +`phase: 'initial'` mirrors a gate `error`; `phase: 'sync'` is a post-ready +health report; `phase: 'request'` is a subset load failure (H6). + +### The health tree + +Every live-query Collection exposes `health`, and every framework hook returns +it beside `data`: + +```ts +type HealthNode = { + collectionId: string + kind: 'source' | 'live-query' + status: CollectionStatus // the node's gate + isError: boolean // this node: gate error, health error, or request error + hasErrors: boolean // this node or any descendant + error: CollectionErrorInfo | null // detail when isError + sources: Array // dependencies; [] for a base source +} +``` + +- The root is the live query itself. Its `sources` are the query's + collections in query order (`from`, then joins, then includes), each + recursing through nested live queries. +- The shape is fixed by the query: every dependency is present whether or not + it is in error. A collection reached by two paths appears once per path. +- Nodes mirror their collections in the same update as the change. +- A post-ready request failure (H6) sets `error` with `phase: 'request'` on + the live-query node that issued the request. The source stays healthy. +- `isError` on the hook result keeps its current meaning: the gate is + `error`. `health.hasErrors` reports anything anywhere in the tree. + +### Effects + +`createEffect({ onSourceHealth?: (health: HealthNode) => void })`. After +the first ready a health change calls it; the Effect is not disposed. Before +the first ready, a gate error still disposes the Effect through +`onSourceError`. + +### Adapters + +- Electric and PowerSync replace post-ready `markError` with `reportError` and + call `reportHealthy` after their next successful sync. PowerSync's + `markReady` recovery becomes `reportHealthy`. +- query-db keeps `utils.lastError/isError/errorCount` and also calls + `reportError`/`reportHealthy`; it no longer calls `markReady` from `error`. +- Persisted collections: local storage failures stay on `persistedError`; + upstream failures after ready are the collection's health. + +### Recovery + +App-level only. `cleanup()` then `preload()` starts a fresh attempt and resets +health to `healthy`. + +## Model check + +`SourceHealth.tla` checks stable shape, mirroring through a nested live query, +only-the-source-clears, gate independence after ready, and that a gate error +is explained by a tree node. Five weakened designs each produce their +counterexample. The model has two sources and one nested live query; it does +not cover a collection reached by two paths. diff --git a/docs/contributing/designs/source-failure-and-health/PLAN.md b/docs/contributing/designs/source-failure-and-health/PLAN.md new file mode 100644 index 0000000000..68ac6dbf9e --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/PLAN.md @@ -0,0 +1,300 @@ +# Plan: final initial errors and the source health channel + +> **Superseded (2026-10-16).** The error surface was redesigned: status +> `crashed`, node `failureReason`/`error`, `fail()`, `reload()`, and +> `collection.health`. Read `error-surface/README.md` and `error-surface/RULES.md`, +> the shipped `docs/guides/error-handling.md`, and +> `packages/db/src/query/live/ARCHITECTURE.md` laws 14, 16, and 17. + +Status: implemented on `feat/source-health-channel`; the pull request is next. +It ships as one pull request, because it is a breaking change and a +significant product change. It replaces #2129. Later decisions (2026-10-14): +a crashed Collection ends every live query and Effect above it; terminal local +persistence failures crash; `request` stays the public phase name; a live +query's Collection health includes its request errors. `markError` means the sync adapter gave up in both phases, so `reportError` has no `final` option and nothing warns; query-db's error utilities and the infinite query's `error` field give way to Collection health. + +Branch: `feat/source-health-channel` (worktree +`.worktrees/source-health-channel`). + +## 1. Decisions (maintainer, 2026-10-13) + +1. **A retry that succeeds is a success.** While the sync adapter retries, it reports + nothing. The Collection stays `loading`. (Already in #2127 for Electric.) +2. **`markError` is final for its sync run.** A sync adapter calls it only when + it gives up. The framework never heals a sync run: no `error` → `ready`. +3. **Recovery is the app's job.** `cleanup()` then `preload()`, a refetch, or a + user refresh starts a fresh attempt. The framework adds no retries. +4. **A final initial error reaches every consumer with its cause**: readiness + waits and one-shot reads reject, live queries enter error for every shape, + Suspense throws, Effects are disposed through `onSourceError`, and + subscribers get `lastError` and `loadSubset:error`. +5. **`status` is only the initial gate.** After the first ready, a sync + failure is metadata on the Collection. The data stays, even if it grows + stale. +6. **`useLiveQuery` returns an `health` tree** for every collection the query + depends on. It always has the same shape, shows whether each node is in + error, and carries detailed error info, so apps can parse it. +7. A buggy source or an app with endless retries can still look stuck. That is + a plugin or app problem, not a framework guarantee. +8. Generated API reference docs are produced on release by an automated pull + request. Do not edit them in this pull request. + +Rationale and controls: `README.md` (failure-law grammar, `SourceFailure.tla`, +10 configurations) and `HEALTH-CANDIDATE.md` plus `SourceHealth.tla` (health +channel, 6 configurations). The user-facing target is `USER-DOCS-DRAFT.md` +(candidate v2). + +## 2. Target design (candidate v2) + +Summarized from `USER-DOCS-DRAFT.md`; that file is the source of truth for +names and shapes. + +- **Two axes.** Gate: `status` (`idle | loading | ready | error | + cleaned-up`), decided only by an attempt's initial sync. Health: per + Collection, changed only by its sync adapter after Collection readiness. +- **Adapter API.** Sync params gain `reportError(error)` and + `reportHealthy()`. `markError` means the sync adapter gave up: an initial + error before Collection readiness, a final health error after it. + `markReady()` after `markError()` in one sync run throws. +- **Collection surface.** `collection.health` and a `health:change` event. +- **Error info.** `{ phase: 'initial' | 'sync' | 'request', final, name, + message, code?, at, cause }`. Everything except `cause` is JSON-safe. +- **Health tree.** `HealthNode = { alias, collectionId, kind: 'source' | + 'live-query' | 'include', status, isError, hasErrors, error, instances?, + sources }`. + - One node per query alias, in query order, fixed by the query definition + (fracture F-a). A self-join appears once per alias. + - An include has one node for all its per-row child Collections, with + `instances: { total, inError }`. + - Nested live queries recurse. + - Healthy nodes are present with `isError: false`. +- **Final flag** (fracture F-b). `final: true` means the sync adapter stopped + trying; only a restart clears it. `reportHealthy()` cannot clear it. +- **Request errors** (fracture F-c). A load for new demand that fails after + Collection readiness sets `phase: 'request'` on the live-query node that issued it. The + Collection stays healthy. It clears when that demand later settles or is + released. +- **Hooks.** Return `health` beside `data`. `isError` keeps meaning "the + initial gate is `error`". Docs lead with `health.hasErrors` for stale data. +- **Effects.** `onHealthChange(health)` after Collection readiness; no disposal. + Initial gate errors still dispose through `onSourceError`. +- **SSR.** `health` is not dehydrated; the client builds its own tree. + +## 3. Work breakdown + +### 3.1 Core: `packages/db` + +- [ ] `collection/lifecycle.ts`: remove `error` → `ready` from the valid + transitions. `markReady()` after `markError()` in the same sync run + throws. Keep `error` → `cleaned-up` and `idle`. +- [ ] `collection/lifecycle.ts` / `sync.ts`: add Collection health state, `reportError`, + `reportHealthy`, the `final` rule, and reset on cleanup and restart. + Scope the callbacks to the sync-run epoch so a stale sync run cannot + report. +- [ ] `collection/events.ts`: add `health:change` to `AllCollectionEvents`. + Define the payload (previous, current) and the ordering: same tick, + after `status:change` when both change (the ordering is still to be + checked). +- [ ] `types.ts`: `SyncConfig` params, `CollectionErrorInfo`, + `HealthNode`, and the `Collection.health` and `health` types. +- [ ] `markError` after the first ready: forward to + `reportError(error, { final: true })` and warn in development. +- [ ] `query/live/collection-config-builder.ts`: + - [ ] a Collection entering `error` before the live query's initial-query readiness stays + a gate error for every shape (root, join, include alike); + - [ ] remove the `erroredSourceIds` recovery path (line ~1131) and its + status-based healing; + - [ ] post-readiness Collection health does not change the live query's status or + rows; + - [ ] build and maintain the `health` tree: per-alias nodes, include + roll-ups, nested live-query relay, and request errors; + - [ ] `failDemand` and `recordSubsetError`: before ready, terminal (gate); + after ready, a request error on the live-query node, cleared on + settle or release. +- [ ] `collection/subscription.ts`: keep `lastError` and `loadSubset:error` + for every rejected load (#2129 review C8). Do not detach demand for + recovery. +- [ ] `query/effect.ts`: `onHealthChange`; no disposal after Collection readiness. +- [ ] `live-query-adapter.ts` and `live-query-observer.ts`: add `health` to + the snapshot and status flags, so every framework adapter gets it from + one place. Keep `isError` as the gate. +- [ ] `query/live/pooled-live-query*`: decide and implement health for + pooled views that share sources (open question). +- [ ] `query/live/ARCHITECTURE.md`: replace #2129's law 16 with the final + error law and the health channel law. Update the cleanup and restart + section. + +### 3.2 Adapters + +- [ ] **Electric** (`electric-db-collection/src/electric.ts`, 9 call + sites): + - [ ] post-readiness `markError` at `:2628` and `:2708` becomes `reportError` + (`final` per site); + - [ ] `reportHealthy()` after the next successful up-to-date following a + problem; + - [ ] the SDK give-up path: before Collection readiness, `markError`; after it, + `reportError(..., { final: true })`. This extends #2127's E1 fix. +- [ ] **query-db** (`query-db-collection/src/query.ts`, 6 sites): + - [ ] a failed refetch after readiness calls `reportError`; a successful + result after a problem calls `reportHealthy()`; + - [ ] `:2556`: no `markReady()` when the status is `error` (V1); + - [ ] keep `utils.lastError`, `utils.isError`, and `utils.errorCount`, and + state how they relate to Collection health (assay compromise). +- [ ] **PowerSync** (`powersync-db-collection/src/powersync.ts`, 6 sites): + a failed tracking rebuild at `:826` calls `reportError`; a successful + rebuild at `:813` calls `reportHealthy()` instead of `markReady()` (V2, post-readiness health). +- [ ] **TrailBase** (`trailbase-db-collection/src/trailbase.ts`, 4 sites): + post-readiness stream failures call `reportError`. Check them against the + #2126 fix. + - [ ] A stream that ends, whether it closes or health, reports + `reportError(error, { final: true })` after Collection readiness. The + adapter does not resubscribe. The `trailbase` SDK (0.10.0) has no + reconnect or resume: `subscribe` is one `fetch` stream, and + `subscribeWs` closes without retrying. A correct reconnect would + have to subscribe again and re-list every active subset. The + maintainer decided to leave reconnect to the TrailBase maintainers. + - [ ] Update the stream-end lane in `tests/lifecycle-oracle.property.test.ts` + (added in #2126) from "rows stay, later changes are not delivered" + to also expect a final health error. +- [x] **RxDB** (`rxdb-db-collection/src/rxdb.ts`): no change. V10 is + closed by inspection: RxDB is eager and has no `loadSubset`, so a final + error has no pending subset loads; its only failure path is the initial + fetch, which calls `markError` before Collection readiness and rejects + readiness waiters under the core initial-error law. Open: the + `rxCollection.$` subscription has no error handler, so an RxDB stream + failure after readiness is not reported at all. Reporting it as a final + health error needs an RxDB fault a test can drive; owner: RxDB adapter, + witness: an observable error after readiness. +- [ ] **Persistence** (`db-sqlite-persistence-core/src/persisted.ts`, 19 + sites): + - [ ] forward upstream Collection health; keep local storage failures on + `persistedError`; + - [ ] follower tabs learn the leader's upstream health through the + coordinator (open question); + - [ ] check the browser and Electron hosts, and the node host. +- [ ] Local-only and local-storage Collections: confirm they never need + health, and document it. + +### 3.3 Framework hooks + +- [ ] React, Vue, Solid, Svelte, and Angular return `health` from every + live-query hook, including infinite-query hooks and + `useLiveSuspenseQuery`. +- [ ] Suspense: throws only for the initial gate, never for post-ready + health. +- [ ] Decide whether `health` keeps stable node identity for memoization + (open question). + +### 3.4 Tests and oracles + +- [ ] Flip the #1751 healing tests on purpose: each one that asserts + `error` → `ready` becomes "stays `error`; recovery needs a restart". + Search `collection-health.test.ts`, live-query recovery tests, and + query-db and PowerSync recovery tests. +- [ ] A new oracle owner for the health channel, from `SourceHealth.tla`'s + properties: stable shape, mirroring through nested live queries, only + the source clears, gate independence after ready, gate explained by a + node, `final` not clearable, and request errors that clear on settle or + release. Extend the topology beyond the TLA model: a collection reached + by two aliases, includes with per-row children, and pooled views. +- [ ] Model D (`includes-temporal-oracle.test.ts`): rework the source-error + lane to the final-error law. Start from `evidence/ + model-d-source-error-grammar.patch` (the C4, C5, and C6 grammar + dimensions: truncate during the error, ready → error, exact + acquisitions, and every `source-ready` position). Under the new law, + `source-ready` after `source-error` in one sync run is illegal, so those + histories become "restart, then recover". +- [ ] Shared framework conformance suite (`packages/db/tests/conformance/`): + a health scenario, so all five drivers check `health` the same way. +- [ ] Adapter witnesses: each adapter reports post-ready failures through + health and keeps its rows. Electric adds SDK give-up cases through the + real SDK (missing headers, retry exhaustion). +- [ ] Mutant calibration for each new law, including the TLA weakenings: + health flips status, nested live query doesn't relay, framework clears, + healthy nodes dropped, failure clears rows, heal on ready, framework + retry. +- [ ] Rerun the #2129 review probes in `evidence/ + pr-2129-review-probes.test.ts.txt` against the new law. Several + expectations change, because recovery now needs a restart. + +### 3.5 Hand-written docs (not generated reference) + +- [ ] `docs/guides/error-handling.md`: rewrite the collection error sections + around the two axes. Remove the contradiction between line ~361 (manual + recovery) and line ~534 (a refetch recovers). +- [ ] `docs/guides/collection-options-creator.md`: the adapter rules from + `USER-DOCS-DRAFT.md` (stay silent while retrying; `markError` only when + giving up before Collection readiness; `reportError` and `reportHealthy` after readiness; + never drop rows). +- [ ] `docs/guides/live-queries.md`: the `health` tree and the + `isError` / `health.hasErrors` callout. +- [ ] `docs/guides/sqlite-persistence.md`, `docs/guides/offline-transactions.md`: + upstream Collection health against `persistedError`. +- [ ] `docs/health.md`: the `markReady`-after-`markError` contract error, + and any new error code. +- [ ] `docs/collections/electric-collection.md`: `onError` retries are + silent; a give-up is final; post-readiness health. +- [ ] `docs/collections/local-storage-collection.md`: check its error-state + wording. +- [ ] `docs/collections/query-collection.md` (if present): how `utils` relate + to health; recovery by restart. +- [ ] Package skills: `packages/db/skills/db-core/custom-adapter/SKILL.md` + (remove "later successful work in the same sync run can call + `markReady()` to recover", line ~349, since recovery now requires a restart), and the React, Vue, Solid, and + Angular skill files. Confirm with the maintainer that these package + skills are in scope. +- [ ] Package READMEs that mention `isError` (for example + `packages/angular-db/README.md`). +- [ ] `docs/contributing/oracle-coverage.md`: the new health owner, and the + Model D source-error lane. +- [ ] `docs/contributing/oracle-reviews/progressive-source-readiness.md`: + record the decided law and the companion fixes (#2126, #2127, #2128). + #2129 carried this update and is being replaced. + +### 3.6 Release + +- [ ] Changeset: a breaking change for `@tanstack/db` and every adapter that + changes. Include a migration guide: + - an initial error no longer heals; recover with `cleanup()` and + `preload()`; + - after Collection readiness, failures no longer move a Collection or a live + query to `error`; read `health`; + - adapter authors: `reportError` and `reportHealthy`; + - `markReady()` after `markError()` now throws. +- [ ] PR body: the decisions above, the design, the evidence, and the + breaking changes. + +## 4. Order of work + +1. Merge #2126, #2127, and #2128 after their review findings. +2. Close #2129 in favor of this pull request, and move its review-record + update here. +3. Core lifecycle and the health channel, with the health oracle first (RED, + then GREEN). +4. Live queries: the gate for every shape, then the health tree, then + request errors. +5. Effects, then the observer and status flags, then the hooks and the + conformance suite. +6. Adapters, one at a time, each with witnesses. +7. Docs, then the changeset and the PR body, then review and CI. + +## 5. Open questions + +- The `health:change` ordering relative to `status:change` (proposed: same + tick, after it). Model-check it before relying on it. +- How follower tabs learn the leader's upstream health. +- Health for pooled live queries that share sources. +- Stable node identity in `health` for memoization. +- Which semver bump the packages get, given the 0.x line. +- Whether the shipped package skills are in scope for edits. + +## 6. Carried from the reviews + +- #2129 C1, C2, C3, C7, C9, C10, and S1 concerned recovery machinery that this + design removes. Recheck them against the new law using the probes in + `evidence/`. +- #2129 C8 (keep reporting `lastError` and `loadSubset:error`) is decided + yes. It is in 3.1. +- #2129 C4, C5, and C6 (Model D grammar gaps) carry over in 3.4. +- #2127 E1 (an SDK give-up must be reported) is fixed in #2127 for the + initial gate. The post-readiness half is in 3.2. diff --git a/docs/contributing/designs/source-failure-and-health/README.md b/docs/contributing/designs/source-failure-and-health/README.md new file mode 100644 index 0000000000..57396754ca --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/README.md @@ -0,0 +1,164 @@ +# Source failure law: design grammar and TLA+ control + +> **Historical.** This run predates the shipped error surface. See +> `error-surface/` for the current design record. + +This directory holds a Field Lab design-grammar run on the source failure law +that the maintainer chose on 2026-10-13. A source either keeps trying, becomes +ready, or reports a final error for its sync attempt. The framework never heals +an attempt. Recovery is the app's job and starts a fresh attempt. + +## Frozen preservation list + +U marks a user-required property, S a source-stated property (mostly from +#1751), and I an analyst-inferred property. The user confirmed the list and +corrected F8. + +| ID | Property | Basis | +| --- | --- | --- | +| F1 | While a source retries, it reports `loading`, not `error`. A retry that succeeds is a success. | U | +| F2 | `markError` is final for its sync attempt. The framework never moves it back to `ready`. | U | +| F3 | Recovery is the app's job and starts a fresh attempt: cleanup, then preload, a refetch, or a user refresh. The fresh attempt requests all demand again. | U | +| F4 | A final error reaches every consumer with its cause: readiness waits and one-shot reads reject, live queries enter error for every shape, Suspense throws, an Effect reports `onSourceError`, and subscribers get `lastError` and `loadSubset:error`. | U | +| F5 | `ready` means a usable snapshot exists. A failure never publishes a false or empty ready. | S | +| F6 | A failure after the first ready does not discard visible data. Open: whether such a failure moves the status to `error`. | S | +| F7 | Cleanup and restart revoke the old attempt. A stale attempt cannot mark the new one. | S | +| F8 | The framework leaves no wait pending forever: every wait resolves, rejects with the final cause, or ends quietly on cleanup. A buggy source or an app with endless retries can still look stuck; that is a plugin or app problem. | I, corrected by U | +| F9 | Retry policy belongs to the source or the app. The framework adds no retries. | U | +| F10 | Every source maps its native signals to three outcomes: still trying, ready, or final error, including give-ups inside an SDK. | I | +| F11 | Every consumer of one source observes the same outcome at the same point. | I | +| F12 | A request that fails while its source is healthy fails its requester, not the source. | I | + +## Model layer: candidate grammar + +Primitives (each traced to the frozen list): + +- **G1 attempt:** one sync run, from start to cleanup. It is the unit of + finality (F2, F7). +- **G2 outcome:** the source's signal, one of `trying`, `ready`, or + `final-error` (F1, F2, F10). +- **G3 snapshot:** usable visible rows (F5, F6). +- **G4 consumer:** a readiness wait, a live query (root or lazy), an Effect, or + a subscriber (F4, F11). +- **G5 request:** one demand's load. It resolves, fails locally, or fails with + its source (F12). +- **G6 app restart:** the only way to begin a new attempt (F3, F9). + +Rules: + +- **R1** `trying` keeps the status `loading` and fails no consumer (F1). +- **R2** `final-error` is absorbing within its attempt (F2). +- **R3** `final-error` reaches every consumer in the same step, with its cause. + Pending requests reject with that cause (F4, F8). +- **R4** `ready` requires a snapshot (F5). +- **R5** A failure after ready keeps the snapshot (F6). Whether the status + becomes `error` is **unresolved** (conflict C1). +- **R6** A local request failure fails only its requester (F12). +- **R7** Signals from a cleaned attempt are inert (F7). +- **R8** An adapter translates every give-up inside its SDK into `final-error` + (F10). +- **R9** Only the app begins an attempt (F3, F9). + +Active overlap and conflict: + +- **C1 (unresolved):** a failure after ready sits where R3 and R5 overlap. R3 + propagates a final error to every consumer; R5 keeps the visible snapshot. + PowerSync's comment at `powersync.ts:823` states the R3 side: "Do not leave + healthy consumers ready against a stale source." #1751 and query-db state + the R5 side: a refetch failure keeps the collection `ready`. The grammar + keeps both branches (adjacent forms AF1 and AF2). +- **C2 (low stakes):** Electric rejects loads and then calls `markError` in + the same turn, so a rejection can arrive before the status. Under R2 and R3 + both orders end with every consumer in terminal error. Only the reported + cause differs. + +## Evidence layer + +### Reconstruction against the current code + +Rebuilding the current arrangement from the grammar exposes these departures. +Each is an observation with a source pointer. None was invented to fit the +grammar. + +| ID | Departure | Rule | Pointer | +| --- | --- | --- | --- | +| V1 | query-db calls `markReady()` after any later successful application, so a refetch heals an initial error. | R2 | `query-db-collection/src/query.ts:2556` | +| V2 | PowerSync marks `error` after ready when a tracking rebuild fails, then marks `ready` when a later rebuild succeeds. | R2, R1 | `powersync-db-collection/src/powersync.ts:813, 826` | +| V3 | A live query leaves error when its errored sources become ready. | R2, R9 | `db/src/query/live/collection-config-builder.ts:1131` | +| V4 | The lifecycle allows `error` → `ready`. | R2 | `db/src/collection/lifecycle.ts:106` | +| V5 | The docs describe healing. | R2 | `docs/guides/error-handling.md:534`; `db/skills/db-core/custom-adapter/SKILL.md:349` | +| V6 | On `main`, Electric marks `error` before the `onError` retry decision. | R1 | `electric-db-collection/src/electric.ts:1970` (fixed by #2127) | +| V7 | With #2127, an SDK give-up after a retry object is not reported. | R8 | #2127 review finding E1 | +| V8 | #2129 detaches waiting loads on a source error instead of rejecting them. | R3 | #2129 `subscription.ts` `detachForErrorRecovery` | +| V9 | Lazy demand fails for good, while a root query follows the source status. | F11 | `collection-config-builder.ts:448` vs `:1131` | +| V10 | RxDB leaves pending loads on a final error unobserved. | R3 (evidence gap) | `rxdb-db-collection/src/rxdb.ts:283` | + +The survey found that post-ready failures keep the last snapshot readable in +every adapter. Electric also calls `markError` after ready +(`electric.ts:2628, 2708`), which places it on the R3 side of C1. + +### Ablation and model check + +`SourceFailure.tla` encodes G1 to G6 as variables and R1 to R9 as actions and +invariants. TLC 2.19 (rev 5a47802) ran with `-deadlock` and `MaxAttempts = 3`. + +| Configuration | Result | +| --- | --- | +| `safe` | no error, 31 distinct states | +| `weak-heal` (ablate R2) | `NoHeal` violated: Start, SourceReady, FinalError, SourceReady | +| `weak-reject-on-retry` (ablate R1) | `RetryIsNotFailure` violated: Start, TransientError | +| `weak-detach-waits` (ablate R3) | `PassThrough` violated: Start, TransientError, FinalError | +| `weak-clear-late` (ablate R5's snapshot clause) | `ReadyMeansRows` violated: Start, SourceReady, LateFailure | +| `weak-poison` (ablate R6) | `RequestFailureIsLocal` violated: Start, RequestFails | +| `weak-stale` (ablate R7) | `ErrorIsOwn` violated: Start, TransientError, FinalError, AppRestart, TransientError, StaleCallback | +| `weak-silent-give-up` (ablate R8) | `StoppedSourceReported` violated: Start, SdkGivesUp | +| `weak-framework-retry` (ablate R9) | `OnlyAppRestarts` violated: Start, FinalError, AutoRestart | +| `weak-lazy-terminal` (ablate F11 under healing) | `ConsumersAgree` violated: Start, TransientError, FinalError, SourceReady | + +Every rule is load-bearing: removing any one produces its expected +counterexample. The lazy asymmetry (V9) violates agreement only when healing is +also allowed. Under R2, root and lazy demand both end in terminal error, so +V9's bug class disappears instead of needing recovery code. + +Encoding receipt: in the first `weak-poison` run, `RequestFails` assigned +`live'` twice, so the poison branch was disabled and TLC found no error. The +action now assigns `live'` once in each branch. + +### Range, negative case, and limits + +- **Range:** untested. No independent marginal case was supplied. +- **Negative case:** a collection that silently returns from `error` to + `ready` when the network comes back ("stale-while-revalidate healing"). The + grammar must not generate it, and R2 excludes it. +- **Limits:** one source, two live-query shapes, one wait, and at most three + attempts. No fairness and no liveness. TLC checks the rules' internal + consequences, not that production follows them. + +## Adjacent forms + +- **AF1 degraded ready (C1, R5 branch):** a failure after ready keeps the + status `ready` and the rows, and exposes the error through utilities such as + query-db's `lastError`. Route: rule combination R4 + R5. Loss: consumers that + must not act on stale data get no status signal. +- **AF2 final after ready (C1, R3 branch):** a final failure after ready moves + the status to `error` and keeps the last rows readable while consumers report + the error. Route: rule combination R3 + R5. Loss: every dependent query + enters error even when its rows were still correct. +- **AF3 app retry helper (G6 + R9):** one call restarts a failed source and its + dependent live queries, replacing the manual sequence in + `error-handling.md`. Route: modular augment over the existing cleanup and + restart interface. Cost: a new public API. + +## Process record + +- Source and preservation list frozen with the user's correction to F8. +- Source facts: an Explore survey of every `markError` and `markReady` call + site, plus direct reads of `query.ts:2556` and `powersync.ts:805–830`. The + survey missed V1; the direct read found it. +- Every model element traces to a frozen item, as listed above. + +Run a configuration from this directory: + +```sh +~/.claude/skills/field-lab/scripts/tlc . -deadlock -config safe.cfg SourceFailure +``` diff --git a/docs/contributing/designs/source-failure-and-health/SourceFailure.tla b/docs/contributing/designs/source-failure-and-health/SourceFailure.tla new file mode 100644 index 0000000000..cba2a11d75 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/SourceFailure.tla @@ -0,0 +1,183 @@ +---------------------------- MODULE SourceFailure ---------------------------- +(***************************************************************************) +(* The source failure law: a source either keeps trying (loading), becomes *) +(* ready, or reports a final error for its sync attempt. The framework *) +(* never heals an attempt; the app recovers by starting a fresh attempt *) +(* (cleanup, then preload). Each element names the frozen property (F1 to *) +(* F12) that it encodes; README.md holds the trace table. *) +(* *) +(* One source serves a readiness wait (preload, stateWhenReady, *) +(* toArrayWhenReady, queryOnce), a direct live query, a live query over a *) +(* lazy join or include, and a skipInitial Effect. Consumers react in the *) +(* same step as the source signal, as their status listeners do. *) +(***************************************************************************) +EXTENDS Naturals + +CONSTANTS + MaxAttempts, + \* Weakenings: each encodes one plausible wrong design. + HealOnReady, \* the source may mark ready after its final error (old #1751 healing) + RejectOnRetry, \* a transient error fails consumers while the source retries + LazyTerminal, \* lazy demand fails for good while the root follows status + DetachWaits, \* waiting loads detach for recovery instead of rejecting + SilentGiveUp, \* the SDK gives up without the adapter reporting markError + StaleApplies, \* a callback from a cleaned attempt reaches the new one + PoisonSource, \* a request-specific failure marks the source failed + FrameworkRetry, \* the framework restarts a failed attempt by itself + ClearOnLateFail \* a failure after ready discards the visible snapshot + +Consumers == {"root", "lazy"} + +VARIABLES + attempt, \* current sync attempt number (F7) + status, \* "idle" | "loading" | "ready" | "error" + retrying, \* the source reported a transient error and is retrying (F1, F9) + alive, \* the source is still trying in this attempt (F10) + finalFailed, \* the source reported a final error in this attempt (F2) + rows, \* a usable snapshot is visible (F5, F6) + wait, \* readiness wait: "none" | "pending" | "resolved" | "rejected" | "aborted" + live, \* live query state per consumer: "loading" | "ready" | "error" + effect, \* "active" | "disposed" + reqFailed, \* the root query's own request failed on a healthy source (F12) + byApp \* the last attempt change came from the app (F3, F9) + +vars == <> + +Init == + /\ attempt = 0 /\ status = "idle" /\ retrying = FALSE /\ alive = FALSE + /\ finalFailed = FALSE /\ rows = FALSE /\ wait = "none" + /\ live = [c \in Consumers |-> "loading"] /\ effect = "active" + /\ reqFailed = FALSE /\ byApp = TRUE + +\* A fresh attempt: all demand is requested again and every wait is new (F3). +FreshAttempt(app) == + /\ attempt' = attempt + 1 /\ status' = "loading" /\ alive' = TRUE + /\ retrying' = FALSE /\ finalFailed' = FALSE /\ rows' = FALSE + /\ wait' = "pending" /\ live' = [c \in Consumers |-> "loading"] + /\ effect' = "active" /\ reqFailed' = FALSE /\ byApp' = app + +Start == attempt = 0 /\ FreshAttempt(TRUE) + +\* The source keeps trying: a transient error with a retry (F1). +TransientError == + /\ status = "loading" /\ alive /\ ~retrying + /\ retrying' = TRUE + /\ IF RejectOnRetry + THEN /\ wait' = IF wait = "pending" THEN "rejected" ELSE wait + /\ live' = [c \in Consumers |-> "error"] /\ effect' = "disposed" + ELSE UNCHANGED <> + /\ UNCHANGED <> + +RetrySucceeds == + /\ retrying /\ alive + /\ retrying' = FALSE + /\ UNCHANGED <> + +\* The source establishes a usable snapshot (F5). +SourceReady == + /\ alive /\ ~retrying + /\ (status = "loading") \/ (HealOnReady /\ status = "error") + /\ status' = "ready" /\ rows' = TRUE + /\ wait' = IF wait = "pending" THEN "resolved" ELSE wait + /\ live' = [c \in Consumers |-> + IF (c = "root" /\ reqFailed) \/ (LazyTerminal /\ live[c] = "error" /\ c = "lazy") + THEN live[c] ELSE "ready"] + /\ UNCHANGED <> + +\* A final error reaches every consumer with its cause (F2, F4). +FinalError == + /\ alive /\ status \in {"loading", "ready"} + /\ status' = "error" /\ finalFailed' = TRUE /\ retrying' = FALSE + /\ alive' = HealOnReady + /\ wait' = IF wait = "pending" /\ ~DetachWaits THEN "rejected" ELSE wait + /\ live' = [c \in Consumers |-> "error"] /\ effect' = "disposed" + /\ UNCHANGED <> + +\* The SDK gives up inside the source; the adapter must report it (F10). +SdkGivesUp == + /\ alive /\ status = "loading" + /\ IF SilentGiveUp + THEN /\ alive' = FALSE /\ retrying' = FALSE + /\ UNCHANGED <> + ELSE /\ status' = "error" /\ finalFailed' = TRUE /\ alive' = FALSE + /\ retrying' = FALSE + /\ wait' = IF wait = "pending" /\ ~DetachWaits THEN "rejected" ELSE wait + /\ live' = [c \in Consumers |-> "error"] /\ effect' = "disposed" + /\ UNCHANGED <> + +\* A later refresh failure after ready keeps the snapshot (F6). +LateFailure == + /\ status = "ready" /\ alive + /\ rows' = ~ClearOnLateFail + /\ UNCHANGED <> + +\* One request fails while its source is healthy (F12). +RequestFails == + /\ status \in {"loading", "ready"} /\ alive /\ ~reqFailed + /\ reqFailed' = TRUE + /\ IF PoisonSource + THEN /\ status' = "error" /\ alive' = FALSE + /\ wait' = IF wait = "pending" THEN "rejected" ELSE wait + /\ effect' = "disposed" /\ live' = [c \in Consumers |-> "error"] + ELSE /\ live' = [live EXCEPT !["root"] = "error"] + /\ UNCHANGED <> + /\ UNCHANGED <> + +\* A callback from the cleaned previous attempt arrives (F7). +StaleCallback == + /\ attempt > 1 /\ status = "loading" + /\ IF StaleApplies + THEN /\ status' = "error" /\ live' = [c \in Consumers |-> "error"] + /\ wait' = IF wait = "pending" THEN "rejected" ELSE wait + /\ effect' = "disposed" + ELSE UNCHANGED <> + /\ UNCHANGED <> + +\* The app recovers: cleanup ends the old waits quietly, then a fresh attempt (F3). +AppRestart == + /\ attempt > 0 /\ attempt < MaxAttempts + /\ FreshAttempt(TRUE) + +\* Weakening: the framework restarts a failed attempt by itself (F9). +AutoRestart == + /\ FrameworkRetry /\ status = "error" /\ attempt < MaxAttempts + /\ FreshAttempt(FALSE) + +Next == + \/ Start \/ TransientError \/ RetrySucceeds \/ SourceReady \/ FinalError + \/ SdkGivesUp \/ LateFailure \/ RequestFails \/ StaleCallback + \/ AppRestart \/ AutoRestart + +Spec == Init /\ [][Next]_vars + +--------------------------------------------------------------------------- +TypeOK == + /\ status \in {"idle", "loading", "ready", "error"} + /\ wait \in {"none", "pending", "resolved", "rejected", "aborted"} + /\ live \in [Consumers -> {"loading", "ready", "error"}] + /\ effect \in {"active", "disposed"} + +\* F1: while the source retries, nothing reports failure. +RetryIsNotFailure == + retrying => /\ status = "loading" /\ wait # "rejected" /\ effect = "active" + /\ live["lazy"] # "error" +\* F2: a final error is final for its attempt. +NoHeal == finalFailed => status = "error" +\* F4 and F8: a final error reaches every consumer, and no wait is left pending. +PassThrough == + status = "error" => /\ wait # "pending" /\ effect = "disposed" + /\ \A c \in Consumers : live[c] = "error" +\* F5 and F6: ready means a usable snapshot is visible. +ReadyMeansRows == status = "ready" => rows +\* F10: a source that stopped trying has reported ready or a final error. +StoppedSourceReported == (status = "loading" /\ attempt > 0) => alive +\* F7: an error in the current attempt is that attempt's own final failure. +ErrorIsOwn == status = "error" => finalFailed +\* F11: consumers agree when no request-specific failure happened. +ConsumersAgree == ~reqFailed => live["root"] = live["lazy"] +\* F12: a request-specific failure leaves a healthy source healthy. +RequestFailureIsLocal == (reqFailed /\ ~finalFailed) => status # "error" +\* F3 and F9: only the app starts a new attempt. +OnlyAppRestarts == byApp +============================================================================= diff --git a/docs/contributing/designs/source-failure-and-health/SourceHealth.tla b/docs/contributing/designs/source-failure-and-health/SourceHealth.tla new file mode 100644 index 0000000000..d9ef5c2fb6 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/SourceHealth.tla @@ -0,0 +1,113 @@ +---------------------------- MODULE SourceHealth ---------------------------- +(***************************************************************************) +(* The source health channel. Status gates the first ready; health is *) +(* metadata that a source reports after it. A live query exposes an error *) +(* tree with one node per collection it depends on. The tree always has *) +(* the same shape, and each node mirrors its collection's reports. *) +(* *) +(* Topology: sources A and B. Live query L1 reads A. Live query Top reads *) +(* L1 and B, so A reaches Top only through L1. Properties H1 to H11 are *) +(* the frozen list in README.md. *) +(***************************************************************************) +EXTENDS Naturals + +CONSTANTS + HealthFlipsStatus, \* a post-ready failure changes the gate status (old Electric/PowerSync) + NestedDoesNotRelay, \* a nested live query does not pass its sources' health up + FrameworkClears, \* the framework clears an error without a source report + DropHealthyNodes, \* the tree lists only failing nodes + FailureClearsRows \* a post-ready failure discards visible rows + +Sources == {"A", "B"} +Nodes == {"A", "B", "L1"} \* the nodes in Top's health tree + +VARIABLES + status, \* gate per source: "loading" | "ready" | "error" + health, \* per source, after ready: "healthy" | "failed" + rows, \* per source: visible rows exist + tree, \* Top's health tree: node -> "ok" | "error"; DOMAIN is the shape + topStatus, \* Top's gate: "loading" | "ready" | "error" + topRows, \* Top's visible rows + reported \* per source: the source's latest report was a failure + +vars == <> + +\* What Top's tree must show, derived only from source state (H4, H11). +NodeState(n) == + CASE n = "L1" -> IF status["A"] = "error" \/ health["A"] = "failed" THEN "error" ELSE "ok" + [] OTHER -> IF status[n] = "error" \/ health[n] = "failed" THEN "error" ELSE "ok" + +Relay == + [n \in (IF DropHealthyNodes THEN {m \in Nodes : NodeState(m) = "error"} ELSE Nodes) |-> + IF NestedDoesNotRelay /\ n = "L1" THEN "ok" ELSE NodeState(n)] + +\* The gate is unchanged from the failure law: Top waits for both inputs. +TopGate == + IF \E s \in Sources : status[s] = "error" THEN "error" + ELSE IF \A s \in Sources : status[s] = "ready" THEN "ready" ELSE "loading" + +Propagate(keepGate) == + /\ tree' = Relay' + /\ topStatus' = IF keepGate /\ topStatus = "ready" THEN "ready" ELSE TopGate' + /\ topRows' = IF topStatus' = "ready" THEN rows'["A"] /\ rows'["B"] ELSE topRows + +Init == + /\ status = [s \in Sources |-> "loading"] + /\ health = [s \in Sources |-> "healthy"] + /\ rows = [s \in Sources |-> FALSE] + /\ reported = [s \in Sources |-> FALSE] + /\ tree = [n \in Nodes |-> "ok"] + /\ topStatus = "loading" /\ topRows = FALSE + +\* Before the first ready, the gate decides (H2). +Ready(s) == + /\ status[s] = "loading" + /\ status' = [status EXCEPT ![s] = "ready"] /\ rows' = [rows EXCEPT ![s] = TRUE] + /\ UNCHANGED <> + /\ Propagate(TRUE) + +InitialFailure(s) == + /\ status[s] = "loading" + /\ status' = [status EXCEPT ![s] = "error"] + /\ UNCHANGED <> + /\ Propagate(TRUE) + +\* After the first ready, a failure is metadata (H1). +ReportFailure(s) == + /\ status[s] = "ready" /\ health[s] = "healthy" + /\ health' = [health EXCEPT ![s] = "failed"] /\ reported' = [reported EXCEPT ![s] = TRUE] + /\ status' = IF HealthFlipsStatus THEN [status EXCEPT ![s] = "error"] ELSE status + /\ rows' = IF FailureClearsRows THEN [rows EXCEPT ![s] = FALSE] ELSE rows + /\ Propagate(~HealthFlipsStatus) + +\* Only the source clears its own failure, by reporting that sync works (H3). +ReportHealthy(s) == + /\ health[s] = "failed" + /\ health' = [health EXCEPT ![s] = "healthy"] /\ reported' = [reported EXCEPT ![s] = FALSE] + /\ UNCHANGED <> + /\ Propagate(TRUE) + +\* Weakening: the framework clears a failure on its own (H3). +FrameworkClear(s) == + /\ FrameworkClears /\ health[s] = "failed" + /\ health' = [health EXCEPT ![s] = "healthy"] + /\ UNCHANGED <> + /\ Propagate(TRUE) + +Next == \E s \in Sources : + Ready(s) \/ InitialFailure(s) \/ ReportFailure(s) \/ ReportHealthy(s) \/ FrameworkClear(s) + +Spec == Init /\ [][Next]_vars + +--------------------------------------------------------------------------- +\* H11: the tree always has the same shape. +StableShape == DOMAIN tree = Nodes +\* H4: every node mirrors its collection, including through the nested live query. +TreeMirrorsSources == \A n \in DOMAIN tree : tree[n] = NodeState(n) +\* H3: a failure shows until its source reports otherwise. +OnlySourceClears == \A s \in Sources : (health[s] = "healthy") => ~reported[s] +\* H1 and H5: after Top's first ready, health never moves its gate or its rows. +GateIgnoresHealth == [][topStatus = "ready" => (topStatus' = "ready" /\ topRows')]_vars +\* H2: the gate's error is explained by a node in the tree. +GateExplained == topStatus = "error" => \E n \in DOMAIN tree : tree[n] = "error" +============================================================================= diff --git a/docs/contributing/designs/source-failure-and-health/USER-DOCS-DRAFT.md b/docs/contributing/designs/source-failure-and-health/USER-DOCS-DRAFT.md new file mode 100644 index 0000000000..5a5561b008 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/USER-DOCS-DRAFT.md @@ -0,0 +1,258 @@ +# Draft: what users would see (candidate v2) + +> **Superseded (2026-10-16).** The error surface was redesigned: status +> `crashed`, node `failureReason`/`error`, `fail()`, `reload()`, and +> `collection.health`. Read `error-surface/README.md` and `error-surface/RULES.md`, +> the shipped `docs/guides/error-handling.md`, and +> `packages/db/src/query/live/ARCHITECTURE.md` laws 14, 16, and 17. + +This is a superseded proposal, not shipped behavior. It applies the repairs +from the hostile assay and the fracture scan to `HEALTH-CANDIDATE.md`. The +shipped API differs (for example, `reportError` has no `final` option, and +query-db's error utilities are removed); read `PLAN.md` and the shipped +guides. + +--- + +## Guide: health, readiness, and stale data + +A Collection answers two separate questions: + +| Question | Where to look | Changes when | +| --- | --- | --- | +| Can I show this data yet? | `status`, `isReady`, `isError` | Only during the initial load of a sync run | +| Is this data still being kept up to date? | `health` (the health tree) | Any time a source reports a problem or recovers | + +### The initial load + +Until a Collection has its first usable snapshot, `status` is `loading`. If the +source keeps retrying, it stays `loading`. If the sync adapter gives up, `status` +becomes `error`, `isError` is `true`, `preload()` rejects with the original +cause, and every live query that depends on it is in error too. + +An initial error is final. TanStack DB does not retry it or recover from it on +its own. To try again, restart the query: + +```ts +await todosQuery.cleanup() +await todosQuery.preload() +``` + +### After the first ready + +Once the data has loaded, it stays. If the source later loses its connection or +a refresh fails, `status` stays `ready` and your rows stay on screen, even as +they get older. The problem appears in `health` instead. + +> **Important:** `isError` only describes the initial load. To show a +> "data may be out of date" banner, check `health.hasErrors`. + +```tsx nocheck +function TaskList() { + const { data, isLoading, isError, health } = useLiveQuery((q) => + q + .from({ task: tasks }) + .join({ user: users }, ({ task, user }) => eq(task.assigneeId, user.id)), + ) + + if (isLoading) return + if (isError) return + + return ( + <> + {health.hasErrors && } + {data.map((row) => ( + + ))} + + ) +} +``` + +### The health tree + +Every live query result includes `health`. It always has the same shape for a +given query: one node for the live query, and one node for each source in the +query, in query order. Healthy nodes are present too, with `isError: false`. + +```ts +type HealthNode = { + alias: string // the query alias, or "root" + collectionId: string + kind: 'source' | 'live-query' | 'include' + status: CollectionStatus // the node's initial-load gate + isError: boolean // this node has a problem now + hasErrors: boolean // this node or any node below it + error: CollectionErrorInfo | null + instances?: { total: number; inError: number } // include nodes only + sources: Array +} + +type CollectionErrorInfo = { + phase: 'initial' | 'sync' | 'request' + final: boolean // the source has stopped trying; only a restart clears it + name: string + message: string + code?: string + at: number // epoch milliseconds + cause: unknown // the original value; omitted from JSON +} +``` + +For the query above, while the users sync is disconnected: + +```json +{ + "alias": "root", + "collectionId": "live-query-1", + "kind": "live-query", + "status": "ready", + "isError": false, + "hasErrors": true, + "error": null, + "sources": [ + { + "alias": "task", + "collectionId": "tasks", + "kind": "source", + "status": "ready", + "isError": false, + "hasErrors": false, + "error": null, + "sources": [] + }, + { + "alias": "user", + "collectionId": "users", + "kind": "source", + "status": "ready", + "isError": true, + "hasErrors": true, + "error": { + "phase": "sync", + "final": false, + "name": "FetchError", + "message": "Network request failed", + "at": 1760342400000 + }, + "sources": [] + } + ] +} +``` + +Phases: + +- `initial`: the source gave up before Collection readiness. `status` is `error`. +- `sync`: the source reported a problem after ready. `final: false` means it + is still trying; `final: true` means it stopped, and only a restart clears it. +- `request`: loading data for this query failed while its source stayed + healthy. It clears when that data loads, or when the query stops needing it. + +A collection used twice in one query, for example a self-join, appears once per +alias. An include has one node for all of its per-row child collections, with +`instances` counting how many are in error. + +`health` is plain data apart from `cause`, so you can log or send it: + +```ts +useEffect(() => { + if (health.hasErrors) reportToMonitoring(JSON.parse(JSON.stringify(health))) +}, [health]) +``` + +Server-side rendering does not transfer `health`. The client builds its own +tree from its own sync. + +### Effects + +An Effect is disposed by an initial-load error, as today. After its first +ready, a source problem does not dispose it. Subscribe to health instead: + +```ts +createEffect({ + query: (q) => q.from({ order: orders }), + onBatch: (events) => { + /* ... */ + }, + onHealthChange: (health) => { + if (health.hasErrors) pauseAutomations() + else resumeAutomations() + }, +}) +``` + +--- + +## Guide for adapter authors + +Your sync function receives two new callbacks: + +```ts +sync: ({ begin, write, commit, markReady, markError, reportError, reportHealthy }) => { + let readyOnce = false + const stream = connect({ + onData: (rows) => { + begin() + rows.forEach((row) => write({ type: 'upsert', value: row })) + commit() + if (!readyOnce) { + markReady() + readyOnce = true + } else { + reportHealthy() // a successful sync after a problem + } + }, + onError: (error, { willRetry }) => { + if (!readyOnce) { + // Keep trying silently. Report only when you give up. + if (!willRetry) markError(error) + } else { + // The data is already showing. Report the problem; keep the rows. + reportError(error, { final: !willRetry }) + } + }, + }) + return () => stream.close() +} +``` + +The rules: + +1. While you are still retrying the initial load, call nothing. The + Collection stays `loading`. +2. Call `markError(error)` only when you give up before Collection readiness. Do + not call `markReady()` after that; restart is the app's job. +3. After the first ready, call `reportError(error, { final })` for problems + and `reportHealthy()` when sync works again. Never move a ready Collection + to `error`. +4. Do not discard rows because sync failed. + +`markError(error)` after Collection readiness still works during migration. It is +treated as `reportError(error, { final: true })` and logs a development +warning. + +### Migration notes for the built-in adapters + +- **Electric:** a stream error that `onError` retries is not reported. When the + stream gives up after Collection readiness, it reports `final: true` instead of + moving the Collection to `error`. +- **PowerSync:** a failed tracking rebuild reports `reportError`; a successful + rebuild reports `reportHealthy()` instead of `markReady()`. +- **query-db:** a failed refetch after ready reports `reportError`; + `utils.lastError`, `utils.isError`, and `utils.errorCount` keep working. A + successful refetch no longer revives a Collection whose initial load failed; + call `cleanup()` and `preload()` instead. +- **TrailBase, RxDB, persistence:** post-ready failures move to + `reportError`. Persistence keeps local storage failures on `persistedError`. + +--- + +## Open questions this draft does not settle + +- Ordering: `health:change` fires in the same tick, after `status:change` + when both change. This is proposed, not checked. +- Follower tabs: how a follower learns that the leader's upstream failed. +- Pooled live queries that share sources. +- Whether `health` should be a new object on every change or keep stable + node identities for memoization. diff --git a/docs/contributing/designs/source-failure-and-health/error-surface/ErrorSurface4.tla b/docs/contributing/designs/source-failure-and-health/error-surface/ErrorSurface4.tla new file mode 100644 index 0000000000..a43cdf6a66 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/error-surface/ErrorSurface4.tla @@ -0,0 +1,399 @@ +---------------------------- MODULE ErrorSurface4 --------------------------- +(***************************************************************************) +(* Frozen grammar v4 (frozen-grammar.md) as a transition system. *) +(* *) +(* One live query over two sources: *) +(* P a persisted, eager source (restores rows from disk, syncs remote) *) +(* D an on-demand, remote-only source (may load after the query ready) *) +(* *) +(* Hidden state says what really happened; the Node*/qStatus projections *) +(* are what the app sees. RemedyFromObs reads only those; Oracle reads *) +(* hidden state. Weak holds negative-control weakenings. *) +(***************************************************************************) +EXTENDS Naturals, FiniteSets + +CONSTANTS Weak + +P == "P" +D == "D" +Sources == {P, D} + +VARIABLES + gate, \* R1: "loading" | "ready" | "error" | "crashed" | "cleaned" + problem, \* R4/R5 after-ready node problem: "none" | "failure" | "error" + rows, \* the source has readable rows + known, \* the source's rows are known to be its data + reloading, \* R7: a reload's new run is in flight + stale, \* R7: a superseded run may still deliver + fromStale, \* hidden: rows came from a superseded run + restore, \* R8 (P): "pending" | "synced" | "never" | "failed" + remoteUp, \* P's remote has synced in this run + remoteFail, \* hidden: P's remote is failing and nobody has reported it + remoteFailedEarly, \* R8: P's remote failed before the restore finished + qStatus, \* R3: "loading" | "ready" | "error" | "crashed" | "cleaned-up" + shown, \* the query has shown rows in this run + req, \* R4 request: "none" | "pending" | "failed" + missing, \* hidden: requested data has not arrived + mounted, \* the query subscribes to both sources + errReloaded \* hidden: a reload committed while the query was in 'error' + +vars == <> + +W(x) == x \in Weak + +\* R3 and R9. A query's 'error' clears once every source is ready again, +\* which needs a reload's commit (ErrorClearsOnlyByReload); 'crashed' stays. +NewQ(q, g) == + IF q \in {"crashed", "cleaned-up"} THEN q + ELSE IF \E s \in Sources : g[s] \in {"crashed", "cleaned"} THEN "crashed" + ELSE IF q = "ready" THEN "ready" + \* F1 decision (b): once no source has a failure, the query leaves 'error' for + \* 'loading' or 'ready'. A source still reloading keeps its 'error' gate (R7). + ELSE IF q = "error" + THEN IF ~W("QueryErrorSticks") /\ \A s \in Sources : g[s] # "error" /\ problem'[s] # "error" + THEN (IF g[P] = "ready" /\ g[D] \in {"ready", "loading"} THEN "ready" ELSE "loading") + ELSE "error" + ELSE IF \E s \in Sources : g[s] = "error" THEN "error" + ELSE IF g[P] = "ready" /\ g[D] \in {"ready", "loading"} THEN "ready" + ELSE "loading" + +SettleR(g, rl, r) == + /\ gate' = g + /\ reloading' = rl + /\ qStatus' = IF mounted THEN NewQ(qStatus, g) ELSE qStatus + /\ shown' = (shown \/ qStatus' = "ready") + /\ req' = IF qStatus' = "ready" THEN r ELSE "none" + /\ missing' = IF qStatus' = "ready" THEN missing ELSE FALSE + /\ errReloaded' = ((qStatus = "error") /\ + (errReloaded \/ \E s \in Sources : reloading[s] /\ ~rl[s] /\ g[s] = "ready")) + +Settle(g, rl) == SettleR(g, rl, req) + +Init == + /\ gate = [s \in Sources |-> "loading"] + /\ problem = [s \in Sources |-> "none"] + /\ rows = [s \in Sources |-> FALSE] + /\ known = [s \in Sources |-> FALSE] + /\ reloading = [s \in Sources |-> FALSE] + /\ stale = [s \in Sources |-> FALSE] + /\ fromStale = FALSE + /\ restore = "pending" + /\ remoteUp = FALSE + /\ remoteFail = FALSE + /\ remoteFailedEarly = FALSE + /\ qStatus = "loading" + /\ shown = FALSE + /\ req = "none" + /\ missing = FALSE + /\ mounted = TRUE + /\ errReloaded = FALSE + +QUnchanged == UNCHANGED <> + +----------------------------------------------------------------------------- +(* Remote sync. *) + +\* R1 first ready, R7 a reload's first commit, R8 P's first remote sync. +RemoteReady(s) == + /\ \/ gate[s] = "loading" + \/ reloading[s] + \/ s = P /\ gate[P] = "ready" /\ ~remoteUp /\ problem[P] # "error" + /\ problem' = [problem EXCEPT ![s] = IF reloading[s] \/ (s = P /\ ~remoteUp) + THEN "none" ELSE problem[s]] + /\ rows' = [rows EXCEPT ![s] = TRUE] + /\ known' = [known EXCEPT ![s] = TRUE] + /\ remoteUp' = IF s = P THEN TRUE ELSE remoteUp + /\ remoteFail' = IF s = P THEN FALSE ELSE remoteFail + /\ Settle([gate EXCEPT ![s] = "ready"], [reloading EXCEPT ![s] = FALSE]) + /\ UNCHANGED <> + +\* R5 fail(e) before the current run's first commit. +RemoteFailBefore(s) == + /\ \/ gate[s] = "loading" /\ ~(s = P /\ remoteFailedEarly) + \/ reloading[s] + \/ s = P /\ gate[P] = "ready" /\ ~remoteUp /\ problem[P] # "error" + /\ CASE reloading[s] -> + \* R7: nothing visible changes except the node's new error. + /\ problem' = [problem EXCEPT ![s] = IF gate[s] = "ready" THEN "error" ELSE problem[s]] + /\ remoteFailedEarly' = remoteFailedEarly + /\ Settle(IF W("ReloadFailIsLoadingError") /\ gate[s] = "ready" + THEN [gate EXCEPT ![s] = "error"] ELSE gate, + [reloading EXCEPT ![s] = FALSE]) + [] gate[s] = "ready" -> + \* R8: P's gate opened from disk; the remote stopped before syncing. + /\ problem' = [problem EXCEPT ![s] = "error"] + /\ remoteFailedEarly' = remoteFailedEarly + /\ Settle(gate, reloading) + [] s = P /\ restore = "pending" -> + \* R8: wait for the restore result. + /\ remoteFailedEarly' = TRUE + /\ UNCHANGED problem + /\ Settle(gate, reloading) + [] OTHER -> + /\ remoteFailedEarly' = remoteFailedEarly + /\ UNCHANGED problem + /\ Settle([gate EXCEPT ![s] = "error"], reloading) + /\ remoteFail' = IF s = P THEN FALSE ELSE remoteFail + /\ UNCHANGED <> + +\* R8: P's remote has a problem while its gate is open from disk. +RemoteProblemFromDisk == + /\ gate[P] = "ready" /\ ~remoteUp /\ ~reloading[P] /\ problem[P] = "none" /\ ~remoteFail + /\ IF W("SilentFromDisk") + THEN remoteFail' = TRUE /\ UNCHANGED problem + ELSE problem' = [problem EXCEPT ![P] = "failure"] /\ UNCHANGED remoteFail + /\ UNCHANGED <> + /\ QUnchanged + +\* R5 reportError / reportHealthy / fail after the current run's first commit. +ReportError(s) == + /\ gate[s] = "ready" /\ ~reloading[s] /\ problem[s] = "none" + /\ problem' = [problem EXCEPT ![s] = "failure"] + /\ UNCHANGED <> + /\ QUnchanged + +ReportHealthy(s) == + /\ gate[s] = "ready" /\ ~reloading[s] + /\ problem[s] = "failure" \/ (W("HealthyClearsError") /\ problem[s] = "error") + /\ problem' = [problem EXCEPT ![s] = "none"] + /\ UNCHANGED <> + /\ QUnchanged + +FailAfter(s) == + /\ gate[s] = "ready" /\ ~reloading[s] /\ problem[s] # "error" + /\ (s = P => remoteUp) + /\ problem' = [problem EXCEPT ![s] = "error"] + /\ UNCHANGED <> + /\ QUnchanged + +\* R7: a superseded run delivers late. +StaleCommit(s) == + /\ stale[s] + /\ stale' = [stale EXCEPT ![s] = FALSE] + /\ IF W("NoRunFence") + THEN fromStale' = TRUE /\ rows' = [rows EXCEPT ![s] = TRUE] + ELSE UNCHANGED <> + /\ UNCHANGED <> + /\ QUnchanged + +----------------------------------------------------------------------------- +(* Local persistence of P. *) + +RestoreDone(r) == + /\ restore = "pending" /\ gate[P] \in {"loading", "ready", "error"} + /\ restore' = r + /\ LET opens == gate[P] = "loading" /\ (r = "synced" \/ W("EmptyRestoreOpens")) + g == IF opens THEN [gate EXCEPT ![P] = "ready"] + ELSE IF remoteFailedEarly /\ gate[P] = "loading" + THEN [gate EXCEPT ![P] = "error"] ELSE gate + IN /\ rows' = [rows EXCEPT ![P] = rows[P] \/ opens] + /\ known' = [known EXCEPT ![P] = known[P] \/ r = "synced"] + /\ problem' = IF opens /\ remoteFailedEarly THEN [problem EXCEPT ![P] = "error"] ELSE problem + /\ Settle(g, reloading) + /\ remoteFailedEarly' = FALSE + /\ UNCHANGED <> + +\* R2/R8 a failed restore, or storage breaking later, is a crash. +StorageBreaks == + /\ gate[P] \in {"loading", "ready", "error"} + /\ restore' = IF restore = "pending" THEN "failed" ELSE restore + /\ rows' = [rows EXCEPT ![P] = FALSE] + /\ problem' = [problem EXCEPT ![P] = "none"] + /\ remoteFail' = FALSE + /\ Settle([gate EXCEPT ![P] = "crashed"], [reloading EXCEPT ![P] = FALSE]) + /\ UNCHANGED <> + +----------------------------------------------------------------------------- +(* App actions. *) + +\* R7. A reload in flight returns its promise (no transition). Crashed throws. +CanReload(s) == + /\ ~reloading[s] + /\ gate[s] \in {"loading", "ready", "error"} \/ (W("ReloadCrashed") /\ gate[s] = "crashed") + +StartReload(s, rl, r) == + IF gate[s] = "loading" + THEN \* a new run replaces the loading one + /\ stale' = [stale EXCEPT ![s] = TRUE] + /\ restore' = IF s = P THEN "pending" ELSE restore + /\ remoteUp' = IF s = P THEN FALSE ELSE remoteUp + /\ remoteFail' = IF s = P THEN FALSE ELSE remoteFail + /\ remoteFailedEarly' = IF s = P THEN FALSE ELSE remoteFailedEarly + /\ rows' = rows + /\ SettleR(gate, rl, r) + ELSE IF gate[s] = "crashed" + THEN \* only under Weak "ReloadCrashed" + /\ stale' = stale + /\ restore' = IF s = P THEN "pending" ELSE restore + /\ remoteUp' = IF s = P THEN FALSE ELSE remoteUp + /\ remoteFail' = IF s = P THEN FALSE ELSE remoteFail + /\ remoteFailedEarly' = IF s = P THEN FALSE ELSE remoteFailedEarly + /\ rows' = rows + /\ SettleR([gate EXCEPT ![s] = "loading"], rl, r) + ELSE \* ready or error: keep everything visible; the node shows reloading + /\ stale' = stale + /\ restore' = restore + /\ remoteUp' = remoteUp + /\ remoteFail' = IF s = P THEN FALSE ELSE remoteFail + /\ remoteFailedEarly' = remoteFailedEarly + /\ rows' = IF W("ReloadBlanks") THEN [rows EXCEPT ![s] = FALSE] ELSE rows + /\ IF W("ReloadShowsLoading") /\ gate[s] = "error" + THEN SettleR([gate EXCEPT ![s] = "loading"], [rl EXCEPT ![s] = FALSE], r) + ELSE SettleR(gate, [rl EXCEPT ![s] = TRUE], r) + +Reload(s) == + /\ CanReload(s) + /\ UNCHANGED <> + \* R7: a failed request is re-issued by the new run. + /\ StartReload(s, reloading, IF req = "failed" THEN "pending" ELSE req) + +\* R9: reload a live query in 'error': reload each failed source. +QueryReload == + /\ mounted /\ qStatus = "error" + /\ LET failed == {s \in Sources : (gate[s] = "error" \/ problem[s] = "error") /\ ~reloading[s]} + IN /\ failed # {} + /\ reloading' = [s \in Sources |-> reloading[s] \/ s \in failed] + /\ UNCHANGED <> + +\* cleanup() throws while the query subscribes; a cascade cleans the query first. +CleanupSource(s) == + /\ ~mounted \/ W("CleanupWithSubscribers") + /\ gate[s] # "cleaned" + /\ rows' = [rows EXCEPT ![s] = FALSE] + /\ problem' = [problem EXCEPT ![s] = "none"] + /\ known' = [known EXCEPT ![s] = FALSE] + /\ Settle([gate EXCEPT ![s] = "cleaned"], [reloading EXCEPT ![s] = FALSE]) + /\ UNCHANGED <> + +CleanupQuery == + /\ mounted + /\ mounted' = FALSE + /\ qStatus' = "cleaned-up" + /\ errReloaded' = FALSE + /\ req' = "none" /\ missing' = FALSE + /\ UNCHANGED <> + +\* A cleaned source preloads again: the recreated instance after a crash. +PreloadSource(s) == + /\ gate[s] = "cleaned" + /\ restore' = IF s = P THEN "pending" ELSE restore + /\ remoteUp' = IF s = P THEN FALSE ELSE remoteUp + /\ remoteFail' = IF s = P THEN FALSE ELSE remoteFail + /\ remoteFailedEarly' = IF s = P THEN FALSE ELSE remoteFailedEarly + /\ UNCHANGED <> + /\ Settle([gate EXCEPT ![s] = "loading"], reloading) + +MountQuery == + /\ ~mounted + /\ \A s \in Sources : gate[s] # "cleaned" + /\ UNCHANGED problem + /\ mounted' = TRUE + /\ qStatus' = NewQ("loading", gate) + /\ errReloaded' = FALSE + /\ shown' = (qStatus' = "ready") + /\ req' = "none" /\ missing' = FALSE + /\ UNCHANGED <> + +ReqVars == <> +RequestStart == qStatus = "ready" /\ req = "none" /\ req' = "pending" /\ missing' = TRUE /\ UNCHANGED ReqVars +RequestFail == req = "pending" /\ req' = "failed" /\ UNCHANGED <> /\ UNCHANGED ReqVars +RequestLoad == req = "pending" /\ req' = "none" /\ missing' = FALSE /\ UNCHANGED ReqVars +RequestRetry == req = "failed" /\ req' = "pending" /\ UNCHANGED <> /\ UNCHANGED ReqVars +RequestRetire == req # "none" /\ req' = "none" /\ missing' = FALSE /\ UNCHANGED ReqVars + +Next == + \/ \E s \in Sources : + RemoteReady(s) \/ RemoteFailBefore(s) \/ ReportError(s) \/ ReportHealthy(s) + \/ FailAfter(s) \/ StaleCommit(s) \/ Reload(s) \/ CleanupSource(s) \/ PreloadSource(s) + \/ \E r \in {"synced", "never"} : RestoreDone(r) + \/ RemoteProblemFromDisk \/ StorageBreaks + \/ QueryReload \/ CleanupQuery \/ MountQuery + \/ RequestStart \/ RequestFail \/ RequestLoad \/ RequestRetry \/ RequestRetire + +Spec == Init /\ [][Next]_vars + +----------------------------------------------------------------------------- +(* What the app observes. *) + +NodeFailureReason(s) == problem[s] = "failure" +NodeError(s) == + IF W("OneErrorField") THEN problem[s] # "none" \/ gate[s] = "error" + ELSE problem[s] = "error" \/ gate[s] = "error" +NodeReloading(s) == ~W("ReloadHidden") /\ reloading[s] + +RemedyFromObs == + CASE ~mounted -> {} + [] qStatus = "crashed" -> {"recreate"} + [] qStatus = "error" -> + IF \E s \in Sources : NodeError(s) /\ ~NodeReloading(s) THEN {"reload"} ELSE {"wait"} + [] qStatus = "loading" -> {} + [] OTHER -> + {r \in {"wait", "reload"} : + \E s \in Sources : + \/ r = "wait" /\ (NodeReloading(s) + \/ (NodeFailureReason(s) /\ ~NodeError(s))) + \/ r = "reload" /\ NodeError(s) /\ ~NodeReloading(s)} + \cup (IF req = "failed" THEN {"retry-action"} ELSE {}) + +Oracle == + CASE ~mounted -> {} + [] qStatus = "crashed" -> {"recreate"} + [] qStatus = "error" -> + IF \E s \in Sources : (gate[s] = "error" \/ problem[s] = "error") /\ ~reloading[s] + THEN {"reload"} ELSE {"wait"} + [] qStatus = "loading" -> {} + [] OTHER -> + {r \in {"wait", "reload"} : + \E s \in Sources : + \/ r = "wait" /\ (reloading[s] \/ problem[s] = "failure" + \/ (s = P /\ remoteFail)) + \/ r = "reload" /\ ~reloading[s] /\ (problem[s] = "error" \/ gate[s] = "error")} + \cup (IF req = "failed" THEN {"retry-action"} ELSE {}) + +----------------------------------------------------------------------------- +(* Invariants. *) + +I1_LoadingErrorNeverAfterData == (mounted /\ qStatus = "error") => ~shown +I2_AfterReadyOnlyCrash == (mounted /\ shown) => qStatus \in {"ready", "crashed"} +I3_ReadyShowsRows == (mounted /\ qStatus = "ready") => rows[P] +I4_RemedyDecodable == RemedyFromObs = Oracle +I6_NoCleanupUnderQuery == mounted => \A s \in Sources : gate[s] # "cleaned" +I7_NoStaleRows == ~fromStale +I8_ReadyMeansKnown == gate[P] = "ready" => known[P] +NoSilentMissing == (qStatus = "ready" /\ missing) => req \in {"pending", "failed"} +LocalRowsNotHidden == restore = "synced" => gate[P] # "error" + +ErrorClearsOnlyByReload == + [][\A s \in Sources : + ((problem[s] = "error" /\ problem'[s] = "none" /\ gate'[s] = "ready") + => reloading[s]) + /\ ((gate[s] = "error" /\ gate'[s] = "ready") => reloading[s])]_vars +QueryErrorClearsOnlyByReload == + [][(qStatus = "error" /\ qStatus' = "ready") => + (errReloaded \/ \E s \in Sources : reloading[s])]_vars +\* A query in 'error' can always get out: some source is failed (reload it) or reloading. +QueryErrorHasAWayOut == + (mounted /\ qStatus = "error") => + \E s \in Sources : reloading[s] \/ gate[s] \in {"error", "loading"} \/ problem[s] = "error" +\* Finding F1 (run 4): a query can sit in 'error' with no failed source while another +\* source is still loading its first ready. Recorded by this invariant: +QueryErrorNamesAFailure == + (mounted /\ qStatus = "error") => + \E s \in Sources : reloading[s] \/ gate[s] = "error" \/ problem[s] = "error" +\* Nothing recovers a crash in place: only cleanup leaves 'crashed', and a later +\* PreloadSource starts from 'cleaned' as a fresh Collection would. +CrashPermanentForInstance == + [][\A s \in Sources : gate[s] = "crashed" => gate'[s] \in {"crashed", "cleaned"}]_vars +ReloadNeverShowsLoading == + [][\A s \in Sources : (gate[s] = "error" /\ gate'[s] = "loading") => FALSE]_vars +============================================================================= diff --git a/docs/contributing/designs/source-failure-and-health/error-surface/README.md b/docs/contributing/designs/source-failure-and-health/error-surface/README.md new file mode 100644 index 0000000000..9c3d6aadb9 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/error-surface/README.md @@ -0,0 +1,37 @@ +# Error surface (2026-10-16) + +This folder supersedes the health-channel surface in `../HEALTH-CANDIDATE.md` and the +`phase` / `final` health tree. The maintainer chose it after a Field Lab study of how other +libraries separate loading errors from errors after data is shown. + +- `RULES.md`: the frozen rules (grammar v4) and invariants. This is the contract the + implementation follows. +- `ErrorSurface4.tla`, `cfg/`, `tlc-results.tsv`: the TLA+ model of those rules, its safe + configuration, and twelve weakened controls, each of which fails at its target. + Run from the field-lab skill root: `scripts/tlc -deadlock -config cfg/safe.cfg ErrorSurface4`. +- `readout.md`: what the model checks and what it does not. +- `user-docs-draft.md`: the developer-facing guide written against the rules before + implementation. + +Decisions, in short: + +1. `status` answers "can the data be shown": `idle | loading | ready | error | crashed | + cleaned-up`, one flag per value (`isLoadingError`, `isCrashed`, ...). After a query has + shown data, only a crash changes its status. `isError` is deprecated. +2. `health` answers "is the data still kept current". Each node carries `failureReason` + (the adapter still tries) or `error` (it stopped), like TanStack Query. `isSyncError` is + `status === 'ready'` and any problem in the tree. Every Collection has `health` and + `health:change`; the `errors` names are gone. +3. Adapter calls: `markReady()`, `reportError(e)`, `fail(e)`, `reportHealthy()`. A call that + does not fit the state machine throws; `reportHealthy()` with nothing wrong is a no-op. +4. `reload()` starts a new sync run like a sync-side truncate: nothing visible changes until + it commits or fails. It returns the in-flight promise when called again, throws on + `crashed`, and works during `loading` and after `error`. A live query's `reload()` + reloads its failed sources. +5. `cleanup()` throws while the Collection has subscribers; `subscribers` lists them and + `cleanup({ dependents: true })` cleans them up first. A crash is not recovered in + place; recovery is a cascade cleanup and a fresh start. +6. Persisted Collections join the same model: a synced disk restore opens the gate, a + failed remote waits for the restore, remote problems after that are reported, and a + failed restore is a crash. `persistedStatus`, `isPersistedReady`, and `persistedError` + are removed. diff --git a/docs/contributing/designs/source-failure-and-health/error-surface/RULES.md b/docs/contributing/designs/source-failure-and-health/error-surface/RULES.md new file mode 100644 index 0000000000..f81c8bf86a --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/error-surface/RULES.md @@ -0,0 +1,83 @@ +# Frozen grammar v4: app-facing error model for a reactive local-first data library + +A *Collection* holds rows kept in sync with a backend by a *sync adapter*. A *live query* +reads one or more Collections ("sources", one per query alias) and is itself a Collection. +Apps read a live query through framework hooks. Some Collections are *persisted*: they +restore rows from local disk storage and also sync with a remote backend. + +## Surface +- `status`: 'idle' | 'loading' | 'ready' | 'error' | 'crashed' | 'cleaned-up' +- One boolean per status: isIdle, isLoading, isReady, isLoadingError (status 'error'), + isCrashed, isCleanedUp. `isError` is deprecated. +- `isSyncError`: status is 'ready' and the tree holds any `failureReason` or `error`. +- `health`: a tree, one node per query alias plus a root node for the query. Each node: + { alias, kind: 'source'|'live-query'|'include', status, hasErrors, error | null, sources }. + A source node carries at most one of: + - `failureReason`: the latest problem while the adapter keeps trying (like TanStack Query's + `failureReason` during retries); + - `error`: the adapter stopped (`fail`), or the gate failed. + Each value is { name, message, code?, at, cause }. A node also shows `reloading` while a + reload's new run is in flight. The query's own node carries a request `error`. +- `collection.health`: the Collection's current after-ready error, or null. +- `collection.reload()`: start a new sync run while keeping the current rows (see R7). +- `collection.cleanup()`: throws while the Collection has subscribers. The app can list a + Collection's subscribers (live queries, Effects, raw subscriptions) and ask cleanup to clean + up those dependents first. + +## Rules +R1 Gate. Each sync run: loading -> ready | error | crashed. An adapter that keeps retrying + before its first ready reports nothing (status stays loading). 'error' (gave up before the + first ready) is final for the run. +R2 Crash. 'crashed' = the Collection can no longer serve reads or accept writes: local + storage broken (closed IndexedDB, a failed local write or restore) or an internal invariant + failure. Final for the run; ends every live query and Effect over it. The cause may or may + not persist into a new run. +R3 Query gate. Until a live query is ready, it takes its sources' worst gate + (crashed > error > loading > ready). After it is ready, only 'crashed' propagates. +R4 After ready, a failure never changes status or removes rows. It appears on the node that + owns it: + - source node, sync problem: `failureReason` (adapter still trying; clears itself) or + `error` (adapter stopped; rows frozen until reload); + - a source whose new sync run fails before its first ready while the query is ready + (an on-demand source loading late, or a source being reloaded): node `error`; the query + stays ready; + - query's own node, request error: a request for more data (next page, newly joined rows) + failed; clears when that data loads or is no longer needed. +R5 Adapter verbs: markReady(); reportError(e) = something went wrong, still running + (sets `failureReason`); fail(e) = stopped (before ready -> status 'error'; after -> node + `error`); reportHealthy() clears `failureReason`. A node `error` clears only when a + reload's new run commits. +R6 Tree identity is stable while content is unchanged; every field except `cause` is JSON. +R7 Reload. reload() starts a new sync run like a sync-side truncate. Nothing visible changes + until the new run's first commit or failure: rows, status ('ready' or 'error'), and the + current error stay, and the node shows `reloading`. The first commit replaces all rows + atomically and clears the node's problem (an 'error' status becomes 'ready'). If the new + run fails first, the old rows and status stay and the node shows the new `error`. + reload() while a reload is in flight returns that reload's promise. reload() on a + 'crashed' Collection throws: a crash is not recovered in place; recovery is a cascade + cleanup, which discards all state, and a fresh start (of the same object or a new one). reload() during 'loading' starts a new run; commits from the old run are + ignored. A failed request is re-issued by the new run. +R9 Live-query reload. reload() on a live query in 'error' reloads each of its sources in + 'error' or with a node `error`. Its status stays 'error' while any source still has a + failure, including a source whose reload is in flight (it keeps its 'error' gate). Once no + source has a failure, the query moves to 'loading', or straight to 'ready'. Adapters retry inside their + first load (for example query-db's retry or the queryFn) before calling fail(). +R8 Persisted Collections. A completed local restore of previously synced rows opens the gate + (status 'ready') even if the remote has not connected; remote problems after that are + reported (R4/R5), including before the remote's first sync. A remote that fails before the + restore finishes waits for the restore: synced rows -> 'ready' with node `error`; none -> + 'error'. A failed restore is a crash. An empty restore of a Collection that never synced + does not open the gate. + +## Invariants +I1 isLoadingError is never true after the query has shown data in its run. +I2 After a query is ready, its status is 'ready' or 'crashed' until it is recreated. +I3 Status 'ready' implies rows are shown; no after-ready failure or reload blanks rows. +I4 From what the app can observe, it can pick one remedy: wait, retry the action, reload, + report broken storage. +I5 No silent recovery or silent degradation: 'error' becomes 'ready' only through a reload's + commit; 'crashed' never becomes 'ready' without a cleanup in between; a failing remote is never + shown as healthy; a reload never shows 'loading' in place of 'error'. +I6 'crashed' always means broken local storage or an invariant failure. +I7 Rows shown never come from a superseded run. +I8 'ready' never means "no rows" when the truth is "unknown". diff --git a/docs/contributing/designs/source-failure-and-health/error-surface/cfg/f1.cfg b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/f1.cfg new file mode 100644 index 0000000000..b45e2b7b36 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/f1.cfg @@ -0,0 +1,5 @@ +CONSTANTS + Weak = {} +SPECIFICATION Spec +INVARIANTS I1_LoadingErrorNeverAfterData I2_AfterReadyOnlyCrash I3_ReadyShowsRows I4_RemedyDecodable I6_NoCleanupUnderQuery I7_NoStaleRows I8_ReadyMeansKnown NoSilentMissing LocalRowsNotHidden QueryErrorHasAWayOut QueryErrorNamesAFailure +PROPERTIES ErrorClearsOnlyByReload QueryErrorClearsOnlyByReload CrashPermanentForInstance ReloadNeverShowsLoading diff --git a/docs/contributing/designs/source-failure-and-health/error-surface/cfg/safe.cfg b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/safe.cfg new file mode 100644 index 0000000000..944c641641 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/safe.cfg @@ -0,0 +1,5 @@ +CONSTANTS + Weak = {} +SPECIFICATION Spec +INVARIANTS I1_LoadingErrorNeverAfterData I2_AfterReadyOnlyCrash I3_ReadyShowsRows I4_RemedyDecodable I6_NoCleanupUnderQuery I7_NoStaleRows I8_ReadyMeansKnown NoSilentMissing LocalRowsNotHidden QueryErrorHasAWayOut +PROPERTIES ErrorClearsOnlyByReload QueryErrorClearsOnlyByReload CrashPermanentForInstance ReloadNeverShowsLoading diff --git a/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_CleanupWithSubscribers.cfg b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_CleanupWithSubscribers.cfg new file mode 100644 index 0000000000..7cd562494a --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_CleanupWithSubscribers.cfg @@ -0,0 +1,5 @@ +CONSTANTS + Weak = {"CleanupWithSubscribers"} +SPECIFICATION Spec +INVARIANTS I1_LoadingErrorNeverAfterData I2_AfterReadyOnlyCrash I3_ReadyShowsRows I4_RemedyDecodable I6_NoCleanupUnderQuery I7_NoStaleRows I8_ReadyMeansKnown NoSilentMissing LocalRowsNotHidden QueryErrorHasAWayOut +PROPERTIES ErrorClearsOnlyByReload QueryErrorClearsOnlyByReload CrashPermanentForInstance ReloadNeverShowsLoading diff --git a/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_EmptyRestoreOpens.cfg b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_EmptyRestoreOpens.cfg new file mode 100644 index 0000000000..a1ea5f4cac --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_EmptyRestoreOpens.cfg @@ -0,0 +1,5 @@ +CONSTANTS + Weak = {"EmptyRestoreOpens"} +SPECIFICATION Spec +INVARIANTS I1_LoadingErrorNeverAfterData I2_AfterReadyOnlyCrash I3_ReadyShowsRows I4_RemedyDecodable I6_NoCleanupUnderQuery I7_NoStaleRows I8_ReadyMeansKnown NoSilentMissing LocalRowsNotHidden QueryErrorHasAWayOut +PROPERTIES ErrorClearsOnlyByReload QueryErrorClearsOnlyByReload CrashPermanentForInstance ReloadNeverShowsLoading diff --git a/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_HealthyClearsError.cfg b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_HealthyClearsError.cfg new file mode 100644 index 0000000000..c752e24d53 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_HealthyClearsError.cfg @@ -0,0 +1,5 @@ +CONSTANTS + Weak = {"HealthyClearsError"} +SPECIFICATION Spec +INVARIANTS I1_LoadingErrorNeverAfterData I2_AfterReadyOnlyCrash I3_ReadyShowsRows I4_RemedyDecodable I6_NoCleanupUnderQuery I7_NoStaleRows I8_ReadyMeansKnown NoSilentMissing LocalRowsNotHidden QueryErrorHasAWayOut +PROPERTIES ErrorClearsOnlyByReload QueryErrorClearsOnlyByReload CrashPermanentForInstance ReloadNeverShowsLoading diff --git a/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_NoRunFence.cfg b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_NoRunFence.cfg new file mode 100644 index 0000000000..0d29d03e39 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_NoRunFence.cfg @@ -0,0 +1,5 @@ +CONSTANTS + Weak = {"NoRunFence"} +SPECIFICATION Spec +INVARIANTS I1_LoadingErrorNeverAfterData I2_AfterReadyOnlyCrash I3_ReadyShowsRows I4_RemedyDecodable I6_NoCleanupUnderQuery I7_NoStaleRows I8_ReadyMeansKnown NoSilentMissing LocalRowsNotHidden QueryErrorHasAWayOut +PROPERTIES ErrorClearsOnlyByReload QueryErrorClearsOnlyByReload CrashPermanentForInstance ReloadNeverShowsLoading diff --git a/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_OneErrorField.cfg b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_OneErrorField.cfg new file mode 100644 index 0000000000..299cee00a4 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_OneErrorField.cfg @@ -0,0 +1,5 @@ +CONSTANTS + Weak = {"OneErrorField"} +SPECIFICATION Spec +INVARIANTS I1_LoadingErrorNeverAfterData I2_AfterReadyOnlyCrash I3_ReadyShowsRows I4_RemedyDecodable I6_NoCleanupUnderQuery I7_NoStaleRows I8_ReadyMeansKnown NoSilentMissing LocalRowsNotHidden QueryErrorHasAWayOut +PROPERTIES ErrorClearsOnlyByReload QueryErrorClearsOnlyByReload CrashPermanentForInstance ReloadNeverShowsLoading diff --git a/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_QueryErrorSticks.cfg b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_QueryErrorSticks.cfg new file mode 100644 index 0000000000..116c64ff14 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_QueryErrorSticks.cfg @@ -0,0 +1,5 @@ +CONSTANTS + Weak = {"QueryErrorSticks"} +SPECIFICATION Spec +INVARIANTS I1_LoadingErrorNeverAfterData I2_AfterReadyOnlyCrash I3_ReadyShowsRows I4_RemedyDecodable I6_NoCleanupUnderQuery I7_NoStaleRows I8_ReadyMeansKnown NoSilentMissing LocalRowsNotHidden QueryErrorHasAWayOut +PROPERTIES ErrorClearsOnlyByReload QueryErrorClearsOnlyByReload CrashPermanentForInstance ReloadNeverShowsLoading diff --git a/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_ReloadBlanks.cfg b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_ReloadBlanks.cfg new file mode 100644 index 0000000000..fbfe805119 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_ReloadBlanks.cfg @@ -0,0 +1,5 @@ +CONSTANTS + Weak = {"ReloadBlanks"} +SPECIFICATION Spec +INVARIANTS I1_LoadingErrorNeverAfterData I2_AfterReadyOnlyCrash I3_ReadyShowsRows I4_RemedyDecodable I6_NoCleanupUnderQuery I7_NoStaleRows I8_ReadyMeansKnown NoSilentMissing LocalRowsNotHidden QueryErrorHasAWayOut +PROPERTIES ErrorClearsOnlyByReload QueryErrorClearsOnlyByReload CrashPermanentForInstance ReloadNeverShowsLoading diff --git a/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_ReloadCrashed.cfg b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_ReloadCrashed.cfg new file mode 100644 index 0000000000..07a7626a99 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_ReloadCrashed.cfg @@ -0,0 +1,5 @@ +CONSTANTS + Weak = {"ReloadCrashed"} +SPECIFICATION Spec +INVARIANTS I1_LoadingErrorNeverAfterData I2_AfterReadyOnlyCrash I3_ReadyShowsRows I4_RemedyDecodable I6_NoCleanupUnderQuery I7_NoStaleRows I8_ReadyMeansKnown NoSilentMissing LocalRowsNotHidden QueryErrorHasAWayOut +PROPERTIES ErrorClearsOnlyByReload QueryErrorClearsOnlyByReload CrashPermanentForInstance ReloadNeverShowsLoading diff --git a/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_ReloadFailIsLoadingError.cfg b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_ReloadFailIsLoadingError.cfg new file mode 100644 index 0000000000..9765d617eb --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_ReloadFailIsLoadingError.cfg @@ -0,0 +1,5 @@ +CONSTANTS + Weak = {"ReloadFailIsLoadingError"} +SPECIFICATION Spec +INVARIANTS I1_LoadingErrorNeverAfterData I2_AfterReadyOnlyCrash I3_ReadyShowsRows I4_RemedyDecodable I6_NoCleanupUnderQuery I7_NoStaleRows I8_ReadyMeansKnown NoSilentMissing LocalRowsNotHidden QueryErrorHasAWayOut +PROPERTIES ErrorClearsOnlyByReload QueryErrorClearsOnlyByReload CrashPermanentForInstance ReloadNeverShowsLoading diff --git a/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_ReloadHidden.cfg b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_ReloadHidden.cfg new file mode 100644 index 0000000000..a3481cde08 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_ReloadHidden.cfg @@ -0,0 +1,5 @@ +CONSTANTS + Weak = {"ReloadHidden"} +SPECIFICATION Spec +INVARIANTS I1_LoadingErrorNeverAfterData I2_AfterReadyOnlyCrash I3_ReadyShowsRows I4_RemedyDecodable I6_NoCleanupUnderQuery I7_NoStaleRows I8_ReadyMeansKnown NoSilentMissing LocalRowsNotHidden QueryErrorHasAWayOut +PROPERTIES ErrorClearsOnlyByReload QueryErrorClearsOnlyByReload CrashPermanentForInstance ReloadNeverShowsLoading diff --git a/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_ReloadShowsLoading.cfg b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_ReloadShowsLoading.cfg new file mode 100644 index 0000000000..d2a76cedd4 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_ReloadShowsLoading.cfg @@ -0,0 +1,5 @@ +CONSTANTS + Weak = {"ReloadShowsLoading"} +SPECIFICATION Spec +INVARIANTS I1_LoadingErrorNeverAfterData I2_AfterReadyOnlyCrash I3_ReadyShowsRows I4_RemedyDecodable I6_NoCleanupUnderQuery I7_NoStaleRows I8_ReadyMeansKnown NoSilentMissing LocalRowsNotHidden QueryErrorHasAWayOut +PROPERTIES ErrorClearsOnlyByReload QueryErrorClearsOnlyByReload CrashPermanentForInstance ReloadNeverShowsLoading diff --git a/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_SilentFromDisk.cfg b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_SilentFromDisk.cfg new file mode 100644 index 0000000000..c546d23b34 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/error-surface/cfg/w_SilentFromDisk.cfg @@ -0,0 +1,5 @@ +CONSTANTS + Weak = {"SilentFromDisk"} +SPECIFICATION Spec +INVARIANTS I1_LoadingErrorNeverAfterData I2_AfterReadyOnlyCrash I3_ReadyShowsRows I4_RemedyDecodable I6_NoCleanupUnderQuery I7_NoStaleRows I8_ReadyMeansKnown NoSilentMissing LocalRowsNotHidden QueryErrorHasAWayOut +PROPERTIES ErrorClearsOnlyByReload QueryErrorClearsOnlyByReload CrashPermanentForInstance ReloadNeverShowsLoading diff --git a/docs/contributing/designs/source-failure-and-health/error-surface/readout.md b/docs/contributing/designs/source-failure-and-health/error-surface/readout.md new file mode 100644 index 0000000000..b827a2d180 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/error-surface/readout.md @@ -0,0 +1,46 @@ +# Design grammar readout: TanStack DB error surface, run 4 (2026-10-16) + +Exploratory run against frozen grammar v4 (`frozen-grammar.md`), which encodes the user's +decisions: TanStack Query-style `failureReason` / `error` per node; adapter verbs +`reportError` / `fail` / `reportHealthy`; `reload()` keeps everything visible until its new +run commits or fails (status 'error' stays 'error'); reload during a reload returns the same +promise; reload on 'crashed' throws; reload during 'loading' is allowed; failed requests are +re-issued; `cleanup()` refuses subscribers; persisted restore opens the gate, waits for the +restore before a loading error, and reports remote problems after. + +## What this tells us +Under v4 the app can always choose one fix from what it sees (wait, retry the action, +reload, recreate) across 10,516 states of one query over a persisted and an on-demand +source. Twelve deliberate weakenings each fail at their target: one error field instead of +two, a hidden reload, silent remote failure behind disk rows, `reportHealthy` clearing a +stopped node, no stale-run fence, empty restore opening the gate, reload on a crashed +Collection, reload blanking rows, reload showing 'loading' over 'error', cleanup with +subscribers, a failed reload becoming a loading error, and a query 'error' that never clears. + +## What it changes +- The TanStack Query split replaces `retrying`/`final`: `failureReason` = wait, + `error` = reload. One error field cannot carry that (control OneErrorField). +- A query's 'error' clears without its own reload once its sources are ready again through + reloads; `query.reload()` is a convenience that reloads its failed sources. +- A crash is not recovered in place; recovery is cascade cleanup and a fresh start. + +## What it does not tell us +- **F1 (decided: b):** a query in 'error' leaves it for 'loading' once no source has a + failure (for example D's reload committed while P is still on its first load), then + becomes 'ready'. A source still reloading keeps its 'error' gate, so a reload never shows + 'loading' in place of 'error'. Retries before a first ready belong inside the adapter + (query-db's retry or the queryFn), so this window should be rare. +- Scope: one query, two sources; no Effects, includes, nested queries, tabs; no liveness. +- The remedy table is analyst-written; no reader evidence for v4 names. +- Run 3's B3 result was weaker than reported: an encoding bug disabled reload whenever a + request had failed. Fixed here; re-issue now exercised. + +## Concrete case +Backend down at startup: query 'error', D's node `error`. The app calls `query.reload()` +every 30 s; the node shows `reloading` and status stays 'error' with no flicker. When the +backend returns, the commit lands and the query becomes 'ready'. + +## Evidence +safe (with F1 b): pass, 10,560 distinct states; f1 check passes (TLC 2.19). Controls: summary.tsv. F1: cfg/f1.cfg, +out/f1.out. Encoding receipts: two (Settle/Reload double assignment of req; stuck query +error after a direct source reload), both noted above. diff --git a/docs/contributing/designs/source-failure-and-health/error-surface/tlc-results.tsv b/docs/contributing/designs/source-failure-and-health/error-surface/tlc-results.tsv new file mode 100644 index 0000000000..1927457c37 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/error-surface/tlc-results.tsv @@ -0,0 +1,14 @@ +f1 Model checking completed. No error has been found. 10560 distinct states found, 0 states left on queue. +safe Model checking completed. No error has been found. 10560 distinct states found, 0 states left on queue. +w_CleanupWithSubscribers Error: Invariant I6_NoCleanupUnderQuery is violated. 5 distinct states found, 3 states left on queue. +w_EmptyRestoreOpens Error: Invariant I8_ReadyMeansKnown is violated. 9 distinct states found, 7 states left on queue. +w_HealthyClearsError Error: Action property ErrorClearsOnlyByReload is violated. 79 distinct states found, 66 states left on queue. +w_NoRunFence Error: Invariant I7_NoStaleRows is violated. 33 distinct states found, 28 states left on queue. +w_OneErrorField Error: Invariant I4_RemedyDecodable is violated. 12 distinct states found, 9 states left on queue. +w_QueryErrorSticks Error: Invariant QueryErrorHasAWayOut is violated. 888 distinct states found, 643 states left on queue. +w_ReloadBlanks Error: Invariant I3_ReadyShowsRows is violated. 14 distinct states found, 11 states left on queue. +w_ReloadCrashed Error: Action property CrashPermanentForInstance is violated. 67 distinct states found, 56 states left on queue. +w_ReloadFailIsLoadingError Error: Invariant LocalRowsNotHidden is violated. 294 distinct states found, 233 states left on queue. +w_ReloadHidden Error: Invariant I4_RemedyDecodable is violated. 14 distinct states found, 11 states left on queue. +w_ReloadShowsLoading Error: Action property ReloadNeverShowsLoading is violated. 47 distinct states found, 41 states left on queue. +w_SilentFromDisk Error: Invariant I4_RemedyDecodable is violated. 62 distinct states found, 53 states left on queue. diff --git a/docs/contributing/designs/source-failure-and-health/error-surface/user-docs-draft.md b/docs/contributing/designs/source-failure-and-health/error-surface/user-docs-draft.md new file mode 100644 index 0000000000..45fec9eb76 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/error-surface/user-docs-draft.md @@ -0,0 +1,329 @@ +--- +title: Error Handling +id: error-handling +--- + + + +## Loading and sync errors + +An app that shows synced data asks two questions: + +1. **Can I show the data?** Read `status`. +2. **Is the data on screen still being kept up to date?** Read `isSyncError` and `health`. + +TanStack DB answers them separately. Once a query has shown data, a network or backend +failure never takes the data off the screen. It shows up in `health`, and the rows stay. + +```tsx +function TaskList() { + const { + data, + isLoading, + isLoadingError, + isCrashed, + isSyncError, + health, + collection, + } = useLiveQuery((q) => + q + .from({ task: taskCollection }) + .join({ user: userCollection }, ({ task, user }) => + eq(task.assigneeId, user.id), + ), + ) + + if (isLoading) return + if (isLoadingError) return collection.reload()} /> + if (isCrashed) return + + return ( + <> + {isSyncError && ( + collection.reload()} /> + )} + {data.map((row) => ( + + ))} + + ) +} +``` + +### Status + +| `status` | Flag | Meaning | What the app does | +| ------------ | ---------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------------- | +| `idle` | `isIdle` | Sync has not started. | Nothing yet. | +| `loading` | `isLoading` | The first data is on its way. | Show a placeholder. | +| `ready` | `isReady` | The data can be shown. | Show it. Check `isSyncError` for a banner. | +| `error` | `isLoadingError` | The first load failed. There is nothing to show. | Show a failure screen and offer `reload()`. | +| `crashed` | `isCrashed` | Local storage broke. This Collection cannot be used again. | Recreate the Collection (see [When a Collection crashes](#when-a-collection-crashes)). | +| `cleaned-up` | `isCleanedUp` | The Collection was cleaned up. | Nothing. | + +Two rules make `status` predictable: + +- **After a query has shown data, only a crash changes its status.** A dropped connection, + a failed refetch, or a backend that stops answering appears in `health`. `isLoadingError` + therefore always means "nothing to show yet". +- **A query takes the worst status of its sources until it is ready.** If any source fails + its first load, the query's status is `error`. + +`isError` is deprecated. It is `true` for `error` and `crashed`. Use `isLoadingError` and +`isCrashed`, which need different handling. + +### Sync errors: the `health` tree + +`health` has one node for the query itself and one for each source in the query, in query +order. The tree's shape is fixed by the query, so you can render, log, or send it to +monitoring. + +```ts nocheck +{ + alias: 'root', kind: 'live-query', status: 'ready', hasErrors: true, + failureReason: null, error: null, reloading: false, + sources: [ + { alias: 'task', kind: 'source', status: 'ready', hasErrors: false, + failureReason: null, error: null, reloading: false, sources: [] }, + { alias: 'user', kind: 'source', status: 'ready', hasErrors: true, + failureReason: { name: 'FetchError', message: 'Network request failed', at: 1760342400000 }, + error: null, reloading: false, sources: [] }, + ], +} +``` + +A node can show one of two problems. They follow TanStack Query's `failureReason` and +`error`: + +| Field | Meaning | What the app does | +| --------------- | ------------------------------------------------------------------------------------------------------ | -------------------------------------------- | +| `failureReason` | Something went wrong, and the sync adapter is still trying. It clears by itself when sync works again. | Show "Reconnecting…" or nothing. Wait. | +| `error` | The sync adapter stopped. The rows from this source stay but no longer update. | Show "Updates stopped" and offer `reload()`. | + +`isSyncError` is `true` when the query is `ready` and any node has a `failureReason` or an +`error`. `hasErrors` on a node says whether that node or any node below it has one. + +Each problem is `{ name, message, code?, at, cause }`. `cause` holds the exact value the +adapter reported, for example the `Response` or the error your `queryFn` threw. Every other +field is plain JSON. A node keeps its object identity until something in its part of the +tree changes, so `===` and `useMemo` work on it. + +`failureReason` says that the adapter is trying, not that trying will help. An expired +token gives a `failureReason` on every refetch until the user signs in. Use `code` or +`cause` for cases like this: + +```ts +function SyncBanner({ health, onReload }: { health: HealthNode; onReload: () => void }) { + const [first] = findProblems(health) // nodes with a problem, in query order + if (first?.failureReason?.code === '401') return + if (first?.error) return + return +} +``` + +`findProblems(health)` walks the tree and returns every node with a `failureReason` or an +`error`, in query order, each with its `alias` path. Use it for the message on a failure +screen too: when `isLoadingError` is true, `findProblems(health)[0].error` is the first +load failure that stopped the query. + +`onReload` here calls the query's `reload()`, which reloads every source that has an +`error`. To reload one source instead, call `reload()` on that source's Collection. + +### Collections without a live query + +Every Collection has the same surface, so code that uses a Collection directly does not +need a live query: + +```ts +todos.status // 'loading' | 'ready' | 'error' | 'crashed' | ... +todos.health // one node: { status, failureReason, error, reloading, ... } +todos.on('health:change', ({ health }) => report(health)) +await todos.reload() +``` + +A live query is a Collection too; its `health` is the whole tree. + +### Failed requests for more data + +After a query is ready it can ask for more data: the next page of an infinite query, or +rows for a newly joined key. When that request fails, the query's own node (`root`) shows +an `error`. The sources stay healthy and the rows already shown stay. The error clears when +the data loads, or when the query no longer needs it, for example after the user scrolls +back. + +```tsx +const { data, health, fetchNextPage } = useLiveInfiniteQuery( + (q) => + q + .from({ post: postCollection }) + .orderBy(({ post }) => post.createdAt, 'desc'), + { pageSize: 20 }, +) + +{ + health?.error && ( + + ) +} +``` + +`fetchNextPage()` resolves either way. A reload of a source re-issues failed requests for +that source. + +### Reloading + +`collection.reload()` starts a new sync run for a Collection. It works like a sync-side +truncate: nothing visible changes until the new run's first commit. + +- The current rows, `status`, and any `error` stay on screen. The node shows + `reloading: true`. +- When the new run commits, its rows replace the old rows in one change, and the node's + problem clears. A `status` of `error` becomes `ready`. +- If the new run fails before it commits, the old rows and status stay, and the node shows + the new `error`. + +On a live query, `reload()` reloads each of its sources that has an `error`. + +```ts +// Retry a failed first load every 30 seconds until the backend is back. +useEffect(() => { + if (!isLoadingError) return + const id = setInterval(() => collection.reload(), 30_000) + return () => clearInterval(id) +}, [isLoadingError, collection]) +``` + +`reload()` returns a promise that settles when the new run commits or fails. Calling it +while a reload is in flight returns the same promise. On a `crashed` Collection it throws. + +Use `reload()` for retries the user or the app decides on. Retries inside one attempt belong +to the sync adapter: for example, set `retry` on a Query Collection, so a short outage never +reaches `isLoadingError`. + +While a query waits on a source's reload, its status stays `error`. Once no source has an +error, a query that is still waiting for another source's first load shows `loading`. + +### When a Collection crashes + +A Collection crashes when its local storage can no longer serve reads or accept writes: + +- its IndexedDB connection closed (for example after a `versionchange` from another tab); +- a local SQLite persistence write or restore failed; +- an internal invariant failed. + +The Collection moves to `crashed`, and so does every live query over it. A crash is +not recovered in place: `reload()` throws. To recover, clean up the Collection together +with everything that reads from it, then start it again. + +```ts +console.log(todos.subscribers) // live queries, Effects, and raw subscriptions +await todos.cleanup({ dependents: true }) +const freshTodos = createCollection(todosOptions()) +``` + +### Cleaning up a Collection + +`cleanup()` throws while anything still subscribes to the Collection, because those +readers would lose their data. Unmount the components that read it first, or pass +`{ dependents: true }` to clean up its live queries and Effects first. +Collections that nothing reads are cleaned up automatically after `gcTime`. + +### Persisted Collections + +A persisted Collection restores its rows from local storage before the remote syncs. + +- **Restored rows open the Collection.** If local storage holds rows from an earlier sync, + the Collection becomes `ready` with them, even if the remote has not connected yet. Remote + problems after that appear in `health` like any other sync problem. +- **A failed remote waits for the restore.** If the remote fails before the restore + finishes, the Collection waits: restored rows make it `ready` with an `error` on its node; + no stored rows make it `error`. +- **An empty local store does not open the Collection.** A Collection that has never synced + stays `loading` until the remote answers, so an empty list always means "no rows", not + "unknown". +- **A failed restore is a crash.** + +`persistedStatus`, `isPersistedReady`, and `persistedError` are removed. + +### Effects + +An Effect ends when a source it reads fails its first load or crashes; it calls +`onSourceError(error)`. After its initial load, any other problem calls +`onHealthChange(health)` with the same tree a live query returns, and the Effect keeps +running. + +### Suspense and error boundaries (React) + +`useLiveSuspenseQuery` suspends while `loading` and throws to the nearest error boundary for +`error` and `crashed`. Sync problems after the first data never reach the boundary; read +`health` from the hook. + +## For adapter authors + +A sync adapter reports failures with three calls: + +| Call | Meaning | Effect | +| -------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------ | +| `reportError(error)` | Something went wrong; I am still trying. | Sets `failureReason` (after the first ready). | +| `fail(error)` | I stopped. | Before the first ready: `status: 'error'`. After: the node's `error`; the rows stay. | +| `reportHealthy()` | Sync works again. | Clears `failureReason`. | + +```ts +sync: ({ + begin, + write, + commit, + markReady, + reportError, + fail, + reportHealthy, +}) => { + let ready = false + const stream = connect({ + onData: (rows) => { + begin() + rows.forEach((row) => write({ type: 'insert', value: row })) + commit() + if (ready) reportHealthy() + else markReady() + ready = true + }, + onError: (error, { willRetry }) => { + if (!willRetry) fail(error) + else if (ready) reportError(error) + }, + }) + return () => stream.close() +} +``` + +- Retry inside the first load before calling `fail`. While you retry before the first + ready, call nothing; the Collection stays `loading`. +- These calls follow the Collection's state machine, and a call that does not fit it + throws: `reportError` or `reportHealthy` before the first ready, `markReady` after + `fail`, and any call after the sync run has ended. `reportHealthy` when nothing is wrong + is allowed and changes nothing, so you can call it after every commit. +- An `error` from `fail` clears only through `reload()`, which calls your `sync` function + again for a new run. + +## Migrating + +| Before | After | +| ------------------------------------------------------------------- | ------------------------------------------------------------------- | +| `isError` | `isLoadingError` (first load failed) or `isCrashed` (storage broke) | +| `status: 'error'` after a crash | `status: 'crashed'` | +| `markError(error)` | `fail(error)` | +| `reportError(error, { final })` | `reportError(error)` or `fail(error)` | +| node `phase`, `final`, `isError` | node `failureReason` or `error` | +| `cleanup()` then `preload()` to recover | `reload()` | +| `cleanup()` with live queries mounted | throws; unmount first or `cleanup({ dependents: true })` | +| `persistedStatus`, `isPersistedReady`, `persistedError` | `status` and `health` | +| query-db `utils.lastError`, `isError`, `errorCount`, `clearError()` | `health`, `reload()` | diff --git a/docs/contributing/designs/source-failure-and-health/evidence/model-d-source-error-grammar.patch b/docs/contributing/designs/source-failure-and-health/evidence/model-d-source-error-grammar.patch new file mode 100644 index 0000000000..1385e175ad --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/evidence/model-d-source-error-grammar.patch @@ -0,0 +1,214 @@ +diff --git a/packages/db/tests/query/includes-temporal-oracle.test.ts b/packages/db/tests/query/includes-temporal-oracle.test.ts +index ee1f8e9dd..5f4228c42 100644 +--- a/packages/db/tests/query/includes-temporal-oracle.test.ts ++++ b/packages/db/tests/query/includes-temporal-oracle.test.ts +@@ -2727,35 +2727,54 @@ function disturbedHistories( + ] + } + +-// A source error before settlement rejects the pending load and puts the +-// source Collection in `error`, in that order, as Electric does when a stream +-// error is final. `source-ready` then recovers the source. Each history moves +-// the source's readiness after the error, so recovery is the source's first +-// `ready` after it; a history may also settle before recovery, when no +-// request is pending. A `fail` rejects the pending load while the source +-// stays healthy: the hostile control for the same rejection. ++// A source error rejects any pending load and puts the source Collection in ++// `error`, in that order, as Electric does when a stream error is final. The ++// first `source-ready` after the error recovers the source; a history whose ++// source was ready before the error gets a recovery at every later position, ++// so ready → error → ready is reached as well as an error before the first ++// ready. The error can come before or after `settle`: after it, the query is ++// already ready and no load is pending. A `truncate` can arrive during the ++// error or right after its recovery. A `fail` rejects the pending load while ++// the source stays healthy, the hostile control for the same rejection, with ++// the source's readiness at every position. ++function insertStep( ++ history: ReadonlyArray, ++ at: number, ++ step: LaggingSourceStep, ++): Array { ++ return [...history.slice(0, at), step, ...history.slice(at)] ++} ++ + function sourceErrorHistories( + failure: `source-error` | `fail`, + ): Array> { + const histories = new Map>() ++ const add = (history: Array) => ++ histories.set(history.join(), history) + for (const history of laggingSourceHistories(`on-demand`)) { +- const rest = history.filter((step) => step !== `source-ready`) +- for (let at = 0; at <= rest.indexOf(`settle`); at++) { +- const failed = [...rest.slice(0, at), failure, ...rest.slice(at)] +- const recoveries = +- failure === `fail` +- ? [history.slice(0, at).includes(`source-ready`) ? 0 : failed.length] +- : Array.from( +- { length: failed.length - at }, +- (_, offset) => at + 1 + offset, ++ if (failure === `fail`) { ++ const rest = history.filter((step) => step !== `source-ready`) ++ for (let at = 0; at <= rest.indexOf(`settle`); at++) { ++ const failed = insertStep(rest, at, `fail`) ++ for (let ready = 0; ready <= failed.length; ready++) ++ add(insertStep(failed, ready, `source-ready`)) ++ } ++ continue ++ } ++ for (let at = 0; at <= history.length; at++) { ++ const errored = insertStep(history, at, `source-error`) ++ const later = errored.indexOf(`source-ready`, at + 1) ++ const recovered = ++ later >= 0 ++ ? [errored] ++ : Array.from({ length: errored.length - at }, (_, offset) => ++ insertStep(errored, at + 1 + offset, `source-ready`), + ) +- for (const ready of recoveries) { +- const candidate = [ +- ...failed.slice(0, ready), +- `source-ready` as const, +- ...failed.slice(ready), +- ] +- histories.set(candidate.join(), candidate) ++ for (const candidate of recovered) { ++ add(candidate) ++ const recovery = candidate.indexOf(`source-ready`, at + 1) ++ add(insertStep(candidate, recovery, `truncate`)) ++ add(insertStep(candidate, recovery + 1, `truncate`)) + } + } + } +@@ -2783,6 +2802,11 @@ type LaggingSourceFacts = { + // The source Collection is in `error`: its rejected loads belong to that + // error, and recovery acquires their demand again. + sourceErrored: boolean ++ // The source error rejected a pending load, so recovery acquires its ++ // demand again. ++ detachedByError: boolean ++ // Acquisitions the model expects beyond the first, one per such recovery. ++ reacquisitions: number + // A load rejected while its source was healthy: a terminal query failure. + failed: boolean + preloadFailed: boolean +@@ -2828,7 +2852,9 @@ function expectedLiveQueryObservation( + facts: LaggingSourceFacts, + ): LiveQueryObservation { + const mode = LAGGING_SOURCE_SHAPES[shape] +- const ready = initiallyReady(mode, facts) ++ const initial = initiallyReady(mode, facts) ++ // A source in `error` puts the live query in error until it recovers. ++ const ready = initial && !facts.sourceErrored + const heldIds = facts.posts + .filter((post) => postMatchesShape(shape, facts, post)) + .map(({ id }) => id) +@@ -2836,7 +2862,7 @@ function expectedLiveQueryObservation( + return { + ready, + // A preload settles at readiness, or rejects with the source error. +- preloadSettled: ready || facts.preloadFailed, ++ preloadSettled: initial || facts.preloadFailed, + preloadFailed: facts.preloadFailed, + // A failed query owes no rows; a recovering one publishes through the + // restart replay barrier. +@@ -2848,7 +2874,15 @@ function expectedLiveQueryObservation( + : facts.settled + ? heldIds.slice(-1) + : [], +- acquisitions: mode === `on-demand` ? 1 : 0, ++ // A replay after a truncate or rekey, and an ordered window, may issue ++ // extra requests by implementation choice; only acquisition is judged. ++ // Otherwise each recovery from a rejecting source error acquires once. ++ acquisitions: ++ mode !== `on-demand` ++ ? 0 ++ : shape === `ordered` || facts.disturbed ++ ? 1 ++ : 1 + facts.reacquisitions, + } + } + +@@ -3182,15 +3216,19 @@ function applyLaggingSourceStep( + facts.replayPending = true + } else if (step === `source-error` || step === `fail`) { + const error = new Error(`subset load failed`) ++ // Before settlement a load is pending; after it, none is. ++ const pending = !facts.settled + for (const source of sources) { + source.rejectPending(error) + if (step === `source-error`) source.markError(error) + } +- if (step === `source-error`) facts.sourceErrored = true +- else facts.failed = true +- facts.disturbed = true +- facts.replayPending = step === `source-error` +- facts.preloadFailed = true ++ if (step === `source-error`) { ++ facts.sourceErrored = true ++ facts.detachedByError ||= pending ++ } else facts.failed = true ++ facts.replayPending ||= step === `source-error` && pending ++ // Only a preload that has not settled can reject. ++ if (pending) facts.preloadFailed = true + } else if (step === `settle`) { + for (const source of sources) + for (const acquisition of source.acquisitions) acquisition.resolve() +@@ -3201,10 +3239,12 @@ function applyLaggingSourceStep( + facts.replayPending = false + } + } else if (facts.sourceErrored) { +- // Recovery: both sources become ready and acquire their demand again. ++ // Recovery: both sources become ready and acquire detached demand again. + for (const source of sources) source.markReady() + facts.sourceErrored = false + facts.sourceReady = true ++ if (facts.detachedByError) facts.reacquisitions += 1 ++ facts.detachedByError = false + } else { + // The twin's source has been ready since its start. + main.source.markReady() +@@ -3255,6 +3295,8 @@ function createLaggingSourceDriver( + readOwes: undefined, + replayPending: false, + sourceErrored: false, ++ detachedByError: false, ++ reacquisitions: 0, + failed: false, + preloadFailed: false, + }, +@@ -3417,6 +3459,8 @@ function laggingSourceProjection( + twinDisagreement: undefined, + }), + assertEqual: (observed, expected) => { ++ if (process.env.DEBUG_MODEL_D && JSON.stringify(observed) !== JSON.stringify(expected)) ++ require('node:fs').appendFileSync(process.env.DEBUG_MODEL_D + '.diff', JSON.stringify({ observed, expected }) + '\n') + expect(observed).toEqual(expected) + return undefined + }, +@@ -3428,11 +3472,20 @@ async function expectLaggingSourceTraceMatches( + shape: LaggingSourceShape, + steps: ReadonlyArray, + ): Promise { +- await runTrace({ +- steps, +- driver: createLaggingSourceDriver(consumer, shape), +- projection: laggingSourceProjection(consumer, shape), +- }) ++ try { ++ await runTrace({ ++ steps, ++ driver: createLaggingSourceDriver(consumer, shape), ++ projection: laggingSourceProjection(consumer, shape), ++ }) ++ } catch (error) { ++ if (process.env.DEBUG_MODEL_D) ++ require('node:fs').appendFileSync( ++ process.env.DEBUG_MODEL_D, ++ JSON.stringify({ consumer, shape, steps, error: String((error as any).message), cause: String((error as any).cause?.message ?? '') }) + '\n', ++ ) ++ throw error ++ } + } + + // Each generated lane uses one grammar and one check in both campaigns. A diff --git a/docs/contributing/designs/source-failure-and-health/evidence/pr-2129-review-probes.test.ts.txt b/docs/contributing/designs/source-failure-and-health/evidence/pr-2129-review-probes.test.ts.txt new file mode 100644 index 0000000000..8cfa419561 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/evidence/pr-2129-review-probes.test.ts.txt @@ -0,0 +1,329 @@ +import { describe, expect, it } from 'vitest' +import { createCollection } from '../../src/collection/index.js' +import { createDeferred } from '../../src/deferred.js' +import { LoadSubsetOperationAbortedError } from '../../src/errors.js' +import { + createLiveQueryCollection, + eq, +} from '../../src/query/index.js' +import { flushPromises } from '../utils.js' +import type { Deferred } from '../../src/deferred.js' + +type Post = { id: number; userId: number; title: string } +type User = { id: number; name: string } + +let seq = 0 +function makeSource(opts: { readyAtStart?: boolean } = {}) { + const acquisitions: Array> = [] + let s: any + const collection = createCollection({ + id: `scratch-src-${++seq}`, + getKey: (p) => p.id, + syncMode: `on-demand`, + startSync: true, + sync: { + sync: (params: any) => { + s = params + if (opts.readyAtStart) params.markReady() + return { + loadSubset: ({ signal }: { signal?: AbortSignal }) => { + const d = createDeferred() + acquisitions.push(d) + signal?.addEventListener(`abort`, () => + d.reject(new LoadSubsetOperationAbortedError()), + ) + return d.promise + }, + } + }, + }, + }) + return { + collection, + acquisitions, + pending: () => acquisitions.filter((a) => a.isPending()).length, + write: (p: Post) => { + s.begin() + s.write({ type: `insert`, value: p }) + s.commit() + }, + truncate: () => { + s.begin() + s.truncate() + s.commit() + }, + markReady: () => s.markReady(), + markError: (e: unknown) => s.markError(e), + rejectPending: (e: unknown) => { + for (const a of acquisitions) if (a.isPending()) a.reject(e) + }, + resolvePending: () => { + for (const a of acquisitions) if (a.isPending()) a.resolve() + }, + } +} + +function makeUsers() { + return createCollection({ + id: `scratch-users-${++seq}`, + getKey: (u) => u.id, + startSync: true, + sync: { + sync: ({ begin, write, commit, markReady }: any) => { + begin() + write({ type: `insert`, value: { id: 1, name: `u1` } }) + commit() + markReady() + }, + }, + }) +} + +const direct = (src: ReturnType) => + createLiveQueryCollection((q) => + q.from({ p: src.collection }).where(({ p }) => eq(p.userId, 1)), + ) + +const joined = (src: ReturnType) => { + const users = makeUsers() + return createLiveQueryCollection((q) => + q + .from({ u: users }) + .leftJoin({ p: src.collection }, ({ u, p }) => eq(u.id, p.userId)) + .select(({ u, p }) => ({ uid: u.id, pid: p?.id })), + ) +} + +const err = new Error(`source failed`) + +async function errorThenRecover( + src: ReturnType, + between: () => void = () => {}, +) { + src.rejectPending(err) + src.markError(err) + await flushPromises() + between() + await flushPromises() +} + +describe(`scratch #2129 evaluation`, () => { + it(`baseline: direct recovers after error → ready`, async () => { + const src = makeSource() + const live = direct(src) + void live.preload().catch(() => {}) + await flushPromises() + await errorThenRecover(src) + src.markReady() + await flushPromises() + const before = live.status + src.write({ id: 1, userId: 1, title: `a` }) + src.resolvePending() + await flushPromises() + console.log(`BASELINE direct before=${before} after=${live.status} rows=${live.size} acq=${src.acquisitions.length}`) + expect(live.status).toBe(`ready`) + }) + + it(`C1a: truncate during source error, then markReady`, async () => { + const src = makeSource() + const live = direct(src) + void live.preload().catch(() => {}) + await flushPromises() + await errorThenRecover(src, () => src.truncate()) + src.markReady() + await flushPromises() + src.write({ id: 1, userId: 1, title: `a` }) + src.resolvePending() + await flushPromises() + src.resolvePending() + await flushPromises() + console.log(`C1a direct status=${live.status} rows=${live.size} acq=${src.acquisitions.length} pending=${src.pending()}`) + expect(live.status).toBe(`ready`) + }) + + it(`C1b: truncate right after markReady (before restart microtask)`, async () => { + const src = makeSource() + const live = direct(src) + void live.preload().catch(() => {}) + await flushPromises() + await errorThenRecover(src) + src.markReady() + src.truncate() + await flushPromises() + src.write({ id: 1, userId: 1, title: `a` }) + src.resolvePending() + await flushPromises() + src.resolvePending() + await flushPromises() + console.log(`C1b direct status=${live.status} rows=${live.size} acq=${src.acquisitions.length} pending=${src.pending()}`) + expect(live.status).toBe(`ready`) + }) + + it(`C2: join with truncate during error`, async () => { + const src = makeSource() + const live = joined(src) + void live.preload().catch(() => {}) + await flushPromises() + await errorThenRecover(src, () => src.truncate()) + src.markReady() + await flushPromises() + src.write({ id: 1, userId: 1, title: `a` }) + src.resolvePending() + await flushPromises() + src.resolvePending() + await flushPromises() + console.log(`C2 join status=${live.status} rows=${JSON.stringify(live.toArray.map((r: any) => r.pid))} acq=${src.acquisitions.length}`) + expect(live.status).toBe(`ready`) + }) + + it(`C3: query starts while source already in error; recovery holds load`, async () => { + const src = makeSource() + src.markError(err) + await flushPromises() + const live = direct(src) + void live.preload().catch(() => {}) + await flushPromises() + const statusDuringError = live.status + src.markReady() + await flushPromises() + const readyBeforeLoad = live.status + const pendingAtCheck = src.pending() + src.write({ id: 1, userId: 1, title: `a` }) + src.resolvePending() + await flushPromises() + console.log(`C3 duringError=${statusDuringError} afterRecoverHeld=${readyBeforeLoad} pending=${pendingAtCheck} final=${live.status} rows=${live.size}`) + expect(readyBeforeLoad).not.toBe(`ready`) + }) + + it(`C3b: query starts while source in error, markReady synchronously checked`, async () => { + const src = makeSource() + src.markError(err) + await flushPromises() + const live = direct(src) + void live.preload().catch(() => {}) + await flushPromises() + let readyAtMarkReady: string | undefined + live.on(`status:change`, ({ status }: any) => { + if (status === `ready` && readyAtMarkReady === undefined) + readyAtMarkReady = `pending=${src.pending()} acq=${src.acquisitions.length}` + }) + src.markReady() + await flushPromises() + console.log(`C3b firstReady=${readyAtMarkReady} status=${live.status}`) + expect(readyAtMarkReady).toBeUndefined() + }) + + it(`C7: second error during recovery replay, then recover`, async () => { + const src = makeSource() + const live = joined(src) + void live.preload().catch(() => {}) + await flushPromises() + await errorThenRecover(src) + src.markReady() + await flushPromises() + const acqAfterFirst = src.acquisitions.length + await errorThenRecover(src) + const midStatus = live.status + src.markReady() + await flushPromises() + src.write({ id: 1, userId: 1, title: `a` }) + src.resolvePending() + await flushPromises() + console.log(`C7 join acqAfterFirst=${acqAfterFirst} mid=${midStatus} final=${live.status} acq=${src.acquisitions.length} rows=${JSON.stringify(live.toArray.map((r: any) => r.pid))}`) + expect(live.status).toBe(`ready`) + }) + + it(`C7 direct: second error during recovery replay, then recover`, async () => { + const src = makeSource() + const live = direct(src) + void live.preload().catch(() => {}) + await flushPromises() + await errorThenRecover(src) + src.markReady() + await flushPromises() + await errorThenRecover(src) + src.markReady() + await flushPromises() + src.write({ id: 1, userId: 1, title: `a` }) + src.resolvePending() + await flushPromises() + console.log(`C7d final=${live.status} acq=${src.acquisitions.length} rows=${live.size}`) + expect(live.status).toBe(`ready`) + }) + + it(`ready→error→ready: error after first ready, then recover`, async () => { + const src = makeSource() + const live = joined(src) + void live.preload().catch(() => {}) + await flushPromises() + src.write({ id: 1, userId: 1, title: `a` }) + src.resolvePending() + await flushPromises() + const s1 = live.status + src.markReady() + await flushPromises() + // A later lazy demand pending when error hits: change nothing; just error + src.markError(err) + await flushPromises() + const s2 = live.status + src.markReady() + await flushPromises() + console.log(`RER join s1=${s1} s2=${s2} final=${live.status}`) + }) + + it(`C8: plain subscribeChanges + requestSnapshot loadSubsetError during source error`, async () => { + const src = makeSource() + const events: Array = [] + const sub = src.collection.subscribeChanges(() => {}, { + includeInitialState: false, + } as any) + ;(sub as any).on?.(`loadSubsetError`, () => events.push(`loadSubsetError`)) + ;(sub as any).requestSnapshot?.({ where: undefined }) + await flushPromises() + src.rejectPending(err) + src.markError(err) + await flushPromises() + console.log(`C8 events=${JSON.stringify(events)} lastError=${String((sub as any).lastError?.message)} acq=${src.acquisitions.length}`) + sub.unsubscribe() + }) + + it(`C8b: ordered window source error then recover`, async () => { + const src = makeSource() + const live = createLiveQueryCollection((q) => + q + .from({ p: src.collection }) + .orderBy(({ p }) => p.id, `desc`) + .limit(1), + ) + void live.preload().catch(() => {}) + await flushPromises() + await errorThenRecover(src) + src.markReady() + await flushPromises() + src.write({ id: 1, userId: 1, title: `a` }) + src.write({ id: 2, userId: 1, title: `b` }) + for (let i = 0; i < 5; i++) { + src.resolvePending() + await flushPromises() + } + console.log(`C8b ordered status=${live.status} rows=${JSON.stringify(live.toArray.map((r: any) => r.id))} acq=${src.acquisitions.length}`) + expect(live.status).toBe(`ready`) + expect(live.toArray.map((r: any) => r.id)).toEqual([2]) + }) + + it(`C8c: subscription load error event during source error`, async () => { + const src = makeSource() + const events: Array = [] + const sub = src.collection.subscribeChanges(() => {}, { + includeInitialState: false, + onLoadSubsetError: () => events.push(`error`), + } as any) + ;(sub as any).requestSnapshot({}) + await flushPromises() + src.rejectPending(err) + src.markError(err) + await flushPromises() + console.log(`C8c events=${JSON.stringify(events)} lastError=${String((sub as any).lastError?.message)}`) + sub.unsubscribe() + }) +}) diff --git a/docs/contributing/designs/source-failure-and-health/health-safe.cfg b/docs/contributing/designs/source-failure-and-health/health-safe.cfg new file mode 100644 index 0000000000..6b537afc45 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/health-safe.cfg @@ -0,0 +1,12 @@ +SPECIFICATION Spec +CONSTANTS + HealthFlipsStatus = FALSE + NestedDoesNotRelay = FALSE + FrameworkClears = FALSE + DropHealthyNodes = FALSE + FailureClearsRows = FALSE +INVARIANT StableShape +INVARIANT TreeMirrorsSources +INVARIANT OnlySourceClears +PROPERTY GateIgnoresHealth +INVARIANT GateExplained diff --git a/docs/contributing/designs/source-failure-and-health/health-weak-clears-rows.cfg b/docs/contributing/designs/source-failure-and-health/health-weak-clears-rows.cfg new file mode 100644 index 0000000000..3f26defed5 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/health-weak-clears-rows.cfg @@ -0,0 +1,8 @@ +SPECIFICATION Spec +CONSTANTS + HealthFlipsStatus = FALSE + NestedDoesNotRelay = FALSE + FrameworkClears = FALSE + DropHealthyNodes = FALSE + FailureClearsRows = TRUE +PROPERTY GateIgnoresHealth diff --git a/docs/contributing/designs/source-failure-and-health/health-weak-drop-healthy.cfg b/docs/contributing/designs/source-failure-and-health/health-weak-drop-healthy.cfg new file mode 100644 index 0000000000..633d8a57d2 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/health-weak-drop-healthy.cfg @@ -0,0 +1,8 @@ +SPECIFICATION Spec +CONSTANTS + HealthFlipsStatus = FALSE + NestedDoesNotRelay = FALSE + FrameworkClears = FALSE + DropHealthyNodes = TRUE + FailureClearsRows = FALSE +INVARIANT StableShape diff --git a/docs/contributing/designs/source-failure-and-health/health-weak-flips-status.cfg b/docs/contributing/designs/source-failure-and-health/health-weak-flips-status.cfg new file mode 100644 index 0000000000..381cea5fbb --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/health-weak-flips-status.cfg @@ -0,0 +1,8 @@ +SPECIFICATION Spec +CONSTANTS + HealthFlipsStatus = TRUE + NestedDoesNotRelay = FALSE + FrameworkClears = FALSE + DropHealthyNodes = FALSE + FailureClearsRows = FALSE +PROPERTY GateIgnoresHealth diff --git a/docs/contributing/designs/source-failure-and-health/health-weak-framework-clears.cfg b/docs/contributing/designs/source-failure-and-health/health-weak-framework-clears.cfg new file mode 100644 index 0000000000..d9ebd8496c --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/health-weak-framework-clears.cfg @@ -0,0 +1,8 @@ +SPECIFICATION Spec +CONSTANTS + HealthFlipsStatus = FALSE + NestedDoesNotRelay = FALSE + FrameworkClears = TRUE + DropHealthyNodes = FALSE + FailureClearsRows = FALSE +INVARIANT OnlySourceClears diff --git a/docs/contributing/designs/source-failure-and-health/health-weak-no-relay.cfg b/docs/contributing/designs/source-failure-and-health/health-weak-no-relay.cfg new file mode 100644 index 0000000000..c646b3d3fe --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/health-weak-no-relay.cfg @@ -0,0 +1,8 @@ +SPECIFICATION Spec +CONSTANTS + HealthFlipsStatus = FALSE + NestedDoesNotRelay = TRUE + FrameworkClears = FALSE + DropHealthyNodes = FALSE + FailureClearsRows = FALSE +INVARIANT TreeMirrorsSources diff --git a/docs/contributing/designs/source-failure-and-health/safe.cfg b/docs/contributing/designs/source-failure-and-health/safe.cfg new file mode 100644 index 0000000000..6f8e544dc0 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/safe.cfg @@ -0,0 +1,22 @@ +SPECIFICATION Spec +CONSTANTS + MaxAttempts = 3 + HealOnReady = FALSE + RejectOnRetry = FALSE + LazyTerminal = FALSE + DetachWaits = FALSE + SilentGiveUp = FALSE + StaleApplies = FALSE + PoisonSource = FALSE + FrameworkRetry = FALSE + ClearOnLateFail = FALSE +INVARIANT TypeOK +INVARIANT RetryIsNotFailure +INVARIANT NoHeal +INVARIANT PassThrough +INVARIANT ReadyMeansRows +INVARIANT StoppedSourceReported +INVARIANT ErrorIsOwn +INVARIANT ConsumersAgree +INVARIANT RequestFailureIsLocal +INVARIANT OnlyAppRestarts diff --git a/docs/contributing/designs/source-failure-and-health/weak-clear-late.cfg b/docs/contributing/designs/source-failure-and-health/weak-clear-late.cfg new file mode 100644 index 0000000000..ec51cd6f21 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/weak-clear-late.cfg @@ -0,0 +1,14 @@ +SPECIFICATION Spec +CONSTANTS + MaxAttempts = 3 + HealOnReady = FALSE + RejectOnRetry = FALSE + LazyTerminal = FALSE + DetachWaits = FALSE + SilentGiveUp = FALSE + StaleApplies = FALSE + PoisonSource = FALSE + FrameworkRetry = FALSE + ClearOnLateFail = TRUE +INVARIANT TypeOK +INVARIANT ReadyMeansRows diff --git a/docs/contributing/designs/source-failure-and-health/weak-detach-waits.cfg b/docs/contributing/designs/source-failure-and-health/weak-detach-waits.cfg new file mode 100644 index 0000000000..cd5ec2b8f5 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/weak-detach-waits.cfg @@ -0,0 +1,14 @@ +SPECIFICATION Spec +CONSTANTS + MaxAttempts = 3 + HealOnReady = FALSE + RejectOnRetry = FALSE + LazyTerminal = FALSE + DetachWaits = TRUE + SilentGiveUp = FALSE + StaleApplies = FALSE + PoisonSource = FALSE + FrameworkRetry = FALSE + ClearOnLateFail = FALSE +INVARIANT TypeOK +INVARIANT PassThrough diff --git a/docs/contributing/designs/source-failure-and-health/weak-framework-retry.cfg b/docs/contributing/designs/source-failure-and-health/weak-framework-retry.cfg new file mode 100644 index 0000000000..054f2d1780 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/weak-framework-retry.cfg @@ -0,0 +1,14 @@ +SPECIFICATION Spec +CONSTANTS + MaxAttempts = 3 + HealOnReady = FALSE + RejectOnRetry = FALSE + LazyTerminal = FALSE + DetachWaits = FALSE + SilentGiveUp = FALSE + StaleApplies = FALSE + PoisonSource = FALSE + FrameworkRetry = TRUE + ClearOnLateFail = FALSE +INVARIANT TypeOK +INVARIANT OnlyAppRestarts diff --git a/docs/contributing/designs/source-failure-and-health/weak-heal.cfg b/docs/contributing/designs/source-failure-and-health/weak-heal.cfg new file mode 100644 index 0000000000..ac3b9417dc --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/weak-heal.cfg @@ -0,0 +1,14 @@ +SPECIFICATION Spec +CONSTANTS + MaxAttempts = 3 + HealOnReady = TRUE + RejectOnRetry = FALSE + LazyTerminal = FALSE + DetachWaits = FALSE + SilentGiveUp = FALSE + StaleApplies = FALSE + PoisonSource = FALSE + FrameworkRetry = FALSE + ClearOnLateFail = FALSE +INVARIANT TypeOK +INVARIANT NoHeal diff --git a/docs/contributing/designs/source-failure-and-health/weak-lazy-terminal.cfg b/docs/contributing/designs/source-failure-and-health/weak-lazy-terminal.cfg new file mode 100644 index 0000000000..ad9a96dbeb --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/weak-lazy-terminal.cfg @@ -0,0 +1,14 @@ +SPECIFICATION Spec +CONSTANTS + MaxAttempts = 3 + HealOnReady = TRUE + RejectOnRetry = FALSE + LazyTerminal = TRUE + DetachWaits = FALSE + SilentGiveUp = FALSE + StaleApplies = FALSE + PoisonSource = FALSE + FrameworkRetry = FALSE + ClearOnLateFail = FALSE +INVARIANT TypeOK +INVARIANT ConsumersAgree diff --git a/docs/contributing/designs/source-failure-and-health/weak-poison.cfg b/docs/contributing/designs/source-failure-and-health/weak-poison.cfg new file mode 100644 index 0000000000..b9bee775cb --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/weak-poison.cfg @@ -0,0 +1,14 @@ +SPECIFICATION Spec +CONSTANTS + MaxAttempts = 3 + HealOnReady = FALSE + RejectOnRetry = FALSE + LazyTerminal = FALSE + DetachWaits = FALSE + SilentGiveUp = FALSE + StaleApplies = FALSE + PoisonSource = TRUE + FrameworkRetry = FALSE + ClearOnLateFail = FALSE +INVARIANT TypeOK +INVARIANT RequestFailureIsLocal diff --git a/docs/contributing/designs/source-failure-and-health/weak-reject-on-retry.cfg b/docs/contributing/designs/source-failure-and-health/weak-reject-on-retry.cfg new file mode 100644 index 0000000000..a3e8589563 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/weak-reject-on-retry.cfg @@ -0,0 +1,14 @@ +SPECIFICATION Spec +CONSTANTS + MaxAttempts = 3 + HealOnReady = FALSE + RejectOnRetry = TRUE + LazyTerminal = FALSE + DetachWaits = FALSE + SilentGiveUp = FALSE + StaleApplies = FALSE + PoisonSource = FALSE + FrameworkRetry = FALSE + ClearOnLateFail = FALSE +INVARIANT TypeOK +INVARIANT RetryIsNotFailure diff --git a/docs/contributing/designs/source-failure-and-health/weak-silent-give-up.cfg b/docs/contributing/designs/source-failure-and-health/weak-silent-give-up.cfg new file mode 100644 index 0000000000..19b54e4f80 --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/weak-silent-give-up.cfg @@ -0,0 +1,14 @@ +SPECIFICATION Spec +CONSTANTS + MaxAttempts = 3 + HealOnReady = FALSE + RejectOnRetry = FALSE + LazyTerminal = FALSE + DetachWaits = FALSE + SilentGiveUp = TRUE + StaleApplies = FALSE + PoisonSource = FALSE + FrameworkRetry = FALSE + ClearOnLateFail = FALSE +INVARIANT TypeOK +INVARIANT StoppedSourceReported diff --git a/docs/contributing/designs/source-failure-and-health/weak-stale.cfg b/docs/contributing/designs/source-failure-and-health/weak-stale.cfg new file mode 100644 index 0000000000..bd72d81eee --- /dev/null +++ b/docs/contributing/designs/source-failure-and-health/weak-stale.cfg @@ -0,0 +1,14 @@ +SPECIFICATION Spec +CONSTANTS + MaxAttempts = 3 + HealOnReady = FALSE + RejectOnRetry = FALSE + LazyTerminal = FALSE + DetachWaits = FALSE + SilentGiveUp = FALSE + StaleApplies = TRUE + PoisonSource = FALSE + FrameworkRetry = FALSE + ClearOnLateFail = FALSE +INVARIANT TypeOK +INVARIANT ErrorIsOwn diff --git a/docs/contributing/glossary.md b/docs/contributing/glossary.md new file mode 100644 index 0000000000..61eb31cd5c --- /dev/null +++ b/docs/contributing/glossary.md @@ -0,0 +1,269 @@ +# Project glossary + +Use one term for one concept across production code, models, tests, and design +documents. Do not rotate synonyms for style. Shared words let a reader move +between the contract, the model, and the implementation without translating. + +This glossary owns terms that cross subsystem boundaries. A subsystem may add +narrower terms in its architecture document. It must not redefine a term here. +When an independent model needs a different abstraction, state the mapping next +to the model. + +Aligned vocabulary does not mean copied logic. A reference model should not use +production queues, caches, or semantic helpers merely to share their names. + +## Runtime terms + +| Term | Meaning | Do not use as a synonym | +| --- | --- | --- | +| Collection | The public keyed data container. Capitalize it when referring to the TanStack DB type. | Relation, table, or query result. | +| source Collection | A Collection read by a query or adapter. | Source relation when the value is a public Collection. | +| live-query Collection | A Collection whose rows are produced by a live query. | Query, observer, or result set. | +| framework binding | What one mounted framework hook or injector keeps between updates: its compiled live query or window binding, its observer or controller, and its source Collection ID claims. | Subscription, observer, or live-query Collection. | +| query identity | The value that decides whether a framework binding compiles its query input again: a nonempty `queryKey`, or the identity derived from the query IR. A `DbClient` matches preloaded and hydrated queries by its hash. React and Preact compare it on every render; reactive adapters compile again when their reads change and compare none. | Query hash when the value is not hashed, or dependency list. | +| source Collection ID claim | A framework binding's record of which source Collection object each Collection ID named, per `DbClient` scope, kept while the binding is mounted. A binding that compares query identity rejects a different object under a claimed ID. | Collection registry or source identity. | +| window binding | The infinite-query state a framework binding keeps between updates: the input, its identity dependencies, the page shape, the window collection, and its controller. `bindLiveQueryWindow` decides whether to keep it, replace its controller, or bind a new window collection. | Window controller or live-query Collection. | +| pooled live query | A live query on one source Collection whose `where` has at least one `eq(field, literal)` conjunct and otherwise reads only the row, optionally ordered by the row's own fields without a limit, served from an equality partition; its live-query Collection is built only when read. | Cached query or shared live-query Collection. | +| equality partition | Source rows grouped by the `eq`-normalized values of one set of fields, shared by pooled live queries on the same source Collection with the same equality fields and order terms, including resolved comparison options. | Index or bucket relation. | +| partition group | The rows of an equality partition whose fields equal one tuple of literals. | Bucket or active bucket. | +| relation | An internal weighted multiset maintained by D2. | Collection. | +| row | One keyed public Collection value or one relation value. Qualify source row, relation row, or public row when more than one kind appears. | Event or transaction. | +| change message | One insert, update, or delete delivered through the Collection sync boundary. | Transaction or publication. | +| sync transaction | The changes between one `begin()` and `commit()` pair. | Optimistic transaction or publication. | +| optimistic transaction | A local mutation transaction whose intent may later complete or roll back. | Sync transaction. | +| optimistic state | The rows an optimistic transaction overlays on the applied synced rows while its mutation function runs. It drops when the mutation function settles, whether it fulfills or rejects. | Retained snapshot or accepted local snapshot. | +| accepted sync transaction | A committed sync transaction that core, or a wrapping sync after its durable step, has taken. It always applies, in commit order. While an optimistic transaction is persisting it is held, and it applies in the publication that drops that transaction's optimistic state. Handler-facing writes and Collection readiness wait for acceptance; commit receipts and subset loads wait for visibility. | Visible or applied sync transaction. | +| deferred sync transaction | A committed sync transaction that a wrapping sync has not handed to the Collection yet, because an earlier durable write is still running. Like a queued sync transaction, it holds settled optimistic rows on its keys until it applies. | Queued or accepted sync transaction. | +| reserved sync transaction | A sync transaction whose source called `reserveCommitTurn` and that has not committed or been abandoned. Every persisted hydration that would start while it is open waits for its commit. A source that stops for good abandons it. | Deferred sync transaction or held optimistic row. | +| held optimistic row | A successful transaction's optimistic row kept only while a committed sync transaction that has not applied touches its key, so the drop and that transaction publish together. The transaction may be queued in the Collection or deferred behind a wrapping sync's durable step. Its source attribution follows the key-and-timing rules in [VirtualOrigin](../reference/type-aliases/VirtualOrigin.md), not source-client identity. | Retained or accepted snapshot. | +| Collection status | One public Collection lifecycle value: `idle`, `loading`, `ready`, `error`, `crashed`, or `cleaned-up`. | Subscription status. | +| subscription status | One subscription value: `ready` or `loadingSubset`. | Collection status. | +| subscription | A consumer of Collection changes with its own subset demands and lifecycle. | Collection, query, or transport. | +| sync adapter | The code supplied through Collection sync configuration that starts a sync run and translates between TanStack DB and a provider. | Provider or source Collection. | +| provider | The external database, service, or SDK from which a sync adapter acquires data. | Sync adapter or source Collection. | +| sync run | One invocation of a Collection's sync function, plus the callbacks and resources installed by that invocation, until cleanup invalidates them. A run may own zero or more provider requests and may outlive any one request. Component-owned `syncRunGeneration` counters fence this lifetime in Collection state, sync ownership, and live-query graph work. | Provider session, replay, generation, or request. | +| provider session | A provider-defined remote stream, connection, or SDK lifetime. Always qualify it with the provider. | Sync run. | +| cleanup | The transition that ends the current sync run and releases its resources. Public `cleanup()` rejects while a live query or Effect reads the Collection; `cleanup({ dependents: true })` ends those dependents first. A direct subscription survives cleanup. Callback invalidation and local teardown begin synchronously. The public cleanup promise and final `cleaned-up` status settle after adapter cleanup settles. The Collection object remains available for cleanup or restart. | Collection destruction, restart, or replay. | +| cleanup start | The internal boundary at which cleanup synchronously closes restart admission, invalidates the current sync run, crashes dependent live queries, and marks dependent Effects disposed. The public Collection status keeps its prior value while adapter cleanup is pending. | A Collection status, cleanup settlement, or proof that adapter resources or Effect handlers settled. | +| restart | Starting a new sync run after the prior sync run has ended. | Same-run recovery or replay. | +| reload | `collection.reload()`: starting a new sync run while the rows, status, and node problems stay. The new run's writes apply on the kept rows, which are its baseline: an insert of a key the Collection holds throws, as at any time. Only a snapshot transaction or the adapter's `truncate()` replaces every row; core deletes no kept row on its own, so removing a row the backend deleted is the adapter's job ([#2269](https://github.com/TanStack/db/issues/2269)). The reload completes when that replacement applies on a ready Collection, or at the new run's `markReady()`, which keeps the rows when no snapshot came. Completion drops the retired run's problems and keeps what the new run reported. A live query's own reload keeps its settled window and commits its new run's first complete graph result as its snapshot. | Restart, truncate replay, or retry. | +| snapshot transaction | A sync transaction the adapter begins with `begin({ snapshot: true })`. It declares that it carries every row, so its commit replaces every synced row, as `truncate()` at its start does, without marking the Collection ready. It means the same at any time. A subset load's transaction is partial and is never one. | Source snapshot or public snapshot. | +| truncate replay | An authoritative source replacement after a truncate, inside the current sync run. The Collection subscription remains active; any dependent live-query graph remains active behind its publication barrier. | Restart, reload, retry, or repair. | +| source snapshot | The source rows established by one snapshot operation within its declared predicate and window. It proves full-source state only when the provider says that scope is authoritative and exhausted. | Public snapshot or proof of full-source coverage. | +| public snapshot | The last coherent set of rows exposed to reads, events, and downstream queries. | Private replacement or source snapshot. | +| hydration seed | A row, with optional row metadata, that `DbClient.hydrate`, `applyCollectionChunk`, or `initialData` supplies to a Collection from outside its sync source. It is provisional base state: it supplies only an object (row data or row metadata) that no surviving source write has written, and a seed that arrives after such a write is ignored for that object. A seed is judged when it arrives: one that lands after a committed source write stays ignored for that object even if a persisted Collection later abandons the write before applying it. Among seeds of an unwritten row, the latest wins whole. | A sync transaction, a source write, persisted rows, or a live-query snapshot. | +| exposed authoritative base | The applied source rows available before a Collection adds optimistic mutations. Committed sync writes queued for reconciliation are not part of this base. `collection.base` reads it without starting sync. | Visible Collection state or a pending sync queue. | +| private replacement | Source or graph state withheld while an authoritative replay or repair is incomplete. | Public snapshot. | +| publication | The boundary that makes one coherent result observable to reads, events, and downstream queries. | Provider return, request settlement, or sync commit by itself. | +| atomic publication | One publication boundary at which state, events, and consumers observe the same result without an intermediate public state. | Any individual `commit()` call or source snapshot. | +| readiness | Evidence that a named consumer may proceed. Always qualify Collection readiness, subscription readiness, or initial-query readiness; they settle at different boundaries. | Provider completion or publication in general. | +| persisted restore | The eager persisted source Collection's startup read and publication of its durable rows, including an empty snapshot, in the current sync run. | Upstream/provider readiness or an SSR seed. | +| persisted readiness | The persisted wrapper's internal record that the current sync run's persisted restore completed or failed. Network-first initial render reads it; Collection status is the only public signal. | Collection readiness, network availability, or row nonemptiness. | +| persisted cache generation | One version of an on-demand Collection's durable rows and resume metadata, identified by a monotonic generation number and a distinct storage ID. One current generation accepts new claims; a retired generation remains available to sync runs that already claim it. A stale run may create a new retired generation for its own recovery without replacing the current one. Rotating a generation does not change the public Collection identity. | Sync run, provider session, or source snapshot. | +| persisted cache claim | A sync run's expiring authority to read and write one persisted cache generation. It is checked before durable access and is separate from a TanStack DB acquisition lease. | Acquisition lease, subset demand, or proof that cached rows are authoritative. | +| persisted subset restore | The persisted wrapper's read of one demand's request data from the current persisted cache generation, and publication of those rows to the Collection, in the current sync run. The result may be empty. | Persisted restore of an eager Collection, or proof that the rows are complete. | +| persisted demand coverage | A record, in one persisted cache generation, that a load of one exact demand reached applied settlement with its rows durably committed, and how many rows the demand read then. It counts only while the persisted subset restore returns at least that many rows; for an ordered window, only while it returns the same rows. It is the durable, cross-session form of the coverage that a settled acquisition establishes in one sync run: when a covered demand settles from SQLite, it is covered in that sense too. It proves completeness when that load settled, not freshness or broader extent. | Rows present in SQLite, a broader or overlapping demand, or persisted readiness. | +| network-first initial render | An opt-in persisted Collection setting, `initialRender: { strategy: 'network-first', networkTimeoutMs }`. The Collection waits for its sync adapter until the deadline passes or the remote fails, then a completed persisted restore that installed rows makes it `ready`. An empty restore keeps it `loading`. A remote failure before the restore completes waits for the restore. Network sync continues. An on-demand Collection instead settles each covered demand's load from SQLite once that load's wait passes or its remote load fails (persisted demand coverage); query readiness follows its loads. | Collection readiness from the sync adapter, an SSR seed, or a separate status. | + +`source` names a role in a data flow; it does not own a lifecycle. Do not coin a +`source session`. Use `sync run` for the local Collection sync invocation +and a provider-qualified `provider session` for a remote lifecycle. + +## Collection status and health terms + +Collection status and the health tree are separate axes. Status says whether the +data can be shown; the health tree says whether the data on screen is still kept +current. After Collection readiness in a sync run, only a crash changes status +until cleanup ends the run. + +| Term | Meaning | Do not use as a synonym | +| --- | --- | --- | +| initial gate | The role of Collection status during a sync run's first load. The status stays `loading` while the sync adapter retries, then becomes `ready` through `markReady()` or `error` through `fail()`. After Collection readiness in a sync run, sync problems never change the status. | The health tree or a live query's request errors. | +| loading error | The `fail()` that ends a sync run's first load in status `error` (`isLoadingError`). It is final for its sync run: `markReady()` afterward does nothing, and only a new sync run can reach `ready`. | Sync error or a retry. | +| crash | Moving a Collection to status `crashed` (`isCrashed`) from any active status, because it can no longer serve reads or admit writes. Core uses it for IndexedDB closure, terminal local persistence failures, a throwing sync function, and internal invariant failures. It cannot be recovered in place: nothing restarts it and `reload()` rejects. Only cleanup leaves it, and cleanup discards all of the Collection's state, so a later start is a fresh start. After a crash, the sync adapter's status calls (`markReady()`, `fail()`, `reportError()`, `reportHealthy()`) do nothing. Sync data that the run commits still applies, because closure keeps accepted sync obligations such as the confirmation of a write admitted before it. | Loading error, sync error, or cleanup. | +| sync error | A problem after Collection readiness on a source's health node, from the sync adapter: a failure reason (`reportError()`, still trying; `reportHealthy()` clears it, and `reportHealthy(error)` removes one failure) or an error (`fail()`, stopped; it stays for the sync run, every later report throws, and a second `fail()` does nothing). A live query's `isSyncError` is `true` when it is `ready` and its tree has any problem. It never changes status or rows. Cleanup resets it. | Loading error or subscription status. | +| failure reason | A node's `failureReason`: the problem while the sync adapter keeps trying, after TanStack Query's `failureReason`. Its frame is the sync run: it lists each `reportError()` since the slot was last clear, once per error value (a value already listed keeps its failure's place and `at`), `reportHealthy(error)` removes that value's failure, `reportHealthy()` ends it, and a reload's new run that reports starts a new one. The slot clears when its list empties. | An error or a retry count. | +| problem | A health node's `failureReason` or `error` (`HealthProblem`). Its `message`, `cause`, and `at` are its newest failure's, and `errors` lists every failure of its frame still open, oldest first, each `{ name, message, code?, at, cause }`. A failure that clears leaves the list; one error value is one failure, which keeps the place of its first failure. Adding or removing a failure changes the problem, so its node is a new object. | A failure, which is one entry of a problem. | +| failure | One reported value in a problem's `errors`: one `reportError()`, the `fail()` of a sync run, a crash, or one failed request. | A problem. | +| request error | A failed subset load (a rejection, or a synchronous throw, which is a rejection) for demand that a live query or Effect started after its initial-query readiness, shown as the `error` on that query's own node. The source Collection's node does not change. The node's problem lists every request failure still open. The public name stays general on purpose: an application sees a failed request for more data, not the subset-load mechanism. It clears when a later subset load for that demand fulfills, including one that the query's own reload issues, or when the demand retires. A load that core aborted decides nothing. A truncate's or a source reload's replay decides every request error of the demand it replayed: its success clears them all, and its failure replaces them with the replay's failures, which stay until a replay succeeds for the demand that remains (a replay failure belongs to the logical demand that failed, ARCHITECTURE law 9) or the demand retires. | Sync error or request data. | +| health tree | The `health` value of a Collection, live query, or Effect: one node for a Collection; for a query, one health node per query alias, in query order, fixed by the query definition. It keeps stable object identity while its content is unchanged, and `health:change` announces a change. | An error list. | +| health node | One `HealthNode` in a health tree, for a source Collection, a nested live-query Collection, or one include. It shows at most one problem, `failureReason` or `error`. A nested live-query node carries its own health tree in `sources`. | Bucket or Collection facade. | + +## Persistence coordination terms + +| Term | Meaning | +| --- | --- | +| durable election term | A per-Collection leadership term reserved in SQLite before a Browser or Electron coordinator announces ownership. Each election gets a greater term even when the previous owner committed no row. | +| publicly applied row version | The durable row version whose rows the persisted wrapper has applied to the public Collection. A position-only resume snapshot can observe a later version without applying its rows. A schema reset rebinds this version to the new reset epoch. | +| durable anchor | The row version and reset epoch observed before one source sync transaction is sent to a coordinator. If both still match and the exact transaction ID is absent, the replacement leader may apply that transaction once. | +| exact-ID reconciliation | The replacement leader checks one immutable source transaction ID under the SQLite writer lock after its RPC result becomes indeterminate. A present ID acknowledges the original commit; an absent ID needs an unchanged durable anchor. | +| reconciliation reload | A peer notification after exact-ID reconciliation that reloads persisted rows even when the receiver has already observed the same stream position. Equal public rows do not publish a second change batch. | +| source progress token | A sync adapter's opaque identity for one complete materialization at a provider position. Re-delivering that position is idempotent. Its predecessor expectation lets persistence detect a discontinuity between interleaved source commits without ordering provider handles. A discontinuity invalidates source progress evidence until an authoritative replacement. It is separate from a durable row version and a sync-run generation. | + +## Offline outbox terms + +| Term | Meaning | +| --- | --- | +| offline executor restart | Replacing an executor with a fresh one over retained durable outbox state. This does not restart a Collection sync run. | +| outbox replay | Resuming unfinished durable offline transaction work, including outbox reconciliation and permitted mutation execution or terminal-phase deletion. This is separate from truncate replay and oracle replay. | +| `deletion-pending` | The mutation function fulfilled; only acknowledged outbox deletion remains before caller success. An offline executor restart must not call the mutation function again. | +| `rejection-pending` | The named mutation function failed and the executor chose terminal rejection, or the retry decision hook failed. The caller rejects with that failure. An offline executor restart removes the marked row without calling the mutation function again or restoring its optimistic state. | + +## Demand and pagination terms + +These terms form a graph, not a one-to-one pipeline. Keep the ownership, work, +evidence, and visibility boundaries visible: + +```text +logical subset owner --retains--> demand +demand --starts or replaces--> 0..n acquisition attempts +accepted attempt --establishes--> physical acquisition + acquisition lease +physical acquisition --may drive--> 0..n sync transactions +commit(sync transaction) --returns--> applied receipt +acquisitions + receipts + graph work --may gate--> publication +publication --advances or replaces--> public snapshot +``` + +One demand may need several acquisitions. One acquisition may write through +several sync transactions. One publication may wait for several acquisitions, +receipts, or graph participants. Do not infer success or visibility from an +earlier node alone. + +| Term | Meaning | Do not use as a synonym | +| --- | --- | --- | +| demand | The logical need for source data. Demand may outlive or replace physical work. | Request, transport, or row ownership. | +| logical subset owner | One subscription claim that keeps a subset demand active. | Physical acquisition. | +| request data | The immutable `LoadSubsetOptions` and attached signal passed to an adapter. | Demand or established coverage. | +| acquisition attempt | One invocation that asks a sync adapter to start physical work. A synchronous throw is a rejection: the attempt fails, keeps its owner, and the owner's release unloads it. If cleanup invalidates the captured sync run after the adapter returns but before activation, core cancels the tentative acquisition without retaining its lease; cleanup owns the adapter resources. | Physical acquisition or transport. | +| physical acquisition | Request-scoped work accepted by a sync adapter for a demand. | Acquisition attempt, logical demand, or transport. | +| lease | An ownership token that requires a matching release. TanStack DB leases do not expire on a timer. Always qualify which resource the lease owns. | Demand, request, or data coverage. | +| acquisition lease | The release obligation created when an adapter accepts a physical acquisition. | The acquisition attempt itself. | +| window lease | One window-controller caller's contribution to the requested window. | Acquisition lease. | +| observer lease | One Query DB owner's claim that retains a query observer or its rows. | Subscription or acquisition lease. | +| deferred acquisition | The state of a live-query Collection's subscription to a source Collection before the live-query Collection has a subscriber or a preload in its current sync run. Its demand is active and reads rows the source already holds, but it starts no idle source sync run and no acquisition attempt. | Paused sync, render deferral, or demand retirement. | +| demand retirement | Removing one logical owner's claim on demand. | Acquisition release or row deletion. | +| acquisition release | Fulfilling one acquisition lease by aborting its signal and giving the owning adapter one unload opportunity. | Demand retirement, guaranteed transport cancellation, or row deletion. | +| adapter unload | The adapter callback invoked during acquisition release. | Demand retirement or proof that transport stopped. | +| abort | A cancellation signal sent to work that may still be in flight. When core aborts a subset load, that load is over for core: its later outcome decides nothing, holds no readiness, and no replay waits for it. | Release, rollback, or guaranteed transport cancellation. | +| transport | Provider work such as an HTTP request or stream. A transport may be shared, may outlive abort, and need not map one-to-one to a physical acquisition. | Acquisition attempt or demand. | +| settlement | A promise becoming fulfilled or rejected. Qualify what settled. | Success, application, or publication. | +| applied receipt | The `SyncAppliedReceipt` returned by `commit()`. It settles when that sync transaction's writes and events become visible, or rejects if they are abandoned. | Provider completion or source exhaustion. | +| applied settlement | The law that a successful subset load waits for every applied receipt that establishes its result. | Publication of a larger graph result. | +| window | The requested ordered view described by offset and limit. | Loaded prefix or established source extent. | +| prefix | Source rows from the start of one declared order through a finite boundary. | Page, window, or proof of exhaustion. | +| cursor | Values and expressions that continue an ordered request from a boundary. | Offset or general request identity. | +| boundary | The last value that an ordered acquisition has safely established for continuation. | Any last local row. | +| tie group | Rows equal under the order terms used by a continuation boundary. | Page. | +| source exhaustion | Authoritative evidence that no more matching source rows exist. | A short response unless the provider says it is authoritative. | +| generation | A monotonic token used to reject obsolete asynchronous work. Qualify the clock it fences, such as sync-run, replay, ordered-load, window-operation, cursor-sequence, post-write-refetch, or demand generation. | Session, replay, or request. | +| repair | Work that tries to restore an authoritative source result after finite coverage becomes invalid or an acquisition fails. Repair may use a replay, but the terms are not synonyms. | Retry, replay, or restart. | +| recovery | Regaining a named capability after failure. Always qualify what recovered and whether it stayed in the same sync run. | Restart or repair in general. | + +## Live-query materialization terms + +The live-query architecture defines the full laws and local vocabulary. These +cross-file terms keep their exact meanings: + +| Term | Meaning | +| --- | --- | +| data plane | The D2 graph that joins, reduces, orders, and materializes relations. | +| demand plane | The asynchronous adapter that starts acquisition attempts and releases acquisition leases. | +| bucket key | The canonical identity of one correlated child partition. | +| active bucket | A bucket referenced by at least one current route. | +| route relation | Weighted links from bucket keys to materialization cells. | +| materialization cell | One include field on one parent-row occurrence. | +| Collection facade | The stable public Collection shared by parents routed to one active bucket. | +| weighted delta | A positive or negative change to a relation row. | + +See +[`packages/db/src/query/live/ARCHITECTURE.md`](https://github.com/TanStack/db/blob/main/packages/db/src/query/live/ARCHITECTURE.md) +for bucket relations, bucket values, arrangements, reductions, and the normative +materialization laws. + +## Value comparison terms + +Equality depends on the declared comparison domain. Qualify the term when +crossing subsystem boundaries; these comparisons are not interchangeable. + +| Term | Meaning | +| --- | --- | +| D2 value identity | The value domain compared by `hash` and `equalHashValues`, including their documented reference leaves. A matching hash digest alone does not establish identity. The [identity oracle](https://github.com/TanStack/db/blob/main/packages/db-ivm/tests/hash-identity-oracle.property.test.ts) defines its bounded value rules. Map/Set insertion-order sensitivity is pinned current behavior, not a universal D2 operator contract. | +| change-event equality | The `deepEquals` comparison used for Collection change-event suppression. Its supported domain ignores Map/Set insertion order, RegExp `lastIndex`, and array holes versus `undefined`. The [change-event equality oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/utils-oracle.property.test.ts) defines its value-class rules. | +| draft equality | The comparison that recognizes a reverted draft value. Its [revert oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/proxy-revert-oracle.property.test.ts) preserves Map/Set insertion order, RegExp `lastIndex`, and array holes, with explicit snapshot rules for class instances. Native mutator methods have separate change-tracking laws. | +| persisted-snapshot equality | The value-shape comparison used before skipping a reconciliation reload. It preserves array holes, enumerable array properties, and exact nested prototypes. Equal keyed class, null-prototype, or cross-realm records with different prototypes require reload; Map and Set insertion order remains significant. | + +## Oracle and model terms + +| Term | Meaning | +| --- | --- | +| contract or law | The promised behavior and its declared boundary. | +| oracle | The rule or mechanism that judges production behavior. | +| reference model | A simpler, independent computation or state machine used as an oracle. | +| history | Starting conditions plus a sequence of actions or events, including relevant dependencies and timing. | +| history grammar | The values, actions, constraints, and schedules that can form legal histories. | +| production driver | Test code that invokes the real entry point and controls relevant external events. | +| observation | One recorded public fact. | +| trace | An ordered sequence of observations. | +| checkpoint or observation cut | The exact point where the contract requires comparison. | +| oracle replay | Re-running a recorded oracle failure from its seed, shrink path, actions, or other replay inputs. Do not use it for runtime truncate replay, retry, or a new random campaign. | +| projection or abstraction | The parts of concrete state retained for one judgment. | +| refinement check | An executable comparison at a named checkpoint that accepts only production observations allowed by the model. A sampled check is not a proof for every behavior. | +| partial oracle | An oracle that judges only named parts of the contract. | +| reach witness | Evidence that the production driver reached the claimed boundary. | +| versioned review record | Review evidence stored at a stable location with the exact reviewed head and append-only entries, so later edits cannot silently change the recorded verdict. | +| mutant | A deliberate wrong answer or implementation used to test oracle sensitivity. | +| fault injection | A deliberate runtime failure, delay, cancellation, or malformed response used to test a system boundary. | +| shrinking | Reducing a failing generated history while preserving the failure. | +| bounded exhaustiveness | Executing every case inside stated finite bounds. | +| held-out challenge | A challenge not used to shape the tested design. After it guides a change, retain it but stop calling it held out. | + +## Grammar rules + +Use nouns for state and verbs for transitions: + +- A demand **becomes active** or **retires**. +- Core **starts an acquisition attempt**. If the adapter accepts it, the attempt + **establishes a physical acquisition** and its **acquisition lease**. A + synchronous throw **fails the attempt** as a rejection does. Cleanup + may instead invalidate a returned attempt before activation; core then + **cancels the tentative acquisition** without retaining its lease. +- A logical owner **retires**. Core **releases an acquisition lease**. The + adapter **unloads** its acquisition. +- An abort signal **requests cancellation**; transport **stops** only when the + provider does so. +- A promise **settles**; it **fulfills** or **rejects**. +- Core **accepts** a sync transaction at `commit()`; a wrapping sync accepts + it after its durable step. An accepted sync transaction always applies. +- A sync transaction **applies** when its writes and events become visible. +- A replay or repair **publishes** one coherent public snapshot. +- Cleanup start **invalidates** a sync run and **closes** restart admission. + Ordinary subscription admission and deferred acquisition resumption **throw** + during active cleanup without changing subscriber ownership. + Cleanup **settles** after adapter cleanup settles and the Collection publishes + `cleaned-up`. Restart then **starts** a new sync run. +- Truncate replay **replaces** source state inside the current sync run and + any current dependent graph. +- A sync adapter **fails** with `fail()`: a loading error before Collection + readiness, a node `error` after it. While it keeps trying, it **reports an + error** (a failure reason) and **reports healthy**. A new sync run + **clears** a node `error`. +- A Collection **crashes** when it can no longer serve reads or admit writes. + +Use `settled` for a promise that fulfilled or rejected. Use `complete` for named +coverage, content, history, or a non-promise operation only when no stronger +boundary term applies. Use `success` only for fulfillment. Qualify overloaded words such as `state`, +`request`, `snapshot`, `session`, `owner`, `release`, `recovery`, and +`generation` at first use. + +## Model alignment rule + +For every model state, action, and observation, apply one of these rules: + +1. If it represents a production concept, use the canonical production term. +2. If it combines production concepts, name the abstraction and list what it + combines. +3. If it splits one production concept, qualify each part and state the split. +4. If it exists only in the model, say so. Do not give it a production name. + +The same check applies in reverse. New production terminology should update +this glossary and every model that represents that concept in the same change. diff --git a/docs/contributing/instruments/adversarial-review.md b/docs/contributing/instruments/adversarial-review.md new file mode 100644 index 0000000000..7340cd1477 --- /dev/null +++ b/docs/contributing/instruments/adversarial-review.md @@ -0,0 +1,79 @@ +# Adversarial review + +**Use when:** a model, oracle, or proposed law needs a concrete challenge. + +**Inputs:** one artifact or candidate, its sources, claimed domain, and success +criterion. Prefer a fresh reviewer that has not seen the author's desired +verdict. Without that separation, report a self-critique rather than independent +validation. Delegation remains subject to the task's authorization and tools. + +## Operation + +Start with a plausible way this account could be wrong. Select the relevant +levels; they are different questions, not a mandatory sequence: + +| Level | Attack | +| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Enforcement | Find a legal history or wrong output that the model, generator, driver, or recorder cannot distinguish. Can the intended production path remain unexercised while the test passes? | +| Formulation | Find an omitted precondition, excessive guarantee, wrong checkpoint, or ambiguous observation. Try a correct alternative implementation as well as a wrong one. | +| Suitability | Question whether the promised behavior is desirable, safe, or conventional. Name the external contract, standard, user consequence, or engineering reason that makes the objection applicable. | +| Composition | Find obligations that conflict, duplicate each other, or require a shared ordering or authority rule. Use the tension or restructuring card if that is the material uncertainty. | +| Concepts | Question the chosen identity, state, ownership, or boundary distinctions. Describe the same case without relying on its current names and check what is lost or becomes visible. | + +For a suspected information gap, use [law discovery](law-discovery.md#operation), +step 5. + +### Locate the repair + +The attack level names what to challenge. Once evidence shows a problem, use +this table to locate the repair: + +| Finding or question | Possible repair location | +| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | +| Production disagrees with an accepted law. | Production code. | +| The reference predicts behavior the accepted law does not allow. | Reference model. | +| An accepted law over-promises, under-promises, or conflicts with another law. | Authorized contract decision, then model and test updates. | +| The law needs information the production path lacks at the promised checkpoint. | Provider, protocol, architecture, or an authorized contract decision. | +| The check rejects an allowed intermediate observation or accepts a wrong one. | Evaluation: checkpoint, history grammar, driver, recorder, or comparison. | +| Correct behavior still fails the user's need. | Requirement and its suitability for the intended use. | + +These locations can overlap. Name the evidence for each before choosing a +repair; an assertion change cannot resolve a code defect or supply missing +information. + +An accepted contract remains an obligation even if the current path lacks the +information needed to meet it. A repair might require additional provider +data. Moving the promised checkpoint or narrowing the guarantee needs an +authorized contract decision; changing an assertion supplies no data. + +For each material attack, name the broken claim and construct a small legal +history or concrete failure scene. State the competing expected observations, +the evidence that could decide between them, and a possible repair condition. +Check that the attack is within scope and that its standard actually applies. +An outside-domain case may motivate a scope change; label that proposal. + +When execution is authorized and practical, run the discriminating experiment. +A hostile control must reach the intended comparison to demonstrate rejection. +Use the oracle guide's calibration and mutant-outcome rules. A source-level +argument can remain useful when execution is unavailable; label it accurately. +For a systematic search across plausible wrong implementations, use the +[mutant gap hunt](mutant-gap-hunt.md) to design semantic edits and classify +survivors. + +**Return:** supported defects, design objections, and untested hypotheses as +separate kinds of finding, with sources, witnesses, outcomes, and repair +conditions. For bugs, identify the test gap that permitted the class of failure. + +**Control:** explain what would defeat the criticism. Check whether the attack +would also reject a valid implementation or depend on a condition the contract +excludes. Do not presume a defect merely because the stance is adversarial. + +**Distortion:** generic pessimism, irrelevant external standards, author capture, +or treating a preferred design as evidence of a current correctness bug. + +**Stop when:** the material attacks have evidence or an explicit unresolved +experiment. No supported finding is a valid result. Challenging a law does not +silently replace it. + +Adapted from Field Lab's hostile failure assay; DB's version also permits +explicit criticism of the candidate's standard and conceptual organization. diff --git a/docs/contributing/instruments/index.md b/docs/contributing/instruments/index.md new file mode 100644 index 0000000000..340aa86a52 --- /dev/null +++ b/docs/contributing/instruments/index.md @@ -0,0 +1,38 @@ +# Design and review instruments + +Use an instrument when a specific uncertainty makes ordinary implementation or +review insufficient. Read only the relevant card. Choose and use it within the +user's authorized task; these cards do not require a separate approval for each +reasoning step or a fixed workflow. + +| Uncertainty | Instrument | Result | +| --------------------------------------------------------------------- | ------------------------------------------- | ----------------------------------------------------------------------------- | +| What behavior should this subsystem promise? | [Law discovery](law-discovery.md) | A sourced candidate law, competing predictions, and a distinguishing witness. | +| Could the implementation, model, or law itself be wrong? | [Adversarial review](adversarial-review.md) | Concrete attacks at named levels, their evidence, and repair conditions. | +| Would a plausible wrong implementation still pass the oracle? | [Mutant gap hunt](mutant-gap-hunt.md) | A semantic mutant, public distinction or scoped equivalence, and a contract-supported verdict. | +| Do sound obligations conflict under the same conditions? | [Tension scan](tension-scan.md) | A traced collision, missing distinction, or no supported tension. | +| Should these laws or concepts be merged, split, replaced, or removed? | [Law restructuring](law-restructuring.md) | An alternative organization and a check of what it preserves or loses. | + +These are reasoning aids, not additional oracle conformance requirements. The +[oracle guide](../oracle-rubric.md) owns testing requirements. Use the +[glossary](../glossary.md) and the relevant architecture and contract sources. +The authoring and review skills route oracle work here; the cards can also help +with architecture and other design questions. + +Keep source statements, observed behavior, generated examples, hypotheses, and +design judgments distinguishable. An existing contract constrains correctness +claims and can itself be challenged in a design proposal. Adopting a changed +contract requires the authority appropriate to the task; an instrument result +alone does not authorize changing product expectations. + +Carry forward the question, selected operation, source pointers, concrete +witness, and unresolved part in the task's normal notes or review. A separate +log or formal schema is unnecessary. No finding, no supported tension, and an +unsuccessful simplification are useful results. A reasoning witness is not an +executed test, and passing sampled histories is not universal proof. + +These compact DB adaptations draw on Field Lab's assumption and boundary +probes, hostile failure assay, tension statement, structural recombinator, and +position-preservation assay. They change the routing for repository work and +extend critique to the suitability of the law itself. They are self-contained; +Field Lab installation, its logging system, and its runtime are not required. diff --git a/docs/contributing/instruments/law-discovery.md b/docs/contributing/instruments/law-discovery.md new file mode 100644 index 0000000000..b705968d2b --- /dev/null +++ b/docs/contributing/instruments/law-discovery.md @@ -0,0 +1,81 @@ +# Law discovery + +**Use when:** a bug, example, or implementation exists but the promised behavior +is unclear; several supplied reports may share a law; or an existing law might +conflate distinct obligations. + +**Inputs:** the question, contract sources, known examples, existing oracle owner +and limits, and the public observation that matters. A missing authority is an +input gap, not permission to infer a promise from production. + +With several supplied reports or examples, ask whether one caller-visible +obligation predicts their different symptoms. Name its common preconditions, +check an apparent outlier and a nearby case it should permit, and split the +proposal if those cases require different promises. A descriptive cluster is a +lead, not evidence that one contract governs every item. + +## Operation + +1. State the obligation in domain terms before describing machinery: for which + legal inputs or histories, under which preconditions, what must be observable + at which checkpoint? Include what can still be pending or unspecified. +2. Trace its authority. Separate accepted contract, observed current behavior, + mathematical consequence, and proposed design. Explain why the obligation + matters to a caller. A mathematical rule still needs an argument that it is + the right rule for this API. +3. Form a plausible rival interpretation. Derive both predictions for the + smallest legal case that separates them, without consulting production for + the expected answer. If authority does not select a prediction, retain the + design question instead of turning one guess into an assertion. +4. Compare an ordinary case with a nearby boundary case. Vary a relevant + condition such as cardinality, ordering, identity, authority, or observation + time. Keep other conditions fixed where possible. Check the boundary is + within the claimed domain. +5. Identify the checkpoint where the contract requires the proposed result; a + permitted pending state does not establish a gap. Check whether the result + is determined by the information available to the responsible production + path there. Seek two legal underlying histories that give the path the same + initial state and inputs through that checkpoint but require incompatible + public observations. If they exist, name the missing information or provider + obligation. A candidate law may be scoped explicitly; do not silently narrow + an accepted promise, which requires authority to revise. +6. Describe the smallest independent model that enacts the obligation and limits + from step 1 and makes the rival and boundary predictions from steps 3–4. + Retain the source facts needed to examine any information gap from step 5, + even when the production path has not received them. + For any proposed merge of model states, ask whether a legal next action can + distinguish them. Map model abstractions to the project vocabulary. + +The information check concerns what production can receive. The test of model +state concerns what the reference must remember. A model may know a source row +that the production path has never received. For example, suppose a path must +publish a full top-ten result after a deletion but receives only the old top +ten and the deletion. If it cannot receive replacement rows before publication, +two source datasets can require incompatible results while the path sees the +same inputs. A live query can request replacement rows with `loadSubset`. If +the provider supplies them through Collection sync writes that apply before +publication, that path has the needed information; judge it at its own promised +publication checkpoint. + +**Return:** the candidate law and status, its source, rival predictions, a +concrete distinguishing witness, and the next experiment or unresolved decision. +For an information gap, include the indistinguishable histories and the +unresolved source, protocol, or scope decision. +For a simple law, a short paragraph and a worked example suffice. + +**Control:** try a legal alternative implementation. Does the proposed law +reject it only because its scheduling or representation differs? Also try a +plausible wrong result: could the proposed observation distinguish it? If an +information gap is claimed, try a nearby path that supplies the missing input, +such as a provider backfill. Check whether that input distinguishes the +histories before the checkpoint; if it does, the original impossibility +argument no longer applies to that path. + +**Distortion:** freezing an implementation accident into a law; treating a vivid +edge case as representative; or overstating what is owed at an intermediate cut. + +**Stop when:** the law is precise enough to enact independently, or a named +missing authority or fact prevents choosing between predictions. Continue other +authorized work while keeping that choice unresolved. + +Adapted from Field Lab's assumption mapping and ground-condition probe. diff --git a/docs/contributing/instruments/law-restructuring.md b/docs/contributing/instruments/law-restructuring.md new file mode 100644 index 0000000000..c69c3a8042 --- /dev/null +++ b/docs/contributing/instruments/law-restructuring.md @@ -0,0 +1,52 @@ +# Law restructuring + +**Use when:** laws overlap, need a missing distinction, or might be better +expressed by merging, splitting, replacing, or removing them. + +**Inputs:** the original laws with their authority, motivations, examples, and +limits. Include existing oracle owners. Do not simplify labels without first +recovering the obligations they represent. + +## Operation + +1. Decompose the laws into traceable obligations, conditions, distinctions, + and promised observations. Check that these parts can reconstruct each + original claim; invented categories can lose the essential constraint. + If public wording and an executable owner disagree, retain both claims and + the unresolved authority. A passing test alone cannot retire public wording. +2. Propose a different organization and state the operation: merge under a + common principle, split by authority or checkpoint, replace a concept, or + retire an obligation with an explicit reason. Fewer words or model fields + alone do not establish improvement. +3. Map every original obligation to where it is preserved, narrowed, strengthened, + rejected, or still unresolved in the candidate. State what insight originally + made the obligation necessary. A claim may be defeated, but needs a reason + and a design decision rather than disappearing from the mapping. +4. Derive old and proposed predictions for a witness exercising each distinction + that the reorganization could erase. Compare both allowed and forbidden + outcomes. Check whether the change accidentally forbids a valid implementation + or admits one of the formerly rejected wrong designs. +5. When an independent preservation check is useful and authorized, give a fresh + agent the original obligation, its sources, and one candidate. Hide sibling + candidates and the author's preferred outcome. Ask what insight survives, + what is defeated, and what smallest repair is needed. Without that separation, + describe the result as a trace audit, not independent validation. + +**Return:** the proposed organization, the old-to-new obligation mapping, +witnesses and their evidence status, losses, and unresolved design decisions. +Keeping the existing laws separate is a valid result. + +**Control:** reconstruct each original obligation from the candidate and check +its discriminating witness. A semantic change may be justified, but is not an +equivalent refactor. Merging prose does not require merging executable owners +whose models or production boundaries differ. + +**Distortion:** neat categories that erase obligations, speculative connections +presented as established, or preserving familiar wording instead of useful +insight. + +**Stop when:** the alternative's preservation and losses are explicit, or the +source obligations cannot be reconstructed. Do not adopt a replacement merely +because this operation produced it. + +Adapted from Field Lab's structural recombinator and position-preservation assay. diff --git a/docs/contributing/instruments/mutant-gap-hunt.md b/docs/contributing/instruments/mutant-gap-hunt.md new file mode 100644 index 0000000000..6b71a0ad22 --- /dev/null +++ b/docs/contributing/instruments/mutant-gap-hunt.md @@ -0,0 +1,94 @@ +# Mutant gap hunt + +**Use when:** a recent refactor, a suspicious green oracle, or an open review +question leaves doubt that the tests would reject a plausible wrong behavior. +One carefully chosen mutant can answer a useful question; a campaign is optional. + +**Inputs:** the changed code and commits that shaped it, the claimed law and its +authority, the primary oracle's contract prose and stated limits, its +coverage-map row, open review questions, and the public observation that could +distinguish a mistake. Keep test bodies and assertions out of the design brief. + +## Operation + +1. **Design plausible mistakes before reading the tests.** Start with semantic, + type-plausible edits near the changed boundaries: a lost operand, wrong + prior value, premature clear, duplicated event, changed ordering, or skipped + callback. Turn each open review question into a candidate. If code is called + dead, try removing it. In a larger hunt, spread candidates across the + affected files and boundaries rather than clustering where edits are easy. + Hand-write candidates at boundaries a designer may miss, such as receipt + wrappers, lazy getters, and caches. If a separate designer is authorized, + keep the current assertions out of its brief. Otherwise draft the candidates + before inspecting those assertions and label the design as self-generated. +2. **Make each edit replayable.** Record `{ id, file, old, new, desc }`; require + `old` to occur exactly once at the pinned revision and `new` to parse. Change + one decision per mutant; a compound kill does not identify which part the + comparison rejected. Run each candidate in an isolated checkout against an + unchanged, pinned baseline, then restore the checkout exactly. Use `main` + when measuring what the existing suite catches; use a PR revision when + challenging its new coverage. Do not combine those kill claims. A full-suite + kill can locate an existing witness, but a crash outside the comparison, + timeout, setup error, or unreached edit is not a kill there. Record that + distinction, not a mutation score. Re-run carried-over survivors when the + baseline revision moves. +3. **Interrogate every survivor.** Find a legal public history and checkpoint + where baseline and mutant predict different observations, or name the + invariant that makes them equivalent. If the observations differ but the + contract permits both, record a **permitted difference** with its contract + source; it is no oracle gap. If that freedom seems undesirable, take the + question to law discovery or adversarial review at the suitability level. + Grade an equivalence claim at a named revision: equivalent by invariant, + equivalent only on instrumented reached histories with a positive control, + or evidence-limited and unresolved. The last is not an equivalence verdict. + Check all relevant entry paths, including reentry and environment-specific + branches; one unreachable path does not establish general equivalence. Probe + the nearest unusual legal history before accepting an unreachability claim. + Treat earlier guesses as hints. Assert the distinguishing premise, then run + the same focused probe on both revisions. A confirming probe for a test gap + passes on the unchanged baseline and fails on the mutant at the intended + assertion. If the contract instead predicts the mutant's result, record + baseline RED and mutant GREEN, then investigate a product bug rather than + calling it a test gap. Remove any temporary in-repository probe and show + clean git status in its checkout. +4. **Repair the oracle chain, when authorized.** Locate the missing law, model + rule, history action, production path, recorder, or assertion. Extend the + primary owner where it can express the distinction. Show that the extension + rejects the motivating mutant and a nearby plausible wrong design without + rejecting a legal alternative. For a bug on the baseline, demonstrate the + production RED before its repair. Update the coverage map when the owner's + reach or limits change. For a claimed oracle repair, follow + [ORC-012](../oracle-rubric.md#orc-012-review-evidence): at closeout, + verdict-critical evidence outside the executable oracle belongs in a + versioned review record tied to the exact reviewed head. This card also asks + the record to name the executable revision of every campaign, witness, and + review-fix run behind its claims. + +**Return:** each chosen mutant's pinned revision, exact edit, observed run +outcome using [ORC-006's categories](../oracle-rubric.md#orc-006-checker-calibration), +public distinction or scoped equivalence argument, and disposition: oracle gap, +product bug, permitted difference, equivalence at a stated grade, or unresolved. +Keep raw run logs and result tables task-local unless the task explicitly asks +to publish them; this does not replace ORC-012's review record when an oracle is +repaired. Permitted difference is a survivor disposition, not a seventh run +outcome. + +**Control:** public observations such as Collection reads, events, transaction +states, or query results decide product claims. Check that the probe reaches +the path and premise it names. A green suite does not prove equivalence; a +surviving mutant does not automatically prove a gap. Use a positive control +when instrumentation reports that a branch was never reached. Recheck types +after a commit hook or other tool rewrites code. + +**Distortion:** mutants chosen to satisfy existing tests, syntax failures +counted as semantic kills, private-state differences treated as product bugs, +an oracle extension that accepts the motivating mutant at the relevant cut, a +negative witness that accepts any failure, or equivalence checked in one build +mode while another mode can observe the change. + +**Stop when:** each selected mutant has an executed distinction, a contract-cited +permitted difference, a revision-scoped equivalence argument, or an explicit +unresolved experiment. Do not infer broad coverage from a kill count. + +The collection-state hunts that inspired this card used larger campaigns. +Their candidate counts and archival format are examples, not requirements. diff --git a/docs/contributing/instruments/tension-scan.md b/docs/contributing/instruments/tension-scan.md new file mode 100644 index 0000000000..a5b7754748 --- /dev/null +++ b/docs/contributing/instruments/tension-scan.md @@ -0,0 +1,40 @@ +# Tension scan + +**Use when:** individually sensible demands appear to collide, or new evidence +may have changed an earlier conflict. + +**Inputs:** the obligations and their sources, the condition in which they +interact, and any earlier formulation. Record which statements are accepted +contracts, user goals, current behavior, or your own inferences. + +## Operation + +1. Establish why each demand matters independently. If only one has support, + return that asymmetry rather than manufacturing two equally sound sides. +2. State the collision concretely: we need A and B, but under C, satisfying A + undermines B through mechanism M. Work through a legal history or design + consequence; a pair of opposing slogans is insufficient. +3. Look for a missing distinction in scope, authority, ordering, ownership, or + checkpoint. Recompute the consequences with that distinction explicit. +4. Check whether both original obligations survive. Removing a guarantee can + be a proposed tradeoff, but does not demonstrate that the tension dissolved. +5. On a recheck, identify the evidence that makes the tension remain live, + sharpen, move, weaken, or dissolve. Read it against the original purpose; + recent or vivid material alone does not justify changing the question. + +**Return:** the sourced demands, concrete collision and mechanism, possible +missing distinction, and current status; or no supported tension, with the +missing support identified. Preserve materially different interpretations when +the available authority cannot choose among them. + +**Control:** restate the case without an A-versus-B framing. Does the conflict +still exist? Keep a generated collision separate from an observed one. + +**Distortion:** invented symmetry, apparent resolution by deleting an obligation, +or using an ordinary clarification to claim a major design discovery. + +**Stop when:** the collision or its dissolution is demonstrated at the stated +scope, or a specific missing fact or design decision is identified. + +Adapted from Field Lab's tension statement, with selection handled by the +repository task rather than an interactive instrument menu. diff --git a/docs/contributing/oracle-coverage.md b/docs/contributing/oracle-coverage.md new file mode 100644 index 0000000000..1db29afe24 --- /dev/null +++ b/docs/contributing/oracle-coverage.md @@ -0,0 +1,6071 @@ +# Oracle coverage and closeout + +## RC npm tag policy, 2026-10-29 + +The maintainer directed RC versions to npm's `latest` tag on 2026-10-29. +`CONTRIBUTING.md` records this policy. The release-history model and command/HTTP +receivers now expect `latest` for candidate and stable uploads. Exact-version +membership still decides whether an upload is needed; an existing version is +never republished to move a tag. Existing `rc` aliases are preserved by the +separate `npm dist-tag add @ latest` operation. + +Historical campaign records below retain the former `rc`-only authority and +its P01 findings. They are not current tag-policy evidence. The existing +histories, comparisons, reach declarations, and run budgets remain intact. +The maintainer explicitly stopped local tests during this release. No RED, +GREEN, or mutant campaign was run for this policy change; execution evidence +is pending CI. The command/HTTP owners do not cover manual tag promotion, +production npm credentials, or Actions authentication. + +## RC scope partition, 2026-10-29 + +The [deferred oracle record](oracle-reviews/2026-10-29-pr-2171-deferred-oracles.md) +names preserved P09/P10/P11/P12 failures, the appended chunk-24 static receiver, +their exact recoverable source snapshot and retained evidence. Their detailed +campaign entries below remain a lossless historical record; those named files +and appended slices are deferred registrations, not active release coverage or +closed laws. Original root metadata and the 24 nullable-facade/public-output +type campaigns remain active. + +For the staged RC publication, the maintainer temporarily removed the duplicate +test matrices and DB report merge from the Release workflow on 2026-10-29. +Release run [40910451885](https://github.com/TanStack/db/actions/runs/40910451885) +succeeded and reported all 20 included RC versions and the two stable versions +already published on npm. The release test matrices and report merge are now +restored. At the maintainer's request, Convex Collection, Preact DB, and React +Router with DB have their public manifests and consumed first-RC release entries +restored too. Their initial manual publications and npm trusted publisher setup +still require verification; restoring publication eligibility does not establish +npm access. +The successful release does not close the counterexamples in +[#2294](https://github.com/TanStack/db/issues/2294), +[#2281](https://github.com/TanStack/db/issues/2281), or +[#2296](https://github.com/TanStack/db/issues/2296), or the reach gap in +[#2287](https://github.com/TanStack/db/issues/2287). + +Use [Oracle Testing Rubric](oracle-rubric.md) when adding or reviewing a +law. This map identifies existing owners, their judgment, and their limits. It +is not a claim that every state or every test has been audited. + +The [project glossary](glossary.md) owns terms shared by these executable +models and their production subsystems. A coverage owner may define narrower +local terms, but it must not silently rename a production concept. + +The [2026-10-10 owner-overlap review](oracle-reviews/2026-10-10-owner-overlap-audit.md) +distinguishes canonical laws from their receiving witnesses across packages. + +## Convex collection integration + +The [October 28 health API migration](oracle-reviews/2026-10-28-convex-health-migration.md) +receives main's #2168 status, health and cleanup contracts. The snapshot owner +now compares public status and health as well as all existing typed row and +error comparisons. An invalid first result ends the sync run; its recovery +starts a new run through `reload()`. After readiness, invalid results retain +rows and report a recoverable failure reason, cleared by a valid result. +Both fixed and random 150-history campaigns reach every declared snapshot +state, including initial rejection and valid-after-error. The offline source +owner also requires reload after its failed first load. This supersedes the +older same-run initial recovery claim below, under #2168's maintainer decision. + +The lazy terminal-graph owner retains every last-row and retired-callback +comparison. Its terminal status is now `crashed`, under architecture law 16. +Its component and actual-backend drivers use internal lifecycle teardown to +exercise that boundary, since law 14 now rejects public cleanup with active +dependents. The component driver additionally checks that public rejection. +Public cascade cleanup ends dependents instead of preserving their graph rows; +core's source-health owner remains primary for that separate law. Synchronous +adapter throws are observed as rejected loads, as law 16 now specifies. +These are explicit contract migrations, not exemptions from row, receipt or +callback comparisons. Provider authentication, reconnect and actual backend +paths retain their receiving suite; the new health comparison itself is at +the controlled callback boundary, not an arbitrary cloud failure guarantee. + +The [October 26 oracle repair record](oracle-reviews/2026-10-26-convex-oracle-repairs.md) +records rejection of five previously surviving wrong designs, enforced per-campaign +reach, and the current replay coordinates. Earlier shrink paths below remain +historical evidence for their named source revisions; changed grammars need the +new coordinates in that record. + +Exact-head follow-up records: [source and backend](oracle-reviews/2026-10-23-convex-source-and-backend.md) and [pagination and codecs](oracle-reviews/2026-10-23-convex-pagination-and-codecs.md), both bound to source revision `6511ee4ed0940cd54ee58ceeba7202ca859344b3`. + +The [public app receiving review](oracle-reviews/2026-10-24-convex-public-apps.md) is bound to test-source revision `4223c39a6d5c2b2e2fedb1a7eee3c0052b5e5013`. The [auth dependency follow-up](oracle-reviews/2026-10-24-convex-auth-dependency-update.md) records patched Auth.js 0.41.3 and Convex Auth 0.0.96 at revision `bae01f788134aa421a9018278ec6d2867519dae3`, with the full final 17-test backend suite passing. + +The Convex package has these independent judgments: + +- `packages/convex-db-collection/tests/snapshot-oracle.property.test.ts` compares + public rows with the latest valid provider array. It covers missing-row + deletion, full-row replacement, reintroduction, empty results, and atomic + duplicate-key rejection, through both raw results and successful select projections. + Constructive prefixes and generated suffixes cross string, finite-number and + mixed keys, including distinct `1` and `'1'`, initial duplicate rejection, + and projections that change key representation. Comparisons retain typed keys. +- `packages/convex-db-collection/tests/ownership-oracle.property.test.ts` owns + concrete options admission separately from snapshot refinement. Its identity + model crosses direct and shallow-copy reuse with idle, ready, and cleaned-up + owners. Reuse rejects before it can replace the first owner's cached mutation + confirmation. Fresh options and separate DbClient descriptor materializations + remain independent, including sibling cleanup and rightful-owner restart. + The original implementation failed admission at seed 20261022/path 2:0:0; + the original two-owner mutation witness settled the first owner to its stale + row. The claim repair passes both fixed and random 60-history campaigns and + direct replay. It follows the existing LocalStorage concrete-options policy. + This owner does not cover arbitrary Collection lifecycle or a real SDK's + multi-listener scheduling. SDK tests receive the cached-listener premise and exercise both real clients with either sibling cleaned up before the survivor receives another update. +- `packages/convex-db-collection/tests/pagination-oracle.property.test.ts` owns + complete finite snapshots across reactive cursor ranges. It varies page sizes + 1-4, forward/reverse callback delivery, row insertion/deletion/movement, and + required/recommended/no split hints. The reference is the latest input array; + the provider fixture independently answers numeric rank intervals. Comparison + retains every select and public Collection publication, permits repeated complete + snapshots, and rejects partial snapshots or a return to the prior revision after + the new revision appears. Pending ranges retain the prior complete snapshot. + Split cursors stay inside their returned page ranges. The accepted-row bound + rejects over-cap results without partial publication; the omitted-option + default accepts exactly 10,000 rows and rejects 10,001. The default-cap + boundary, invalid `maxRows` rejection, and cleanup-failure fidelity are fixed + tests in `packages/convex-db-collection/tests/pagination.test.ts` (fixed tests; + no oracle owner (tracked in #2264)). + Its initial-materialization work law also varies 3-40 pages and 1-8 rows per + page. The original implementation copied incomplete prefixes and failed at + seed 20261022/path 0:0:0:0. The repaired fixed/random campaigns copy each + provider row once at first exhaustion. Cache scans still grow quadratically + with page count during sequential acquisition, this is not a total-work bound. +- `packages/convex-db-collection/tests/offline-oracle.property.test.ts` owns the + bridge's ordered argument/mutation/sync stages, durable key propagation, and + terminal application-error classification. Argument and mutation failures + reject with NonRetriableError; post-confirmation sync failures can retry. + The original argument-error path failed this oracle at seed 20261022, path + 5:1:1. The repair includes argument construction in terminal classification. + Rejected source proposals now follow both initial and already accepted + empty/nonempty bases, including later insert/update/delete/replacement, + unchanged/changed cache retries and cleanup/restart recovery. + +- `packages/convex-db-collection/tests/values-oracle.property.test.ts` owns the + Convex receiving check for native scalar types, bytes, nested containers, and + removed fields across durable storage and fresh readers. Real Collection CRUD + mutations supply distinct original/modified operands, equal-payload and changed + updates, and removed-field changes; each operand is compared independently. + +Each owner runs matching fixed and random campaigns and accepts explicit +seed/path replay through their CONVEX_*_ORACLE_SEED/PATH environment variables. +Each normal lane has a fresh reach ledger, fails if any declared state has fewer +than one reached history, and prints its counts as `ORACLE_REACH` JSON. Explicit +path replays run directly with one selected fast-check run and bypass campaign +minima; shrink-path reconstruction can evaluate the callback more than once. +Snapshot replay uses CONVEX_SNAPSHOT_ORACLE; pagination has separate CONVEX_PAGINATION_ROWS, CONVEX_PAGINATION_WORK, and CONVEX_PAGINATION_BOUND selectors; +offline bridge replay uses CONVEX_OFFLINE_ORACLE; native value replay uses +CONVEX_VALUES_ORACLE; ownership uses CONVEX_OWNERSHIP_ORACLE. + +`packages/convex-db-collection/e2e/backend-oracle.test.ts` receives these contracts +at an actual isolated local Convex backend using both Convex 1.47.0 SDK clients. +Its row-bound rejection is a fixed test in +`packages/convex-db-collection/e2e/backend.test.ts` (fixed tests; no oracle owner +(tracked in #2264)). +It compares pagination with a separately evaluated unpaginated server query, +including real server-generated split responses. Its durable driver runs fixed +and random insert/update/delete histories through SIGKILL after disk admission and, optionally, +a second SIGKILL after server acceptance but before recording outbox completion. +It requires no offline provider call, restored Collection rows, and one server +receipt after replay with the original key and the expected final server rows. +Native payload variation covers inserts and updates. A separate non-idempotent application counter must equal one, and the receipt key must match the on-disk durable key. Restored Collections are compared on every application field, including native payloads. Atomic batches and multi-tab leadership +remain outside this backend witness. Replay uses +CONVEX_BACKEND_INSERT_ORACLE_SEED/PATH, CONVEX_BACKEND_UPDATE_ORACLE_SEED/PATH, or CONVEX_BACKEND_DELETE_ORACLE_SEED/PATH, with matching operation test selection. The command owns backend startup and shutdown. +The offline engine's interruption, settlement, storage, and leadership owners +remain primary for those laws; this is their Convex receiving witness. +Generated bundles constructively cover known and uncertain acknowledgement for +each native payload class while retaining six actual process histories per +operation per lane (36 total). Reach is credited at observed disk admission, +server-acceptance and full replay comparisons, not from requested fixture inputs. + +`packages/convex-db-collection/e2e/public-pagination-oracle.test.ts` receives the +finite pagination contract through the actual public `get-convex/convex-demos` +functions at revision `2fac21e360bfcceae6065096f1b81480bd8c6fd5`. Its independent +append ledger predicts descending message order, indexed author membership, and +uppercase/author-initial projections. Both SDK clients exercise all three query +forms at page size three, live appends, real TCP connection loss, direct writes +while disconnected, and complete recovery after reconnect. Unique authored +bodies supply the stable key omitted by the transformed query. It observes +every ordered input to select and public Collection publication against complete +authored ledger revisions allowed in that phase, plus final source-acceptance rows. +Future ledger entries cannot retroactively justify an earlier observation. +This receiving relation checks complete available revisions; it does not check +monotonicity within one phase. The primary pagination owner checks that law. +This is six authored receiving histories, not a new primary pagination model or +an arbitrary transformed-key guarantee. Setup fetches the pinned source at run +time, with no upstream application source redistributed in this repository. + +`packages/convex-db-collection/e2e/auth-app-oracle.test.ts` receives the snapshot +contract through the official role-based Convex Auth app at revision +`45f08008e1257758560948db4280cc4e7d64e84a`. The app's schema, message functions, +permission checks, and role updates come from that pinned source. Setup changes +only the external sign-in providers to the official Anonymous provider, with +ephemeral signing keys and real issued JWTs. An independent set-valued permission +policy drives all six read/write/admin operation cells, including demotion and repeated equal-body sends, while a multiset of +authored message bodies predicts the public rows at each accepted source +checkpoint. Both SDKs must reject an unauthenticated Collection preload; an +invalid-token HTTP query is a separate authentication control. The app deliberately +allows self-service role selection, so this does not establish secure role +administration. The two authored receiving histories do not cover OAuth redirects, +email delivery, token refresh, browser auth components, or production hosting. +Both public-app fixtures deploy with this package's installed Convex 1.47 SDK. +The pagination app's upstream package declares 1.48, its business functions are +unchanged here but its original dependency installation is not being tested. + +`packages/convex-db-collection/e2e/backend-startup.test.ts` (fixed tests; no +oracle owner (tracked in #2264)) covers the receiving fixture's occupied-port +startup judgment. It holds the former fixed +pagination port, invokes the actual pinned public-app setup, and requires a +different loopback URL serving a fresh empty query. Expected availability comes +from an independently held listener, not a copy of CLI selection logic. An +already occupied port is also a valid precondition, and its external owner is +never closed. All three fixture setups delegate default port selection to the +supported Convex CLI, an explicit user `CONVEX_TEST_PORT` still requires that +port. This is a fixed native startup witness, not a concurrent-allocation proof +or a promise to start when no free ports exist. A CI run reproduced the original +mandatory-port failure at auth port 43330 before any tests ran. The [startup follow-up](oracle-reviews/2026-10-24-convex-backend-startup.md) records exact source revision `4b37df090402469cafed0cf9f5e16b9528dae60f`, the rejected fixed-port control, and the final 18-test run with the former auth port occupied. + +`packages/offline-transactions/tests/transaction-serializer-oracle.property.test.ts` +remains the primary wire-format owner. Its opt-in codec grammar crosses +insert/update/delete and arrays of signed 64-bit integers, special floats and +binary buffers in modified/original/changes. It uses an independently supplied +V8 codec, recreates the reader, and checks missing/mismatched/throwing codecs +through both get and getAll while preserving the exact stored bytes. +The codec grammar uses real CRUD with independently authored before/after +payloads, changed and equal-payload updates, and removed fields. Native class +and operand distinctions have enforced measured reach in each campaign. +Default unversioned/v2/v3 histories retain their prior expectations. A version-4 record +requires its codec, and configured readers continue to accept old formats. +The original serializer failed at durable admission for seed 20261022/path +0:0:0:0, a single BigInt insert. The repaired codec path preserves the values. +The Convex values receiving owner, +`packages/convex-db-collection/tests/values-oracle.property.test.ts`, checks +bounded nested native trees, marker-like objects and undefined fields against +an explicit type/byte observation. The backend process witness also varies +native payloads. Transaction metadata is intentionally outside codec dispatch. + +The source-snapshot mutant that omits deletion and the partial-row mutant both +fail the snapshot owner at public row comparison. Disabling automatic continuation +or publishing an incomplete required split fails the pagination owner at public +row comparison; replay seeds and shrink paths are recorded beside the property. These bounded tests do not prove +continuous unbounded growth, cross-Collection atomic publication, cloud service +availability, or power-loss atomicity during a filesystem write. The test backend +has no application authorization; production authorization and atomic receipt +storage are requirements of each application's server mutation. + +The October 23 review adds real-Collection source-acceptance histories to the offline bridge owner: idle, pending first result, failed startup, provider failure, and cleanup. Successful empty or nonempty snapshots must actually be accepted. Original idle acceptance fails seed 20261023/path 0:0:0, the repaired fixed/random campaigns and direct replay pass. The same file keeps its ordered-stage model separate from the source-settlement and rejected-acceptance judgments. A rejected durable acceptance stays terminal for that sync run, same or changed cached rows cannot falsely certify a retry, and cleanup/restart with a fresh accepted snapshot recovers. The nonterminal mutant fails seed 20261023/path 0:0 under CONVEX_OFFLINE_ACCEPTANCE_ORACLE_SEED/PATH. Ordinary provider errors retain same-run valid-result recovery. Dedicated codec replay uses OFFLINE_CODEC_ORACLE_SEED/PATH/RUNS; legacy serializer properties keep OFFLINE_ORACLE selectors. + +The paginated factory now also supports `syncMode: 'on-demand'` for one fixed +query with an explicitly aligned finite numeric leading order. The +[lazy paging implementation record](oracle-reviews/2026-10-26-convex-lazy-paging.md) +records the approved law, semantic RED/GREEN, review repairs, checked mutant +replays, source hashes and limits. `tests/lazy-paging-model-oracle.ts` owns the +independent exact-window, complete-leading-tie, delivered-evidence successor-work +and public-publication comparisons. `tests/lazy-paging-oracle.property.test.ts` +runs five fixed/random 60-history families through real Collection controls and +ordered live queries. Each current reach state was observed in all 60 histories +per campaign. It covers finite/offset/whole/tie/continuation/empty scopes, +short nonterminal pages, both splits, no-demand/zero/growth/cached-peer work, +rejected acquisition/unload, held initial/moved/repaired windows, cache-before- +callback refetch, abort before/after acceptance, held cleanup/restart, and two +real applied receipts across publication reentry. Duplicate pending request +objects retain both waiters. An invalid lazy startup never publishes readiness. +Malformed order declarations are fixed tests in +`packages/convex-db-collection/tests/lazy-paging.test.ts` (fixed tests; no oracle +owner (tracked in #2264)). + +The [October 27 strengthening record](oracle-reviews/2026-10-27-convex-lazy-paging.md) +supersedes those historical source bindings for the current files. The native +`lazy-paging-history-oracle.property.test.ts` adds generated acquisition, +retirement, abort, delivery, source-change, application-hold, cleanup and restart +actions. Its constructive prefix retires or aborts the wider authorizer while +work remains pending, then cleans up with a real accepted receipt held. Both +SDK subscription shapes receive stale success/error callbacks. Every source +publication is checked against the provider facts available at that publication; +optimistic overlays are excluded. Paired hidden suffixes compare actual discovery +traces before revealing the suffix. Retain/reacquire alternatives are positive +scientific-comparator calibrations, not claims about native retention strategy. + +`lazy-paging-receipt-model.ts` independently owns the exact many-to-many +attempt/establishing-receipt relation. Its controlled real-Collection receiver +varies response groups and commit partitions independently, checks cached zero, +one/shared/multiple receipts, held idempotent writes, true with zero/one/multiple +synchronous receipts, Promise rejection and synchronous throw. Six faulty +adapters distinguish borrowed receipts, first-only waits, skipped provider +completion, content-only application, premature canceled settlement and a +quiescent stranded caller. Native lifecycle checks trace every source/dependent +publication and status transition during stale injection at cleanup start, +held cleanup and restart; a fresh dependent succeeds while the old one remains +terminal. A stale provider-error RED preceded the subscription fencing repair. + +The [interleaving follow-up](oracle-reviews/2026-10-27-convex-lazy-interleavings.md) +adds four receiving families and supplies the current source binding for the +shared receipt model/driver. `lazy-paging-interleaving-oracle.property.test.ts` +varies legal response delivery, actual acceptance/application, and cancellation +orders independently. An authored uncancelable in-flight obligation distinguishes +abort before acceptance from withdrawal of already accepted work. Previously +initiated work may establish a receipt after abort. The generic core model +permits fulfillment after abort once facts, obligations and receipts apply; +native/controlled receivers separately check their cancellation outcome policy. +Borrowed/first-only receipts and premature in-flight cancellation fail promised +observations. Extra waiting on a future peer receipt is contract-permitted; +the earlier injected-edge control is not claimed as a rejected waiting strategy. + +`lazy-paging-native-receipt-oracle.property.test.ts` feeds actual native batches +and core receipts into that same scientific model. Independently authored +required versions absent at acquisition have unique supplying batches, which +can establish two callers. It covers cached zero new edges, a shallow cached +caller behind a deeper held peer, canceled/successful shared callers, and explicit +cache revalidation to a new version. Early or delayed cached settlement is +permitted; responsive post-drain completion is required. Its hostile native +receiver returns the already applied startup receipt instead of the actual +held receipt. Native attribution here excludes idempotent/superseding facts, +reentrant commit observations and cleanup abandonment; the older controlled +receiver retains idempotent receipt coverage and its all-responses-first profile. + +`lazy-paging-version-model-oracle.ts` owns coherent authored revisions and their +monotone public frontier. `lazy-paging-version-oracle.property.test.ts` checks +every source publication after full lazy acquisition, with multiple accepted +revisions held behind real optimistic transactions, both SDK shapes and skipped +intermediate revisions. Old complete values may remain while held; rollback +and mixed versions fail even when final values are correct. The two hostile +controls inject wrong public observations; they calibrate the recorder/model, +not a production pager mutation. This profile keeps keys/order stable. General +membership changes and partial-prefix retention retain their existing owners. + +`lazy-paging-failure-history-oracle.property.test.ts` composes preacceptance +failure, two owners' shared held batch, cancellation, peer retirement, provider +error, held cleanup, stale callbacks and restart. Every old dependent value and +status event is checked for terminality; a fresh graph succeeds. Ordinary +native errors may recover on a valid coherent result. It does not require an +ordinary rejected attempt to wait its receipts. Its terminal controls calibrate +the model; production stale-callback mutants remain in the prior repair record. + +Core `tests/query/load-subset-joint-replay-oracle.property.test.ts` receives the +authoritative truncate law through real source commits and two live owners. +It composes older uncancelable participants, held receipts, failure followed by +ordinary writes, owner retirement, retry and held cleanup/restart. Newest-only +publication and ordinary-write reopening production mutants fail the public +snapshot comparison. Native Convex does not issue same-run authoritative +truncates; these are distinct receiving profiles. Scientific models hold facts, +obligations and allowed publications; transport/pager mechanics stay in drivers. +No bounded campaign establishes arbitrary nonquiescent temporal progress or +every formal schedule product. + +`e2e/lazy-concurrency-oracle.test.ts` now receives the same receipt and version +models at the actual isolated Convex backend through both real SDK clients. +Each generated history runs both SDKs, with independently authored stable +rows/ranks, varied page sizes/text, shared full acquisitions, real optimistic +application holds, post-acceptance cancellation and a cached shallow peer. +Before cancellation, unique required facts establish the actual shared held +receipt and both callers' missing-fact sets are empty. Cached extra waiting is +permitted. After full acquisition, atomic test-server full-relation mutations +offer revisions before writing; two distinct accepted revision witnesses are +held before application, and the independent writer advances during reader TCP +loss. Every active source publication feeds the unchanged version/provenance +comparisons. Actual callback snapshots must contain the drained and reconnected +revisions; manual checkpoints cannot supply that reach evidence. Actual receipt +fulfillment supplies application, even when a newer FIFO revision is already +public before the Promise callback runs. The normal family has matching fixed +and random four-history campaigns, with both SDK receivers in every history; +`CONVEX_BACKEND_CONCURRENCY_ORACLE_SEED/PATH` replays directly. Removing applied +waiting and dropping the first updated row are production mutants rejected by +the receipt and version laws respectively through both SDKs, with shrink/replay +and byte-for-byte restoration. The [backend concurrency record](oracle-reviews/2026-10-27-convex-lazy-backend-concurrency.md) +binds sources, commands and results. + +This backend receiver preserves the stable-key, fully acquired revision profile. +It does not claim arbitrary acceptance/application orders, idempotent receipt +attribution, accepted cleanup abandonment or native application/provider errors. +TCP loss tests transport recovery; it does not promise a provider-error callback. +The broader combined failure grammar retains its controlled receiving profile; +the boundary receiver below adds the after-acceptance query-error/cleanup subset. +Other real backend source/window mutation and framework boundaries retain their +existing owners. + +The [boundary-strengthening record](oracle-reviews/2026-10-27-convex-lazy-boundaries.md) +extends these receiving profiles without importing pager mechanics into the +scientific models. `lazy-paging-revalidation-oracle.property.test.ts` attributes +actual native commits made inside isolated explicit revalidation calls, including +equal-valued accepted writes and later supersession. Actual receipt fulfillment +proves application; a historical value need not remain visible at its Promise +callback. Cached reuse may accept another receipt or owe zero new receipts. +Every history exercises both controlled SDK shapes, then an unheld reactive +revision, with callback-only publication witnesses and retained semantic errors. +This is not evidence that a real Convex backend emits equal-valued query results, +nor general attribution of asynchronous/reentrant native receipt edges. + +`e2e/lazy-boundary-oracle.test.ts` receives ordered windows computed by +`expectedLazyRows` and the sequence-observation mode of `LazyVersionModel`. +It never compares the whole retained partial source to a full server revision. +Every base publication separately receives the real SDK provenance judgment. +Generated histories keep an actually unread suffix while atomic server edits +insert, delete and move rows across the window. Window growth remains pending +on a real held application receipt while two or three later revisions arrive. +The same episode then composes two full callers' shared held acceptance, caller +abort, a genuine reactive server query error, cleanup-abandoned actual receipts, +and a fresh graph while the old graph stays terminal. `LazyReceiptModel` now +distinguishes pending, applied and cleanup-abandoned receipts; cleanup authority alone +does not discharge an unobserved receipt. The shared `LazyTerminalGraphModel` +supplies the old graph judgment to both controlled and backend receivers. + +Both new families run matching fixed/random campaigns, direct seed/path replay, +and both SDKs in every history. Selectors are `CONVEX_REVALIDATION_ORACLE_SEED/PATH` +and `CONVEX_BOUNDARY_ORACLE_SEED/PATH`; `CONVEX_LAZY_ORACLE_RUNS_MULTIPLIER` +expands both campaign budgets equally. Standard package oracle scripts discover +them; the existing browser CI lane runs the backend receiver via `test:e2e`. +Calibration distinguishes unchanged-content application shortcuts, lost native +deletions, and a separate stale-publication perturbation that ends with correct +rows. Exact commands, source hashes and outcome classifications are in the +record. This does not close arbitrary scheduling, rank ties in the new backend +family, provider errors before acceptance, adversarial stale real-SDK callbacks, +same-run authoritative truncate, or React rendering boundaries. Those retain +their existing owners; the controlled failure family remains broader than its +new backend receiving subset. + +The [application/cancellation follow-up](oracle-reviews/2026-10-27-convex-lazy-app-abort.md) +adds three receiving families motivated by issue #2269. `LazyWriteModel` in +`tests/lazy-paging-write-model-oracle.ts` independently owns the authored backend +ledger and the composition of source revisions, requested windows and one +optimistic operation. Backend acknowledgment and optimistic settlement are +different boundaries. The publication relation retains every compatible +monotone source/window explanation; rejecting an optimistic operation may reveal +an older applied source without permitting source rollback after a newer view. +Its comparator controls accept that legal history and reject atomic mixing, +source rollback, lost confirmed writes and remote effects of atomic rejection. + +`e2e/lazy-app-write-oracle.test.ts` receives that model through both actual SDKs +and the isolated backend. Each history includes confirmed/rejected Collection +insert, update and delete handlers, independent visible/unread remote edits, +demand growth behind a real held receipt, and an unread suffix. Successful +handlers must expose the correct source key at application settlement while +real SDK caches are current and notifications are withheld. Every graph +publication is judged; the backend is independently checked against the ledger, +and drained windows against its exact projection. Rejected server transactions +throw after attempting their writes, exercising real atomic rollback. This is +not a promise to undo arbitrary handler side effects after a remote commit. +Limits: unique ascending numeric ranks, one application operation at a time, +bounded strings, no rich-value/reload/health API coverage. + +`tests/lazy-paging-load-outcome-oracle.property.test.ts` compares the same +generated failed-replay/ordinary-write/retry history under synchronous throw and +Promise rejection, through direct subscriptions and live queries. It checks +original error identity, every public value, failed-publication retention and +successful recovery. The comparison permits their different acquisition lease +lifetimes. Initial-load equivalence and arbitrary reentry retain the core +lifecycle owners; this new family claims the replay boundary only. + +`tests/lazy-paging-abort-replay-oracle.property.test.ts` receives preacceptance +cancellation through both controlled native SDK shapes, followed by two real +same-run core truncate replays with disjoint source keys. Abort settles before +withheld delivery; no-demand delivery accepts no rows. Retired success/error +callbacks after each successful replay cannot resurrect rows, poison current +sync or read a retired React watch. Native Convex does not initiate these +truncates; the driver supplies that core input. This is no claim about actual +SDKs emitting adversarial stale callbacks or about bounded work during replay. +The existing receipt model still owns accepted writes. This checkout's older +core waits for uncancelable baselines; #2269's immediate-core-abort decision +belongs to integration with unmerged #2168. The new family exercises their +shared cancellable-adapter obligation without silently changing either law. + +All three families run fixed/random campaigns with direct seed/path replay and +declared reach. The follow-up records three rejected production decisions +(early handler confirmation, swallowed throws, missing retirement fencing), +a misdirected-write driver calibration, shrunk replays and restored GREENs. + +`tests/sdk-cache-oracle.test.ts` supplies both real SDKs' coherent cache-before- +callback premise through legal controlled multi-query wire transitions. +`e2e/lazy-paging-oracle.test.ts` receives shared value/coverage/work judgments +through both SDKs and the isolated local backend: unread finite tails, window +movement, live delete/move, real required/recommended splits, native leading ties +with a local trailing order, and same-run TCP reconnect. Native work checking is +limited to stable-source acquisitions; mutation/reconnect phases compare values +and coverage at settled cuts. `packages/react-db/tests/convex-lazy-paging-oracle.test.tsx` +receives exact data, every page, page parameters and lookahead through +`useLiveInfiniteQuery`, checking every committed ready tuple, across provider +page sizes 1–4. Other framework receiving paths are untested under these owners. + +The eager mirror and retained lazy prefix default to maxRows 10,000. Retained +ranges stay subscribed until cleanup; this is no network-byte, peak-memory, +total-call, scan or lifetime subscription-count bound. Whole-source requests +may exhaust the query. Core can issue such a request after source restart even +for a terminal dependent: the graph remains terminal but its source subscription +can survive. The cleanup driver accounts for that actual demand. Terminal graph +publication is covered; zero post-error acquisition is not promised here. +General query translation, arbitrary application filters/orders, nullable/string +leading orders and array select in lazy mode are excluded by the public profile. +An incompatible external truncate wrapper is not a Convex reset mechanism. +The existing core load-subset replay, transaction, ordered lifecycle and cleanup +owners remain primary for replay/failure gates and delayed cancellation. Their +458-test delegated run is distinct from native Convex coverage. Responsive- +provider quiescent stalls fail a named progress comparison; continuously +nonquiescent work remains a diagnostic limit. The frozen TLA+ design projections +are analytical evidence, not proof of this production implementation. + +Validation at the initial review: 43 adapter checks, 302 offline-engine tests, and seven real-backend tests passed. The October 23 strengthened backend suite passes nine tests with 36 generated crash histories. Final local validation passes 62 adapter checks and 302 offline-engine tests. Exact-head review records accompany this follow-up. The native-value receiving oracle rejects a +scalar-as-JSON decoder at the typed-value comparison (seed 20261022/path 0:0); +its repaired replay passes. `pnpm size:compare --packages +convex-db-collection,offline-transactions --files` measures 2,863 gzip / 2,594 +brotli bytes for the new adapter's combined exports, with dependencies external. +The shared outbox grows by 192 gzip / 174 brotli bytes. Kyle review repairs add 230 gzip / 213 brotli bytes over the published PR head for actual first-source acceptance and the accepted-row bound. Existing tree-shaken app scenarios remain unchanged. Reusing the sync config materialization hook avoids a parallel startup lifecycle, and the existing page loop enforces the bound without a second pager. A new adapter has no +baseline to subtract; normal and paginated sources share one snapshot writer, +and the existing outbox supplies durability instead of a second write queue. +The codec hook is needed to preserve native values without changing default +JSON semantics. Review fixes add 74 gzip / 69 brotli bytes over the initial +adapter implementation, paying for concrete-options ownership enforcement and +deferring row materialization. Tests and documentation are separate from these +shipped production measurements. + +## Scope of the oracle repair project + +[Issue #1808](https://github.com/TanStack/db/issues/1808) commissioned a bounded +portfolio audit and repair. The September 11 inventory contained 329 tracked +paths: 132 selected/support entries and 197 discovery-tier files. The latter +received routing recommendations, not 197 full semantic reviews. Focused +examples, type tests, and host-wiring tests remain useful; converting them all +to generated tests is not a completion criterion. + +## Literate model audit + +The literate-model pass audits every primary executable owner in +[Find an owner](#find-an-owner) and every repository file explicitly named +`oracle` or `property`. Derive that strict inventory from the repository rather +than copying a count into this document: + +```sh +git ls-files -co --exclude-standard packages \ + | rg '/[^/]*(oracle|property)[^/]*$' \ + | sort -u +``` + +The command includes tracked and untracked package files so an in-progress +audit cannot hide a new owner. A surface is complete only when each primary +owner states its contract, model, history grammar, production driver, and +refinement check—including its public observations and checkpoint—in the +executable file. Focused regressions outside that inventory may remain short, +but a file named `oracle` or `property` cannot use that allowance to omit the +literate structure. + +The pass must not change product behavior. If clearer prose exposes a missing +model rule or assertion, strengthen the oracle and run it against unchanged +production first. Track any production failure as separate follow-up work. + +The same pass audits vocabulary. Shared concepts use the production names in +the project glossary. The audit corrected model prose that collapsed a physical +acquisition into a request or called an acquisition lease merely a lease. +Model-only terms such as an appointment ledger or fault tape remain local and +say what production facts they abstract. This is a semantic review, not a rule +that test identifiers must copy production's private data structures. + +| Surface | Status | Completed or next owner | +| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Ordered relations and BTree | Complete | The signed top-K relation, BTree/Map refinement model, and DBSP incrementalization laws are literate. | +| D2 operator work | `packages/db-ivm/tests/d2-work.test.ts` (fixed tests; no oracle owner (tracked in #2264)) | A finalized 65-branch graph receives one direct input at the first, middle, or last branch, then a second input on a different branch. The test compares exact forwarded messages and operator invocations after each `run()`: branches without queued input perform no operator work. A connected reversed-registration pair must drain in the same run, and an empty unfinalized graph rejects. The old unconditional loop fails with 64 idle calls per turn. This bounded work check does not establish elapsed latency, fused operators, arbitrary graph topologies, or provider-level live-query work. | +| Includes and publication | Complete, [facade rollback](https://github.com/TanStack/db/blob/main/packages/db/tests/query/bucket-facade-rollback-oracle.property.test.ts) ([review](oracle-reviews/perf-lazy-bucket-rollback-copy.md)) | The central recomputation model plus cross-formulation, temporal demand, layered publication, Collection facade lifecycle and space bounds, route-context transport, functional projection, query-shape, optimistic, and source-work owners are literate. They keep the architecture document as their contract source. A failed facade flush restores every facade it wrote, disposes the facades it created, publishes no event or layout revision, and reads the stored rows of no other facade; nested facades across two edges have a pinned witness. The grammar adds optimistic facade inserts and optimistic deletes of shown graph rows; a retired facade must show exactly its visible optimistic rows, and every history ends by settling them and comparing all rows again. A facade commit that throws before committing is generated, and the restore must commit the open sync transaction. Every change pending at a failed root commit, child rows included, stays pending, and the next flush publishes it once. Open: the flush's edge and bucket map copy is unmeasured bookkeeping; disposal between a flush and its rollback has pinned witnesses only; held facade commits are generated one hold at a time for up to three flushes, and a release that covered earlier flushes checks rows only; the grammar also cleans up a shown facade from outside, lets optimistic rows share graph ids and later rolls them back, after which those keys are compared by value; a holder's cleanup of a shown facade rejects (code 241) and the facade keeps receiving the graph's writes; retiring a shown bucket without the graph's retractions is generated (`contradict`) and throws code 240; optimistic deletes and updates of shown graph rows are generated, and an update's copy keeps the row's key; a facade that `resolve()` created before activation can stay ready and empty after a failed flush (recorded limit). | +| Collection lifecycle | Complete | The shared logical-owner/acquisition-attempt/sync-run grammar plus mutation admission, lifecycle trace, publication, replay, disposal, and transaction-refinement boundaries are literate. The lifecycle history driver checks law 13 at every command: an attempt that settled without an abort keeps no core abort listener on its signal (RED on d0cde4778 in 36 histories; a mutant that keeps the replay participant's listener fails 33). `parkedReloadDemandCases` drives a demand parked behind a failed first load through a reload whose new run marks ready, commits a snapshot inside `sync()` before or after `markReady()`, or commits it after the first load settled: one `loadSubset` per replay attempt that starts work and one `unloadSubset` per replaced acquisition (RED on d0cde4778 for `ready-then-snapshot`: two loads and one unload, because the restart joined the attempt before the attempt's own setup ran; a mutant without the per-attempt check fails it). Limits: these are four fixed histories, not generated; a second reload and asynchronous failures are outside them. | +| Collection status and health tree | `packages/db/tests/source-health-oracle.test.ts` (`source-health.history`, `source-health.required-history`); the `problem values`, reload invariant, request error, crash recovery, failed join load, root request, Effect, pooled view, and include node lanes are in `packages/db/tests/source-health.test.ts` (fixed tests; no oracle owner (tracked in #2264)) | An independent per-Collection model of the first-load gate, `failureReason` and stopped `error` slots with serials and each slot's frame of failures (maintainer decision: a problem shows its newest failure and lists its frame in `errors`; a failure reason's frame is its sync run's reports until `reportHealthy()`, once per error value, less each failure `reportHealthy(error)` removed, and a reload's new run that reports starts a new one), crashes, and reloads derives each live query's status and expected health tree for direct, join, self-join, nested, include, and pooled-nested shapes. The additive `source-health.required-history` lanes execute all nine existing reach prefixes with generated suffixes, in join/nested contexts with direct subscribers. Each fixed/random lane independently enforces all 26 reach states; the original 60 random histories retain six shapes and both subscriber choices. Generated histories cross `ready`, `fail`, `report`, `healthy`, `crash`, `reload`, `reload-cleanup-fails` (a retired cleanup that rejects crashes the Collection), `reload-cleanup-held` and `retired-cleanup-rejects` (a retired run's cleanup that rejects later still crashes the Collection, also after a cleanup and a fresh start: the adapter broke its contract for that Collection, by maintainer decision), `cancel` (a commit with an aborted signal changes nothing), `reload-query` (a query's `reload()` reaches every Collection below it that shows an `error`, through nested queries; a crashed query rejects without touching its sources), `cleanup-reload` (cleanup, a fresh start, and a reload in one tick: the cleaned-up run's reload decides nothing for the new one), `snapshot` (`begin({ snapshot: true })` replaces every row at its commit, at any time, also when the reload fails or the Collection crashes before that commit; maintainer decision: only a snapshot or the adapter's `truncate()` replaces rows, and every other write of a reload's new run applies on the kept rows), `mark-ready` (a reload without a snapshot completes at the new run's `markReady()` and keeps the rows; a run's `markReady()` after it is ready or after its `fail()` does nothing, by maintainer decision), `reload-ready-throws` (a first-ready callback that throws while a reload's new run marks ready inside `sync()`, also when the reload starts a cleaned-up Collection through its preload, is reported uncaught and the Collection stays ready, by maintainer decision), and restart. A second `fail()` in a sync run does nothing. A `problem values` lane checks that `fail()`, `reportError()`, and a crash behave the same for any value, also one that cannot describe itself. The Effect lane checks that a problem a source already shows when the Effect's gate opens reaches `onHealthChange` once, that a healthy gate announces nothing, and that each later change is one call. The `effect healthy gate` lane crosses where the gate opens: at the Effect's start, inside an eager source's, a nested query's, or a join's second source's `status:change`, or when an on-demand load settles. A healthy gate that announced its own tree for the status change that opened it failed the three `status:change` openers; an Effect that skips its first healthy change fails the on-demand opener. The lazy grammar's `reload-in-load` checks that a reload from inside the adapter's `loadSubset` rejects and leaves the query running. A query-db receiving witness checks that a direct insert, update, or delete during a reload applies on the kept rows, because it is not a snapshot, and ends with the same rows as during a refetch in flight. A second query-db receiving witness, `housekeeping during a reload` (`query-db.reload-housekeeping`), checks that Query Collection's retention housekeeping keeps the rows during a reload of an eager persisted Collection: an expired persisted retention entry is removed by its expiry timer or by the new run's startup maintenance while the new run's Query fetch is held, and the rows stay, the reload stays in flight, and it ends with the same rows as a refetch. A fourth kind, `expiry-timer-lagging-clock`, makes the expiry timer fire before `Date.now()` reaches `expiresAt`; the entry must still expire in that run, and a callback that drops the early timer fails every time (this was a CI flake and a production bug). The reach counts only begins of the run after the retired one, and the window is observed by polling until the entry is gone. A housekeeping `begin()` marked `snapshot: true` fails it: it replaces the kept rows with none. A third kind, `release-at-startup`, reaches the retention entry that a subset release writes: during a reload of an on-demand persisted Collection, the driver requests and releases a fresh subset through a change subscription in every microtask turn until the new run's `markReady()`, and in the turns after its startup retention maintenance settles each release writes its entry while the reload is in flight; the rows stay and the reload stays in flight in every turn, and it ends with the same rows as a refetch. A retention write marked `snapshot: true` fails it. Release of a Query's rows also begins a housekeeping transaction, but no legal history begins it in the new run while the reload is in flight: core runs the run's cleanup only after retiring its callbacks, and a release takes that path only without a persistence capability, where Query Collection writes no retention entry, so the new on-demand run calls `markReady()` inside `sync()`; a snapshot there is an equivalent mutant for the lane. The lazy grammar's `deferredStart` dimension starts the child the way a persisted Collection does, after the query asked for data, so every lazy history can begin with a first load that enters the adapter from the deferred start; with `reload-in-first-load` that load calls `reload()`, which must reject (a deferred call that skips the guard fails the generated property, shrunk to the empty history). The lazy join grammar checks which request error the query's node shows after every step: every failure has its own message, and the model shows the newest open failure by the order failures became request errors (maintainer decision). It also compares the node's `errors` with the model's frame after every step: every open request failure once, oldest first, each the very value the adapter failed with; a shared error keeps the place of its first failure, and a failed replay lists its failures in failure order, with the newest on top (maintainer decision; the subscription's own replay error stays its earliest held failure). The window grammar makes the same comparison. A replay's verdict decides every request error of the demand it replayed (maintainer decision: coarse verdict): loads that settle while it is judged decide none themselves, its success clears them all, and its failure replaces them with its failures, every failed load of its current attempt that the query still holds for a key that no later held load loaded. Closed from 30-times runs, each now a named history: a query's reload passes through a nested query that a cascade ended, which is a Collection below it that shows A's error, so that inner query's own reload reloads A and starts it, also when another source's reload rejects and the outer query does not start (`a query reload starts an ended nested query even when another source rejects`; the model had reloaded A past the ended inner query). A synchronous throw from `loadSubset` is a rejection (maintainer decision): the lazy driver's `throw-next` failure holds its demand, so the next demand change or replay requests its key again, and the churn replacement twins (`a churn replacement that throws | rejects leaves its key to the older load after the next demand change`) must agree; the driver sees the abort that releases a thrown load's demand. A replay load the query released does not count for the replay's verdict, even when it loaded its keys before the release (law 9: the replay succeeds only as a replacement for the demand that remains; `a released replay load's success does not count for the replay's verdict`; model). A failed replay request that the query released holds nothing, so a parked demand change that asks for its key again parks it (law 9; `a parked growth asks again for a key whose released replay request failed`; model). The main grammar projects every problem's `errors`(messages, order, and which report each cause is) and rejects a problem whose`message`, `cause`, or `at`is not its newest failure's; it checks that a legal`report`or`healthy`announces one`health:change`(a self-join announced two, one per alias, before the relay was registered once per Collection). The demand model treats a replay's failed request of a parked acquisition, which never loaded, as that demand's own failed first load: it covers nothing, and the next demand change parks a retry that holds the failed replay open (law 10; a 30-times counterexample on b521d29d9 of the model, which let the request cover the key, pinned as`a demand change retries a parked key whose replay request failed`; a production mutant that retires the failed segment without a retry fails it and 12 other lazy histories). The `request error bookkeeping`property checks the shared tracker's listed causes and its change result, including a request with several failures. Its`fail-again`step fails an issuer again with every cause it still lists and one new cause, as a replay's later verdict does: a cause still open keeps the place of its first failure (law 17; RED on d0cde4778, which released the issuer's causes before it counted the new list). The lazy and window grammars make`Date.now()`strictly increasing and check every request error's`at`after each step: a failure the model did not show anew keeps the`at`the node showed for it (RED on d0cde4778 in the named history`a failed replay's error stays when parked demand retires after the child restarts`and in the generated lazy property; reach`replay verdict lists an open failure again`, entered by a prefix in which a parked demand joins a failed replay; a mutant that clears the verdict's request errors before it reports its failures fails the named history). Both grammars also check that no `health:change`of a ready query repeats the tree before it. Open (R18, maintainer decision pending on code weight): a replay's verdict, and a lazy load or demand change that decides several keys, announce once per request error they change, so a listener sees intermediate trees (a verdict that replaces key errors with its failures first shows them cleared); a check that every announcement shows a request error frame the model held fails on d0cde4778 (for example`a failed replay's verdict replaces an earlier open failure`, step 5) and passes with a lifecycle `changeRequestErrors(change)`batch that costs about +57 B gzip, so it is not committed. Limits: these announcement checks skip trees whose query is not ready, because a source crash makes the query announce the same crashed tree twice in one step (seen on d0cde4778 at the lazy grammar's`crash`step); the main grammar owns crash announcements and does not count them. Not fixed (R3, R5): during a reload, before it completes, the new run's`reportHealthy()`clears the retired run's failure reason, and a query's own reload drops the retired run's request errors at the new run's first request error; the main and window models encode that behavior, which is in tension with law 16 ("a reload keeps the node's problems until it completes"), and needs a maintainer decision. Not reached (R8): a retired run's committed but unapplied truncate, from a listener that reenters during`commitPendingTransactions`, can complete the new run's reload. A deterministic work witness (`request-errors-work.test.ts`) bounds one failed 2,000-key segment's report-and-settle work at 20·K visits; the original scan-and-sort tracker used 14,007,001. A lazy named history pins that a demand change which retires a fragmented-churn replacement and another segment, while the replaced segment's replay load has failed, fails the replay for the still-held key (laws 9 and 16); the subscription lifecycle owner's demand-change grammar generates the class. A failed replay does not list the failure of a load the query released before the verdict (law 16; glossary request error: it clears when its demand retires); the named history `a failed replay does not list a load the query released before its verdict`fails on the model that listed it and on a production mutant that keeps a released demand's failure. A cleanup during a reload (ARCHITECTURE law 14; maintainer decision that a reload ends with its run) is a generated step in two grammars. The main grammar's`cleanup-during-reload`runs`cleanup({ dependents: true })`from the Collection's or the dependent query's`health:change`while`reload()`announces`reloading: true`. The lazy grammar's `ending`runs`cleanup()`or`cleanup({ dependents: true })`from those, from the Collection's`loadingSubset:change`, or from a change subscription's `status:change`while the reload retires its demand. In both, the model is an ordinary cleanup: no sync run stays live at the adapter, a reload in flight rejects, the Collection ends`cleaned-up`, and a later fresh start's commit is its diff baseline. Setting the epoch after the reload's announcement, or the truncate after the run's retirement, fails 12 histories; restoring the old flag order, or dropping the epoch return, fails the subscription-status ending. Cleanup from the retired run's adapter cleanup function or from the new run's `sync()`is outside this lane. A cleanup of a Collection that a cleanup during a reload left`cleaned-up`has no run to wait for and settles at once; the driver holds an adapter cleanup only while a run is live (a 30-times counterexample of the driver). Focused witnesses check that a reload from inside a`loadSubset`call the deferred start runs rejects, that a cleanup run from a subscription's status listener while a reload retires the run ends that reload with its run (no sync run starts and no replacement stays armed), and that a lazy join load that fails after readiness, under an ordered window that filters on it, publishes the output it held. The main grammar also compares the query's row count at the`status:change`that makes it ready with the count the step leaves (law 9): A's truncate commit that marks A ready while the nested shape's outer query waits for its inner query reaches it, in the first load and in a reload of a failed first load; a completion check that ignores the graph's pending work fails it. Limits: row count only, not identity. After every step the check compares status, rows, the projected tree, node identity,`status:change`/`health:change`order, and`findProblems()`. Named histories pin a reload's new-run `failureReason`surviving its completion. Focused lanes cover request errors, overlapping root loads (a request error clears only when a load started after the failure succeeds; 8 histories plus a later clearing load), a query's own failed first load not restarting when an unrelated source becomes ready, reload invariant witnesses (the retired run's open sync transaction is dropped; a canceled reload commit changes nothing and the reload keeps the rows; a diffing adapter sees an empty baseline inside a reload's snapshot), and Effect disposal on a failed first load or crash. The shared framework conformance scenarios`sync-errors-after-readiness`and`crashed-status`judge the hooks;`sync-errors-after-readiness`reads a two-failure`failureReason.errors`and a one-failure`error.errors`through every framework driver. Adapter receivers for reload after a failed first load (the new run's own readiness decides it, not Collection status): query-db, RxDB, and PowerSync focused tests, Electric's integration test, and TrailBase's lifecycle lane. Adapter receivers for reload of a ready Collection (maintainer decision F19: the reload keeps the rows until it completes; only a`begin({ snapshot: true })`transaction or the new run's own`truncate()`replaces them; every other write applies on them): Electric's integration tests`reload of a ready Collection`(a resumed stream with no changes, or with an update, a delete, and an insert, applies them on the kept rows and completes at up-to-date; a stream that cannot resume replaces the rows with its fresh snapshot; a must-refetch keeps the rows through its reset-marker commit and replaces them at its snapshot) and`an on-demand reload keeps its rows and ends with the source's rows`(resumed or not: the replayed subset's rows apply on the kept rows, and the adapter writes an update for a kept key, since core no longer rewrites a kept-key insert and sweeps nothing; the adapter owns removing rows deleted while no stream listened, [#2269](https://github.com/TanStack/db/issues/2269)), and`a progressive reload that cannot resume keeps its rows through a subset load`(no kept row vanishes before the atomic swap, the kept key takes the source's value, the live query has no error; it failed on the parent core with DuplicateKeySyncError); query-db`a reload of a ready eager | on-demand Collection keeps its rows and ends as a refetch does`; PowerSync `eager mode: a reload of a ready Collection keeps its rows and ends with the table`(three batches in one snapshot transaction); RxDB`a reload of a ready Collection keeps its rows and ends with the documents`(a first run that missed changes; two fetch batches); and TrailBase's lifecycle lane`a reload of a ready eager Collection`(10 histories; see its ORACLE.md). On the parent core, Electric's resumed and must-refetch cases, query-db on-demand, PowerSync, and TrailBase's two-page cases fail; RxDB's passes and guards the port. Each of these mutants fails them: a resumed Electric run whose first commit replaces rows; a fresh Electric, PowerSync, or RxDB load without a snapshot; and a query-db housekeeping or direct write marked`snapshot: true`. A reload keeps every row its new run does not rewrite, and core deletes no kept row on its own (maintainer decision; removing a row the backend deleted is the adapter's job, [#2269](https://github.com/TanStack/db/issues/2269)); the adapters' own on-demand reload receivers belong to their packages. Adapter receivers for an on-demand reload (maintainer decision: no core sweep; a held key is written as `update`; issue #2269): Electric `an on-demand reload keeps its rows and ends with the source's rows`(resumed: deleted rows go through the log; cannot resume: a row deleted while no stream listened stays), query-db`a reload of a ready on-demand Collection keeps its rows and ends as a refetch does`(row ownership removes the deleted row), PowerSync`an unchanged row survives an on-demand reload`(the adapter's own tracking removes the deleted row), and the TrailBase lane`an unchanged row survives an on-demand reload`(a row deleted with no event stays). Without Electric's`syncedKeys`seed and its update-for-held-key write, its non-resumed and progressive witnesses fail. Subset snapshot rows go through the same`syncedKeys`check as stream rows. PowerSync`eager mode: a reload whose later batch fails keeps the rows and the next reload ends with the table`checks core state: no open transaction remains and the accepted rows equal the kept rows; a snapshot begun before the batches fails it. Abort receivers (an abort ends the load): Electric, PowerSync, TrailBase, and query-db`an aborted load writes no rows and its late result changes nothing`; each fails under a mutant that ignores the signal at that adapter's stop point. Limits: an Electric abort while a request is in flight still lets the SDK publish its rows; query-db applies a late result after an abort without `unloadSubset`(a replay replacing the load within a run), whose data is current, so it costs a write, not correctness; RxDB has no on-demand path. Electric's`a ready after the stream gave up changes nothing`witnesses that`markReady()`after the run's`fail()`is a no-op (F6): the SDK gives up while a ready run's up-to-date commit waits for a wrapping sync's acceptance, and the acceptance arrives afterwards; there is no unhandled rejection and no status or health change. It fails on the parent core with an unhandled`CollectionStateError`. Limits: the progressive and on-demand Electric witnesses are fixed examples, not generated histories. A joined-filter lane checks that a failed join load lets an ordered window move again. An Effect has no request errors: a failed load after its gate still ends it, and the Effect load-failure lane checks that. An on-demand reload lane crosses the new run's first commit (an empty `markReady()`or a row commit inside`sync()`, or `markReady()`after it returns) with an unfiltered and a filtered query. While the new run's loads are in flight, it checks that the query keeps the old run's rows and that a direct read shows the kept rows with the new run's own commit on top (no snapshot came). After the loads settle, it checks the new run's rows. A late-first-load lane enumerates an on-demand Collection that fails its first load, stops after readiness, or crashes after a direct or joined query is ready, then calls the query's`reload()`. A query that follows its source into `error`after showing data fails it, and so does a query reload that skips a source still in its first load. A focused RxDB test checks that a change-stream error calls`fail()`before and after readiness. The generated grammar also keeps each Collection's rows and the sync run's open transaction:`write`and`commit`steps span other steps, the driver writes like a diffing adapter, a`write`can omit the`tag`field (an update merges into the row, an insert replaces it, also when a failed reload's open transaction applies on the kept rows), and after every step the check compares rows, the adapter's baseline, the query's rows, a direct subscriber with the initial state on each source when the history includes one (its rows equal the Collection's within one Collection lifetime, also while a reload is in flight or after it fails; a source that starts again after cleanup gets a fresh one), and an Effect over A (ended, source errors, listeners left).`cleanup-hold`and`cleanup-settle`steps run every other step while adapter cleanup is pending, including a crash, a new Effect, and the query's reload, which restarts a query a cascade ended. A second generated property,`source-health.on-demand`, drives a root window over an on-demand Collection: window moves, settle-all outcomes, source reloads, and the source's own calls, with requested loads reported from the adapter boundary; it checks the query's status, its request error, the source's node, and the window's rows. A third, `source-health.lazy-demand`, drives an eager parent Collection left-joined to an on-demand child Collection: parents come and go, loads settle ok or failed, the child source reloads, and the source makes its own calls; the driver reports each load's requested keys from the adapter boundary. It checks the query's status, its request error (which clears when a later load of the failed key succeeds or when the query no longer needs that key), and each parent's child. The lazy and window drivers can add a direct subscriber with the initial state on the on-demand source (its rows equal the Collection's at every step; the driver answers its own requests at once), and the lazy driver can add an Effect over the same join (at quiet checkpoints it has the query's rows; a delete that a later insert undoes between checkpoints is not observed). The lazy driver also crosses a child that holds `markReady()`until a`child-ready`step, so the child can be`loading`or fail its first load when it reloads, a reload stays in flight past its new run's first commit, and a second`reload()`returns the first one's promise; and the child adapter's own`truncate()`(a must-refetch in the same sync run, also while a reload is in flight: its replay requests every needed key, also one whose load threw, and a demand change's replacement that applies releases the coverage it replaces), loads that throw, and source reloads during the first load: the first load waits for the whole replay, and a replay fails only for a key the query still needs when it is judged. The child adapter honours aborts; an adapter that ignores them is outside the contract (maintainer decision: an abort ends the load). In the on-demand lane, a window move after a failed replay waits for the next successful replay, and a later reload aborts it. A reload starts its replay when it retires the sync run, so a load the reload aborted is over and its consumers wait for the replay, as after a truncate; a named history checks that the retired run's load decides nothing when its abort rejects it. A named history pins that a truncate after a reload's failed replay replays the demand again. The on-demand window grammar adds`settle-aborted-fail`and`settle-aborted-ok`, which settle only the loads the query aborted, with declared reach for an aborted load that settles beside a pending load after a request error. A source reload that aborts the app's retry after a request error leaves that error to the replay's verdict: the retry's late rejection or success decides nothing, and a failed replay's verdict replaces the retry's failure. On 2bc79fc7c the coarse verdict's window histories fail, and a mutant whose replay failure reports no error fails 37 tests. Window moves never abort a root load outside a replay, so a root load's late success after an abort outside a replay is unreached (a mutant that lets it clear the root error survives); page-distinct root failures are not modelled: the grammar keeps one logical window demand, as law 16's "later load of that Collection" wording allows. The lazy grammar's `remote-delete`step deletes a child at the provider with no stream event; a child Collection rows check after every step pins that a reload keeps that row (maintainer decision: no reload sweep; RED on 2bc79fc7c, which swept it). The main grammar's blind`insert`write pins that an insert of a key the baseline holds throws, during a reload as at any time (maintainer decision; RED on 2bc79fc7c, which turned a reload's insert of a row the retired run left into an update; a mutant that lets a reload's insert of a held key apply fails 12 named histories); the lazy grammar's diffing adapter writes an`update`for a kept child. Limits: the window grammar has no provider deletion. Every truncate replays the demand again, also one the reload's new run sends while the reload's own replay is open or after it settled (F8: a latch that skipped the reload's own truncate let a replacement delete replayed rows with nothing re-requesting them); a reload with no held acquisition keeps the rows and replays nothing. The window grammar's`reload-query`reaches the query's own reload while its node shows a request error: the query keeps its rows, its node's error, and`reloading`until its new run's window load settles, keeps the window it showed (a window move waiting for a replay ends with the retired run), and then shows only the new run's failure, if any (F4). During the query's own reload the window grammar also issues window moves and`restart`. A move of the new run whose load fails keeps the reload, its rows, `reloading`, and its promise until a later move's load succeeds, and a failure the new run reports shows on the node at once (maintainer decision). `restart`(cleanup and preload of the query) discards the window and any own reload in flight, so the new lifetime loads the initial window. Its limits: no source reload, report, or aborted settle during the own reload; the reload starts only when the source shows no problem; and a restart ends the request-error announcement watch for the rest of the history. Named histories also pin that a later load of a failed key clears its request error while an unrelated load is still pending, that a load failing during a replay is decided by the replay's verdict, that a reload during the first load fails it only for a demand still held when the replay is judged, and that a reload keeps a window move that joined the replay while it aborts a later move that waited. Mutants that restore last round's reload, cleanup, Effect, and replay bugs, and a request error that outlives the failed key's demand, each fail named histories. Witnesses pin that cleanup start announces the problems it drops, that a reload rejects during pending adapter cleanup, that a ready callback that throws still settles a reload, that a failed reload keeps its rows as a diffing adapter's baseline, that a query's own reload settles with its new run (a load that fails again, or a graph that throws), that a source's or the query's reload clears a request error, that an Effect over a source already in`error`or`crashed`ends, and that a pooled view keeps node identity when its source alias is`root`. Later witnesses pin that a reload's open transaction applies on the kept rows after the new run fails, that an Effect over a stopped source leaves no load, subscription, or listener on it, that cleanup start keeps an `error`status's cause, that a source reload's replay keeps a request error that failed after it started, and (query-db) that a late recovery counts when another Query fails meanwhile. The query-db lane`failureReason follows the still-failing Queries` (`query-db.failing-queries`) enumerates every history of four fetches over two subsets' Queries, directly and behind the persisted wrapper (a receiving witness: the wrapper must relay `reportHealthy(error)`'s value). Its model keeps each failing Query's latest error value and the listed values, one per value, oldest first (review: one entry per failing Query, and a value stays listed while any failing Query holds it); fetches succeed, fail with a fresh error, or fail with one shared value; after every fetch it compares the listed causes by identity and order, the shown `message`and`cause`, each kept failure's `at`, and health tree identity (ARCHITECTURE law 17): a fetch that leaves the frame as it was keeps the node and its `failureReason`object and announces no`health:change`. Declared reach includes a Query that fails again while listed and one that recovers with two failures listed while another fails. RED on the parent: a recovered Query's failures stayed listed. The main grammar's `report`and`healthy` steps can name a listed failure's error value or an unlisted value with the same message; a report of a listed value and a healthy call for an unlisted one leave the frame as it was and must announce nothing (maintainer decision), and a healthy call for a listed value removes only that failure. Limits: replacing a Query's failure announces two health:change events (add, then remove). The [snapshot oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/live-query-observer-snapshot-oracle.property.test.ts)'s health lane (`live-query-observer.snapshot-health`) generates `report`, `healthy`, `write`, `subscribe`, `unsubscribe`, and `read`over one ready source and checks that every`getSnapshot()`shows the current`failureReason`, `hasErrors`, and `isSyncError`, on detached reads, attached reads, and across the attach cut, and that the tree keeps its identity while unchanged; after a round trip between reads that ends on the previous read's content, either identity is legal (glossary, health tree). Each read also compares the `failureReason`'s `errors`(messages in order, each cause by identity) with the model's frame, and a declared reach state reads a frame of several failures. Limits: one source,`failureReason`only, no hydration seed, no framework. The shared conformance cells`crashed-status`and`crashed-status-during-first-load-{collection,query}`read each driver's status flags without reading`data`, and require Solid's resource path to show `crashed`, not `error`, when preload rejects for a crashed Collection. By maintainer decision, an error boundary receives an Error for a failed first load or a crash: the cause itself when it is an Error, otherwise an Error with the problem's message whose `cause`is the reported value; React's`useLiveSuspenseQuery`and Solid's resource witnesses cover a failed first load and a crash, each with an Error and a non-Error cause: a failure inside sync and after mount, a sync function that throws, and a later crash (after readiness for React, during the first load for Solid). A crash path that throws the raw cause fails the non-Error crash cells in both. The replay refinement oracle's`settle`command can keep holding loads that start during the step, so a held retry keeps a failed join load's request error visible. A crash-recovery witness checks that nothing recovers a crash in place and that a cascade cleanup gives a fresh start with only the new run's rows; a`reload()`that recovers a crash, a cleanup that keeps the crashed run's rows, and a crash that survives cleanup each fail it. The three generated properties share one model: the main grammar's source model drives the on-demand Collection of the window and lazy grammars too (a load that succeeds is one sync transaction, so a reload's first load replaces every row), and one demand model, from ARCHITECTURE laws 9, 10, and 16, judges the window grammar's single window demand and the lazy grammar's per-parent keys. The lazy grammar checks the request error after every step, also while a replay is open or after one failed, and both grammars check a coverage law: once the query is ready, every held key has a load the adapter still serves, its rows, a replay failure awaiting judgment, or a shown request error (an aborted load without a replacement fails it). Named histories pin the cases that closing the earlier open item found: a demand parked behind a child whose first load failed joins the open replay, also an in-flight one when the child restarts (the CI counterexample seed -838690579: production replaced the in-flight replay and lost its own failed load;`a reload after the source stopped replays its demand and fails with every load`); a failed replay stays open while the query holds demand, and its error clears when its failed demand retires after the rest loaded (law 9); a load the truncate aborted does not clear a request error; a held replacement that loaded decides the demands it replaces; and a cascade cancels a query's pending reload. Law-silent, recorded with the maintainer: a truncate's `markReady`option does nothing on a ready Collection, also after`fail()`; a cleanup after a crash that followed cleanup settles at once; a truncate after `fail()`is outside the grammars; a window move with no request is served from loaded rows. A child whose first load failed parks new demand without a request; by maintainer decision the lazy model predicts the parked acquisition from law 10 (growth or churn), and a declared reach state and prefix enter that region. Named histories pin that a replay re-requests parked demand and then publishes, that a released parked demand parks again, that a replay's failed request keeps its acquisition's coverage, and that a later change drops a parked replacement that no longer matches. ARCHITECTURE law 16 states the rule. A query that crashes while it starts, because a Collection it reads is still cleaning up, reads nothing: it announces nothing more and no cascade ends it (maintainer decision). A pooled view that is unsubscribed, outlives its source's`cleanup({ dependents: true })`and restart, and then subscribes again stays`cleaned-up`, while a live-query Collection restarts; the pooled cleanup-admission lane crosses observed and unobserved views but never restarts their source; a follower tab does not see a provider failure on the leader outside a pending request. | +| Paced mutations | Partial | The core virtual-clock owner checks strategy timing, admission, cleanup, and settlement. The React render-cut owner checks committed callback ownership across suspended and committed transitions, descendant layout effects, and StrictMode while preserving mutate identity. Callback affinity to transaction admission, other frameworks, new debounce/throttle admission after cleanup, failed persistence, and broader generated schedules remain open. | +| Optimistic state | Complete | The independent base/intent/source-queue graph plus outcome, transaction-payload, and publication drivers are literate. | +| Drafts and native values | Complete | Native differential behavior, draft change tracking, detachment and its class-instance exception, hostile keys, aliases, cycles, and Map/Set live iteration are literate. | +| Query DB and observer | Complete | Query-scope row ownership, subset identity and cancellation, failure and recovery, and the per-listener eligibility ledger are literate. | +| Query DB SSR dehydration | Fixed QueryClient/Seroval boundary | Successful on-demand entries with a compound predicate, a request signal, and an ordered cursor with a custom comparator retain their row and user metadata through Seroval's reconstructed stream payload and Query Core hydration; request options are absent from the stream, while query functions retain the original values. The TanStack Start router and browser Collection resume path still need a host witness. | +| Ordered acquisition | Complete | Exact demand identity, applied settlement, independent pagination recomputation, request work, lifecycle products, replay authority, source-generation readiness, and transaction-refinement abort boundaries are literate. An initial full-source ordered load shares the synchronous readiness cut ([historical review](oracle-reviews/initial-full-source-sync-readiness.md)). The ordered-lifecycle oracle covers an inexpressible-boundary fallback before first publication when every acquisition returns literal `true`. It also checks a later null-boundary fallback after the first joined window publishes, across literal, Promise, and held-receipt responses, including a held receipt after child demand settles ([review](oracle-reviews/issue-2152-initial-ordered-fallback.md)). The ordered loader state oracle also has a generated request-authority lane (`ordered-loader.request-authority`, fixed and random campaigns). It drives the real loader over a fake subscription through settlements in any order, failures, deferred invalidation and report reactions, ordinary passes and window moves, and began with four laws from a TLA+ model (#2191): a successful repair keeps its acquisitions until a covering read succeeds; quiet loading holds nothing older than its last authoritative read; a superseded request's failure is recovered by a full-source read without an explicit retry; and once quiet, an invalidation and a pass issue a read unless a full-source read covers the source. Scenarios also throw from the first release callback, answer a repair synchronously from installed rows, and let a page's own delivery invalidate ordering; named replays keep the traces they found, each of which failed on main (a throwing release that unwound a resumed repair, a synchronous repair waiting behind a superseded request, and a self-invalidating page whose tie was never retired). On main it found a superseded tie that failed after its repair finished and a newer invalidation's reaction was deferred, which left refills blocked; a named replay keeps that trace. A stale prefix that retires the repair's prefix fails the held law, and a stale request held after the repair finished fails the leak law. The lane also crosses unexpressible boundaries, joined work that defers loading, truncate replay that succeeds or stays pending, optimistic departures, source deletes, the bounded backoff retry, window moves that shrink or widen, plans that read only the full source, and reports inside the loader's own first page. A replay owns the requests issued before its truncate, and when the subscription still holds a full-source lease the settled replay covers the source. These dimensions found three more defects, each kept as a named replay: a repair prefix that settled after a replay started its replacement took the replacement's chain tag and was never retired; a replay counted a full-source read as complete after an explicit retry had released it; and a failure from before a truncate dropped the next repair's staged step. Each of the seven defect replays fails on the loader at the branch's merge base with main. A later law, one request at a time, found that a replaced repair chain's prefix still counted as current and issued a tie beside the replacement's prefix; a chain is now superseded by any newer authority start. The failed-lease law also counts a covering replay, which a named replay reaches. Repeated random campaigns then found a repair request that settled after full-source coverage and stayed held; full coverage now retires it. The held law counts a later prefix that a successful replay reacquired while it was in flight. Four histories from TCP's known error classes followed: a request the provider never answers (a lost segment), a release callback that truncates synchronously (a reentrant reset), a window offset other than zero, and an at-most-once release law with no release while a replay owns the demand unless an explicit retry replaces it or a held full-source lease covers it. They found that the backoff retry's release pass kept releasing failed demands after a release started a truncate; every release pass now stops on a reentrant reset. The oracle now observes epoch moves as calls to `invalidateSourceOrdering()`, so it tells a replacement chain from a chain step exactly. The loader's work slot and debt now move only through a transition table that throws `OrderedLoaderInvariantError` on an illegal move or an incoherent work and debt pair; the oracle records any such error as a broken law, so its generated histories fuzz the table. A covering-lease law (a full-source lease whose read succeeded or whose demand a replay reacquired is never released) found that a full-source transport failing after a covering replay turned complete coverage into an own failure; the table then found that a repair request failing after a replay finished its chain did the same. Mutants that drop each new guard fail. A release callback that runs an ordinary pass synchronously, as a graph run does when the release delivers deletes outside a transaction, found that the pass issued a page while another request was settling, and that request then issued its tie; every release pass now blocks refinement until it unwinds. A dispose command and a release callback that disposes the loader check that a disposed loader issues and releases nothing; mutants that drop the loader's own `active` checks in the release pass and in failure handling survive because dispose resets the cursor, so those checks were removed. TLA+ run 3 (`LoaderTable.tla`) models the table with truncate and replay; its safe rules pass with 9,368,122 distinct states, and each of the four guards from this round, removed alone, produces a counterexample. Reverting any one fix fails its replay and a campaign. The generated histories also stack a second truncate on a pending replay and truncate after a failed replay, each with a reach witness. A lease missing from its map during a release pass is now an invariant violation; no history reaches it. A second high review found that a covering replay did not retire a superseded failed lease from before its truncate, because the failed-lease law exempted every replay-owned request. The law now judges a superseded failure that was live (it failed in the incarnation it was issued in) once a covering replay settles after it, and the replay retires those leases. The subscriber now schedules the replay's graph run in a finally. The replay-refinement oracle is the receiving witness: its generated ordered section (`replay.ordered-window-faults`) drives a real on-demand Collection and an ordered live query through rank moves, held and failed loads, truncates, and unloads that throw without a truncate, while a replay starts, and while it settles. Its settled-window law found that a release throwing after a successful ordered load left the window on old rows, because the builder counted the rejected participant as a failed load (also on main); the loader now marks release errors and the builder publishes. It also fails without the subscriber's finally. The section now also crosses tied ranks (its law compares the window's rank sequence and each shown row's current rank), a peer live query on the same source, offset 1, descending order, and explicit window moves; a move that rejects after a failed source recovery leaves the window, and one rejected only by a release failure still moves. A reported-faults law accepts injected faults as rejected loads, thrown releases, rejected moves, or errors the subscription rethrows from a microtask, and rejects any other escaped error. These found that a window move whose load succeeded was recorded as failed when a release threw, so the window stayed private until another move. A throwing release is an adapter bug, so the loader now reports it uncaught from a microtask and changes nothing else; the reported-faults law rejects a move that a release failure rejects, and the request-authority oracle requires each throwing release to be reported uncaught exactly once (a mutant that swallows the error fails). It also crosses a joined marker source: a LEFT join that keeps marked or unmarked rows, and an inner join. The marker source is eager or on-demand, with marker loads that are held, fail, and truncate. This found that an on-demand joined source whose loads finished without delivering a row left the window on old rows, because nothing ran the graph that the pending joined work held; the subscriber now runs it when a joined source becomes ready. A truncate never aborts a window move: the move waits for any replay that holds publication and then applies (ARCHITECTURE.md), so a move that rejects with `AbortError` during a history is an escaped error. (An earlier version of this entry claimed a named replay reached an aborted-move branch; no history reached it, and the branch, which quietly accepted such a rejection, is gone.) Recovery truncates the marker source too, because a failed marker replay keeps the last complete publication until that source replays. A hostile-mutant pass over the loader's guards found guards that no ordered oracle kills. A differential instrument then ran 3,000 generated request-authority histories through the loader and each mutant and compared every request's shape, outcome, and release. Three guards the code makes redundant are deleted: the backoff timer guard, the explicit-move check in the retry callback, and the `requiresFullSource` check on a report. An ignored-report law now kills two of the mutants that changed public requests: a report issues no authoritative request before any request settled (a request in flight still owes an exact result for its own options) or while the window is inside the loader's own first prefix. Named replays reach both, and the random campaigns judge them in quiet histories. A random ordered-work campaign later found that the first-prefix check was unsound: it took the next installed row as the next source row, but a signalling refetch moved the page's end before the tie the loader had read, so that tie's row stood past the page while a row tied with the new end was never sent, and a later window move skipped it. The loader now accepts the row only when its page read it or when it read the tie of the page's current end. When the end moved, it repairs (see the later maintainer decision below); query-db's first-page witness requires bounded reads only, no full-source read, and an unchanged count of active Queries. The optimistic-window cases that allow but do not require a refill still bound any refill that occurs: an ordered read within the refill bound or a predicate read, never the full source (a mutant that made the growth read unbounded fails them). The ordered-work model now sorts `undefined` ranks with `null` ones, as the comparator does, and its histories generate both; nullish ranks tie in its window and in-flight comparisons. An `undefined` end also matches a loader that attempted no tie; a nullish end is never an expressible cursor, so the loader has started or will start a full-source read, which covers the source either way. A guard against that match survived the generated campaigns as an equivalent mutant and was removed. The ordered-work oracle keeps the trace as a named replay that fails on main, and the request-authority oracle has an unproven-report law (a report over a first page that no row it read proves starts a repair or reads the end's tie), whose replay and fixed campaign fail on main; the broad campaigns reach that law in about one history in 10,000, so an unproven-report campaign (`ordered-loader.unproven-report`, fixed and random) settles the first page, settles the read after it, and reports, over random case parameters and a random suffix, and declares its reach (about a third of 300 histories); `main`'s loader fails both runs. Its fake subscription now reads installed rows as the subscription does, applying a tie's equality, a cursor, and a limit; when its reads ignored the limit, the tie was always the last installed row's, so the first-prefix ignore branch was unreachable. The cost law's per-chain bound, 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 grows its prefix instead of reading the full source. The report campaign (`ordered-loader.unproven-report`, 1,000 runs; at 600 a random seed could miss the minimum) now starts each history with one of eight prefixes and declares reach for each report premise: a proven first page, a page that read past a window that shrank, a held full-source read, a head insert that moved the page's end, an owed repair, authoritative work in flight, a release of the older tie that truncates or disposes the loader, and a report to a disposed loader. The unproven-report law requires a bounded repair or the end's tie when every installed rank is expressible; a full-source read no longer satisfies it (a mutant that forced full debt after a report passed before). The cost law also rejects a repeated read: within one invalidation, no prefix, tie, or page repeats more often than the ordering epoch moved. Two laws are stronger: an ignored report, over a proven first page or a held full-source read, issues no request at all, where it used to forbid only authoritative ones; and a report while a repair is owed reads no tie or page before the repair. Deleting the moved-end early return in `revalidateLoadedCoverage`, or its past-window term, fails the first law, and deleting the owed-debt term in `loadMovedEndTie` fails the second; a deletion sweep had found all three surviving every oracle. With that reach, the report path's own `!active` checks were deleted: `dispose()` advances the generation that the stale-run check after the release reads, and a disposed loader issues nothing, which the dispose law judges. A head insert can carry a `-Infinity` rank, and two more laws judge every request: the loader never reads the tie of a rank no cursor can express, and the first finite request after a truncate has no cursor. The fake's `dataNeeded` now counts the window rows not at or before the boundary it is given, where it returned a constant. With those, the stale-run check after the older tie's release fails the reset law when a release truncates the loader and the window then grows (a prefix declares that reach), and removing `loadBoundary`'s own unexpressible-value check fails the unexpressible-tie law. The covered-source early return and the staged-continuation term were deleted: coverage that a full-source read completed holds the work slot, whose check then decides the report, and a staged finite continuation cannot coexist with a proven end, while a staged repair step owes a repair. The ignored-report law's full-source branch no longer needs a quiet history, since such coverage owes nothing, and a prefix reaches a paged loader whose repair fell back to a full-source read; removing the work-slot check then fails it. The unexpressible-end term in `loadMovedEndTie` stays: the unindexed-refetch random campaign found that without it an unindexed loader reads the full source through the boundary fallback where its bounded prefix repair would do, and a named replay of that trace fails without the term. Maintainer decision, later: a report whose page end moved now repairs instead of reading only the new end's tie, to cut shipped bytes; `loadMovedEndTie` and its guards are gone, so their mutants above no longer exist. The laws stay and judge the repair path: the ignored-report, owed-repair, unexpressible-tie, and reset laws all still run, and the unproven-report law accepts a repair. 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, and the query-db replay of an insert into a top-N list now expects the bounded repair's prefix and the new end's tie. Still open there: the `Object.is` term of the moved-end check. It skips work when the loader already read the end's tie, and it decides differently from its fallback only while other work is in flight, which the ignored-report law's quiet premise excludes. The ordered-work oracle owns that branch instead: peers over a refetching provider stay in its grammar for source filters (#2155 excludes only joined filters), and a proven-report law requires that a report which changes nothing the first query's own page and end tie prove, with the model's next row installed and no installed row deleted or reordered, starts no request for it and leaves it owing no `refetch: true` repair. A campaign fixed to that premise must reach it, and a loader that repairs every report fails both its fixed and random runs. A staged end tie that pending joined work defers is read by the pass after that work finishes; a request-authority named replay reaches it, and a loader that drops the staged tie fails it. The unproven-report law judges that later pass, and a truncate, failure, move, or disposal in between voids what the report owed. A history that starts during the initial preload found that a window move deferred behind a marker replay, which the source truncate then discarded, rejected with that replay's `AbortError`, so the older move's window won (also on main). A deferred move now waits for every replay that holds publication and applies unless one failed or cleanup ended the sync run; a named replay fails without the fix. The ordered-work refetch law also required no full-source read after a signalling refetch for an unindexed plan, which the architecture then let refill a short window from the full source; random seeds found it on main. Maintainer decision on this branch: an underfilled unindexed prefix never reads the full source, because that read is unbounded. It grows instead: each growth asks for twice the last prefix, and it stops once the last growth added no rows. A deletion sweep showed that the older condition's other term, a prefix that reads short, follows from that one. The refetch law now forbids a full-source read after a signalling refetch with no unindexed exception. The named replay of a short unindexed window and the fixed unindexed-refetch campaign (`ordered-work.eager-unindexed-refetch`, fixed and random, with reach on windows the prefix fills and windows it leaves short) failed on the full-source fallback and pass with growth; the full-source mutant still fails them. Growth made a new optimistic history possible: an unindexed one-row window behind a joined or anti-joined filter that installs only the prefix it grew to, whose departure leaves the replacement row missing. Sixteen fixed optimistic-window cases (update and delete, both consumers, acknowledge and rollback) cover it, with a refill bound of twice the installed prefix plus the overlaid row. A loader without the stop rule never reaches a fixed point; a loader that stops whenever the prefix reads short leaves those windows empty after settlement, because the pending departure hides a row from the count. The existing unindexed optimistic cases now allow, but do not require, a request behind a joined filter when the replacement row is installed: growth proves rows only up to its prefix, where the full-source fallback had proven every row (a maintainer-approved expectation change). The proven-report random campaign now declares its reach too. One mutant still changes requests without a law: dropping the optimistic-refill flag turns a refill into a refetching prefix. Six more are equivalent in the fake but stay, because the fake cannot reach their premise or they encode a documented rule: the own-first-page check behind a held sync transaction (the fake never holds one), a truncate moving complete coverage back to held, clearing the pending request when a full-source read starts (the fake never answers a full-source read synchronously), staging a joined `more` continuation, the staged-continuation guard before an unindexed prefix, and the drain re-entry guard (a long synchronous chain would otherwise recurse). An include dimension adds a `toArray` child that reads the marker source for each shown row, and an include-children law requires each shown row's markers to equal the model wherever the window law holds; a mutant that never loads newly demanded child keys fails it in an include-only scenario. A newest-move law requires a superseded window move to resolve only after the newest move settles (a superseded move that fails still rejects with its own error), and the model lets the newest move that resolved set the window; it found that a superseded move resolved as soon as its own load settled, so awaiting it showed neither window. The terminal-error law also allows `error` after a load fails while the query is still loading, the documented initial-load failure; a lazy join source is exempt. Limits: includes are one level deep. It also found that a release failure in lazy joined demand during a window move rejected the move, because the subscription reported it as a load error. A window law now lets the newest move win; it found that two moves deferred by a pending source recovery could resume in the opposite order, so the older request replaced the newer window. `setWindow()` now ignores a deferred call that a newer call superseded. A terminal-error law allows a final `error` only after a lazy join demand load, on either side, failed outside a truncate's recovery; named replays reach that branch. A load belongs to a recovery when it asks again for a demand its source held before the truncate (the same request data), or when the graph starts it while the truncate commits. The first model read a flag set only during the commit, but subscriptions start replay loads in a later microtask, so it counted every replay load as new demand and allowed `error` after any failed replay load; a random campaign found the reverse case (#2224): a marker truncate privately moved the window, the include's new demand started outside any replay because releasing its old demand ended the subscription's replay, and its failure ended the query. The demand controller now starts new coverage before it releases retired coverage, a query subscription opens a replay at every truncate even without demand (a replay that starts no recovery work and reports no loading), and a demand failure that fails its subscription's replay keeps the last publication instead of ending the query once the query has first become ready. The section also generates histories that start during the initial preload, with an initial-readiness law (the preload has settled once loading is quiet), marker loads whose aborted requests reject with `AbortError` as the source contract asks, and settles of the newest held load. These found two older defects on main: an aborted lazy demand request that rejected after its truncate replay succeeded ended the query, and so did a superseded segment that the controller released; the controller now treats an aborted request as obsolete demand, which its replay or newer coverage decides. Named replays keep each trace; reverting each production change fails one of them, and a mutant whose replay rebuilds its request data fails the campaigns' replay-load reach witness. The ordered-lifecycle oracle owns the truncate-work witness: a zero-limit window issues no request and reports no loading at a truncate, while a one-row window reacquires. A retention law then judges law 9 at generated checkpoints: a truncate raises a publication barrier, every load its source starts while the barrier is up joins it, a participant's failure fails the replay while its logical demand is held, and while any barrier is up the query shows exactly the rows it showed when the first one rose. It runs after every command and once loading is quiet. A failed-replay campaign (`replay.ordered-failed-replay`, fixed and random) starts each history by holding loads, truncating a source, optionally starting demand, and failing a held load, with a branch that fails an include demand while its marker replay still loads; it declares four reach states with `createOracleReach` (a judged failed replay, a judged held replay, a demand started after the commit that fails while its replay loads, and the include form of that demand). A replay participant now also counts as recovery for the terminal-error law, so a demand that starts after the commit while its replay is open is judged in generated histories, not only by a named replay. A mutant whose failed replay releases the publication gate fails both failed-replay campaigns and the broad campaigns; a mutant that ends the query when such a demand fails fails both failed-replay campaigns; the branch's production files reverted to `main` fail both. A preload-recovery campaign (`replay.ordered-preload-recovery`, fixed and random) starts every history during the initial preload over on-demand markers and declares two more states: lazy demand in a replay that fails before the first ready, and a window move that waits behind a replay a newer truncate discards. A builder whose deferred move a discarded replay rejects, and one that keeps a failure before the first ready non-fatal, each failed only a named replay before; both now fail the fixed campaign, whose seed kills both (each dies in about half of 200-run seeds, so the random campaign kills each about half the time). Building the law found three model errors, each corrected from ARCHITECTURE.md: a failure is scoped to its logical demand, so a retired failed demand and the demand that replaces it publish when the replacement succeeds; a replay completes when its last participant's rows apply, so demand that a deferred window move starts afterwards does not join it; and an ordered tie request (a `where` on rank) is not lazy demand, which the terminal-error law had treated it as, so a failed tie request had excused a terminal error. A query's subscriptions to one collection now share one replay barrier (ARCHITECTURE.md, "Replay participants and failure"): a probe found that in a query that joins and includes the marker source, a marker truncate retired the include's only demand privately, its emptied replay closed while the join's replay still loaded, and include demand the graph then started failed outside every replay and ended the query. A sibling-demand branch of the failed-replay campaign declares that state; the branch's code before the fix fails the named replay and both campaigns at `convergence: terminal error`, and removing either sibling hold (at replay completion, or for an emptied replay) fails all three. The random failed-replay campaign then found the other half: one subscription's replay failed, the other's finished once it settled, and demand that a rank move then started on the finished subscription failed and ended the query, because the subscriber judged the failure by its own subscription. The subscriber now asks whether a replay of the source's collection failed; a named replay (`a demand that fails after the sibling subscription's replay failed`), a `failedSiblingDemandFailures` reach in the sibling branch (which now may move a row and fail the oldest held load), and both campaigns at 400 runs fail at `convergence: terminal error` on the per-subscription rule. A design that judged a failure against any failed replay of the query, across collections, used to survive, because the convergence check runs after an explicit window move, which retries a failed row demand. A strict terminal-error law now requires a failed lazy demand outside every recovery to end the query (ARCHITECTURE.md, error 228), skipping only failures the model cannot place: after a load threw, and, with a peer, a load that joined a replay. Merge with #2257: its review (CF01, [record](oracle-reviews/2026-10-26-pr-2257.md)) judged a new keyed request that a truncate commit starts as terminal, and called non-fatal recovery of that demand a future contract. This PR adopts that contract by maintainer decision. The merged owner keeps CF01's initiating-request receipt check as a second account of every terminal error, at each history cut and at convergence, plus its named replays and the `replay.subscription-demand-origin` and `replay.keyed-request-failures` campaigns, with their two reach-state pairs measured on this owner's model. Both demand origins, new demand during a marker commit and a settled demand that the replay asks for again, must stay nonterminal after the first ready. An always-fatal `failDemand` mutant fails 15 tests, including both ported campaigns; a never-fatal mutant fails the strict law in 8. CF01's duplicate of the #2224 named replay is folded into this owner's existing one. Merge with #2168 (ARCHITECTURE.md law 16): after initial-query readiness no load failure ends a live query, and a failed lazy demand is a request error on the query's node. That supersedes the strict terminal-error law, the error-228 rule, and the always-fatal and never-fatal mutant results above for a query that has been ready. The replay owner now allows a terminal error only after a load failed while the query was still loading, judged at each history cut and at convergence, and it adopts #2168's request-error witness (`requestErrors`); the excusals for thrown and unplaced failures went with the strict law. #2168's design already starts new demand before it releases retired coverage, and it treats a synchronous `loadSubset` throw as a rejection whose owner's release unloads it (the subscription oracles pin that unload), so the adapter-release law now counts a thrown load as acquired. It lacked the shared barrier, the failure hold behind a sibling's failed replay, and the deferred window move's wait, which the merge ports onto it. Without the deferred-move loop, 8 tests fail, #2221 trace 2 among them; without `failReplacement`, the two-failures named replay fails; without the sibling wait, that replay and both failed-replay campaigns fail. A deferred move rejects when a reload ends the sync run of the query or of a source, which the source-health oracle requires. The window-request reset went: a move can no longer throw, and deleting it failed nothing. The replay that a truncate opened without demand went too: under law 16 its demand's failure no longer ends the query, and its named replay passes on #2168's subscription. Review of the merge (2026-10-28): the replay owner gained a request-error account. After initial-query readiness, each failed lazy demand load that is still owed must appear among the query node's open request failures, by its injected error object, at every history cut, and no request error may stay after recovery replays every source (authority: ARCHITECTURE.md law 16, law 17, and the glossary's request error). A failure is met when 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 owes nothing until every participant settled. A terminal error now needs a load that failed while that same query was loading, so a peer's initial failure no longer excuses `live`. A thrown load joins a replay that no participant joined yet, and the failed-sibling branch now generates a held retry. `requestErrorChecks` reaches the declared minimum of 5 in each failed-replay campaign. Mutants: demand outcomes that never report an error fail 9 tests at the per-cut check; a successful replay that keeps its request errors fails 22 at the stale-error check. Limits and open items: 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 finding that `failReplacement`'s replay, which has no recorded failure, completes when every demand that joined it settles is closed as a law revision by maintainer decision: the hold ends when every demand load that joins it loads, a later replay of the source succeeds, or the subscription's last demand retires (ARCHITECTURE.md, "Replay participants and failure"). The query retries the failed demand at once and that retry joins the hold, so the hold cannot publish without the failed demand's rows. The replay oracle judges each subscription's request-error verdict by the subscription that a load names, and its failed-replay campaigns declare the premise (a subscription's load succeeds while that subscription's failure is still owed; 30 fixed and 24 random histories at the normal budget). No live-query history violates the request-error or retention law there; a mutant that completes such a hold when any participant settles fails both failed-replay campaigns at the retention law. A subscription-level `it.fails` test that asserted the earlier, stricter wording (no joined demand may end the hold) was removed with the revision. Limit: the oracle drives the hold only through live-query histories, where the retry always joins; a direct `Collection.subscribeChanges` caller that never retries is outside the live-query contract. Which failure a deferred window move rejects with, when its replay has several, is not specified: `main` rejected with the replay's earliest failure, and this branch rejects with `lastSubsetError`; by maintainer decision the move rejects with its replay's earliest failure that still decides it (law 17 decides which count: a retired demand's, a retried request's, and a replaced demand's failures do not). An identity law now judges deferred moves that start before any failure of the replays they wait on; on the parent code both failed-replay campaigns and the named replay `a window move behind a replay with two failed loads` fail with "replay error identity", and the deferred-move loop that rethrows a non-abort replay error passes. The request-error account decides failures per key: a later load of the source that asks for some of a failure's keys decides them, and keys it no longer asks for left the demand. The held-retry branch then found, at three times the run budget, a marker change publishing over a window that the model retained after a failed marker replay (`hold, truncateMarkers, settle ok, settle ok, settle fail newest, mark 3 off`, ascending, offset 1, limit 1, includes over on-demand markers). It was a model defect: the query retries a failed demand at once, and when that retry loads the failed keys the failure no longer counts (ARCHITECTURE.md law 17), so the replay succeeds and publishes. The model now clears a replay failure when a later load of the replay loads its keys. The replay oracle passes at the normal budget and three times at three times it, and production without `failReplacement` still fails its named replay at the retention law. A move whose `setWindow` throws synchronously after the request generation increments is reachable only through invariant errors, which probes for a throwing subscriber, a crashed source, and a throwing `select` did not reach. An aborted request's rejection and the failure of a segment that newer coverage released are not failures. That design now fails both failed-replay campaigns. An independent oracle review then narrowed several judgments. The terminal-error excuse after a synchronous throw applies only to the thrown error itself, ending the query after the step that threw; the throw's own step, and every other failure, are judged strictly (a per-history flag had excused every later failure). A load that fails outside every barrier after a failed demand of its source retired is unplaced, because the failed replay may still hold through the subscription's other demands, which the adapter cannot attribute. Limit: with a peer, the strict law accepts either query ending, since the adapter boundary cannot tell which query's subscription issued a load; attributing each failure to its query's subscription would close that. The window check now also requires every shown row to be eligible and to appear once, so a tie cannot hide a row the join rejects. A synchronous-throw dimension (`markerThrows` arms the next marker load to throw while it starts) found that a load throwing inside an open replay ended the query: the subscription dropped the demand, so its failure did not fail the replay, and the subscriber treated the throw as fatal. The subscription now hands the replay a rejected participant for such a throw and keeps the demand, which a later replay retries; named replays (`a replay load that throws while it starts`, `a demand whose load throws inside its open replay`), a `thrownReplayDemands` reach in a thrown-demand branch, and both failed-replay campaigns (now 600 runs) fail on the parent code, and a mutant that later unloads the never-acquired load fails the adapter-release check. Model corrections, each from the adapter-boundary rule that a thrown load acquires nothing: a thrown load joins a replay only if it still has a participant or a failure; an armed throw expires before the drain; and a thrown demand's retirement is invisible, so the retention law skips the marker barrier from a throw to the end of its episode (a later barrier in the episode inherits the ambiguity, which also corrects the existing release-throw case), and a failure that joins an already failed or ambiguous barrier may end the query. Maintainer decision: a synchronous throw inside an open replay fails that replay as a rejection does, so the subscription replay oracle's `new request throws` case and five `live-query-collection` cases, which expected a thrown new demand to roll back, now expect the rejection outcome. Limits: a design that dropped a thrown demand and let its replay succeed passes wherever only retention could see it; after a history command follows a throw, a terminal error is excused, because the model cannot see whether that command retired the thrown demand and with it its failed replay (a demand that waited on that replay then fails outside it). The named replays judge the throw's own outcome strictly. The failed-replay random campaign at five times its run count then found a retention counterexample with no throw, on the parent code too: a join and an include of one marker source; one subscription's replay fails, the other's finishes, and a row move starts demand on it whose load fails, which the shared barrier kept from ending the query; a marker change then retired the first replay's failed demand, which discarded that replay and published an empty window, because the second failure held nothing. That failure now fails its own subscription's replacement, which holds until a later replay succeeds or the subscription's last demand retires. A named replay of the trace fails without the hold, and a `partialFailureRetirements` reach (a failed demand retires while another failure still holds the barrier) is declared in the failed-replay campaign; the campaigns themselves do not fail without the hold, so the named replay is the witness. A window move whose load throws while it starts counts as a settled, failed move in the model (it used to stay pending, so the newest-move law misjudged the older move), with a `thrownMoves` reach. The broad random campaign at five times its run count then found an older move whose settlement resolved with itself: the newer move threw after it bumped the request generation and before it replaced the latest request, so the older move's settlement returned its own promise, a chaining cycle that escaped as a TypeError. A call now clears the latest request when it bumps the generation, so no older move can wait on itself; a named replay fails without that. The same assignment covers a move deferred behind a replay, which had the same shape (an independent review found it can hang forever when a newer move throws between the replay settling and the deferred move resuming); that microtask gap is outside the grammar, which flushes between commands, so only the shared mechanism, not that path, has a witness. The ordered-work oracle also keeps the trace from #2221 as a named replay: before prefix growth the loader read the full source twice after a signalling refetch past a NaN boundary; with growth it reads none. Limits: the adapter boundary cannot tell which query issued a load, so the retention law and the participant rule skip histories with a peer. The retention law judges barriers that rise after the query first became ready; before that the initial-readiness law governs. It skips a barrier where a release threw while the replay started, which may fail that replay. Core now reports a throwing `unloadSubset` uncaught and fails nothing with it; the subscription lifecycle, replay, Effect disposal, includes-temporal, and subset-error oracles check the uncaught report, once, with the adapter's error. The request-authority oracle now also generates requests that throw while they start, the last history only TLA+ covered, with an error-identity law: a failed request's participant reports that request's own error. It found that a superseded failure whose recovery read threw at its start reported the start error instead (review finding 6). A cost law then checks, after loading is quiet and a bounded repair is available, that one invalidation reads no full source and issues at most one chain per ordering epoch move (in the fake, a prefix, a tie, and a refill); a mutant that always repairs with a full-source read fails it. The replay-refinement section's status law requires a quiet live query and its peer to stay ready when no load failed. Limits: whether a release runs the graph at once or defers it to a transaction belongs to the scheduler, and the oracle generates both forms without checking the scheduler's choice; the fake's rows do not move except for the unexpressible boundary; publication holds stay with other tests; the laws do not judge requests issued before a truncate; a hung authoritative request suspends the leak law for its unfinished chain; a recovery start that throws during a superseded failure is rare in the campaigns, so a named replay owns it. | + +| Indexed predicate filtering | Complete for bounded dotted-path collision | An independent row filter checks direct predicates and public callback predicates for a dotted scalar field beside a nested path, with and without an index. Selected-field ordering checks the same distinction through `$selected`. | +| WHERE predicate publication | Complete for bounded predicate and sync-transaction grammar | The contract, Kleene reference evaluator, snapshot and change-history grammar, three subscriber consumers plus direct snapshot, and per-commit key-set refinement check are literate. A fixed same-key update checks live-row and direct-subscriber payloads; a focused descriptor boundary checks stored-row prefilter safety. Generated cleanup and restart histories for filtered subscribers remain open. | +| Joined result keys | Complete for bounded two-source key grammar | The contract, nested-loop pair model, delimiter-, number-like, infinite, and `NaN` key grammar, public join driver, and per-checkpoint pair and key-count check are literate. Joins over subqueries, more than two sources, custom `getKey`, and optimistic mutations remain outside this owner. | +| Grouped result keys | [Grouped result key oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/query/group-result-key-oracle.property.test.ts) | A grouped live-query Collection gives each group one key of the Collection key type, a string or a number, whatever the group value. Maintainer decision (2026-10-27): grouped keys match the declared key type rather than widening it to nullish keys. Group values come from strings, numbers, `NaN`, `null`, `undefined`, booleans, a bigint, strings that print like those values, and strings in the reserved `\u0000tanstack-db:` namespace; histories move, delete, and insert rows. After preload and each synced change, the (group value, count) pairs equal an independent partition by type and value, every key is a string or number, no two groups share a key, and a group that survives a change keeps its key. Reach (minimum 5, per campaign of 80): a nullish group published (32 to 38), a group value published beside a string that prints like it (18 to 21), and a key kept across a change (49 to 53). RED on the parent commit: keys `undefined`, `null`, `false`, and `10n`. Mutants that encode with `String(value)` (lookalike keys collide) or that encode only nullish values (boolean keys) fail. Encoding only the internal D2 key is equivalent in this domain, because the Collection keys rows by the public group key. A focused witness in `collection.test.ts` checks that `sync.write` rejects `undefined`, `null`, boolean, and bigint keys before staging them, and that a later valid write commits alone. A sync adapter that writes such a key breaks its contract, so this is a fail-fast invariant witness under AGENTS.md ("Separate Valid Edge Cases from Contract Contradictions"), not a generated owner; SortedMap never receives these keys. Not covered: Dates, `-0`, objects, and multi-field groups, whose values the aggregate select oracle owns; real sync adapters, since the source is a controlled mock. | +| D2 Index storage | Complete for bounded prefix grammar | The contract, plain-`Map` multiset model, prefixed and unprefixed value grammar, `Index` driver, and per-addition `get`/`has` check are literate. Compaction, presence tracking, and structural payloads remain outside this owner. | +| Pooled live queries | Complete for bounded eq-filter grammar | The contract, independent `eq` model, live-query Collection second formulation, sync/optimistic/mount/cleanup-restart grammar, observer driver, and per-step refinement check are literate. On-demand and persisted sources, `DbClient`, Suspense, and every clause beyond `eq` conjuncts keep the live-query Collection. | +| Live-query deferred acquisition | Complete for bounded synchronous grammar | The law, independent model, history grammar over source states, shapes, and depth, real-Collection driver, and per-step refinement check are literate. Pooled views and DbClient stream preloads have their own blocks. The grammar also adds a peer consumer starting the source and a source truncate before the first subscriber; pinned blocks cover a subscriber that outlives cleanup, reads that wait for readiness, and release of two independent descriptor sources when one start fails. Asynchronous sources, several live-query consumers, and general failure histories stay open. | +| Flat-row change tracking | Complete for bounded flat-row grammar | Flat and proxy trackers and an independent change model run the same generated callbacks. Nested values fall back to the proxy, which its own oracles own. | +| Production error messages | `packages/db/tests/production-error-messages-oracle.test.ts` | Every public error class keeps its development message for frozen sample inputs, and its coded production line shows the JSON of each shown input, even for hostile inputs. Every plain `Error`, `TypeError`, `RangeError`, or `AggregateError` site in `packages/db/src` keeps its frozen development template, has a code, and passes each showable interpolated expression or one of its sub-expressions; the TypeScript checker keeps plain objects, such as rows, out of production lines. A projection of an object, such as `Object.keys(handlers)`, is not required. Every `console` call whose argument holds library text is coded, development-only with its text frozen per guarded region, or a development-only decoration of a value. Text that reaches the console through a parameter, such as `setErrorState(message)`, is coded where it is written; the census cannot follow it. `scripts/test-production-errors.mjs` checks that a production bundle drops the full text and every development-only hint, in ESM and CommonJS. Errors built from a caller's value stay outside this owner. | +| Lazy target path identity | Focused compiler boundary | A same-source union/coalesce witness keeps dotted and nested demand paths distinct during target deduplication. | +| Correlated include path identity | Focused public route-context witnesses | One-level and nested includes keep dotted and nested parent paths, including ancestor aliases, distinct. Conditional result paths receive separate routes. Fixed fixtures cover initial reads and selected source updates; other recursive source forms and arbitrary path segments remain outside this witness. | +| Alias scope identity | Generated cross-scope alpha-renaming and finite exact-output refinement | Optimizer copies, wraps, and collapses keep `SourceId`; captured refs retain lexical bindings under ancestor shadowing; ancestor/descendant source declarations need distinct builders while sibling placements may reuse one; public aliases remain visible. The bounded paths and remaining gaps are in the owner row. The [ad5e0108 review](oracle-reviews/issue-2072-alias-shadowing-ad5e0108.md) adds finite self-join, joined-context, and captured whole-row witnesses. The [outer-join follow-up](oracle-reviews/issue-2072-alias-shadowing-outer-joins.md) covers absent RIGHT/FULL sides and captured ancestor join operands. | +| Join equality and cold acquisition | Complete | Independent cold relational recomputation, acquisition evidence, established equality domains, replacements, and scan/index routes are literate. | +| Opaque backend pagination | Complete | The full-relation value model plus opaque token, cache generation, publication, browser acquisition, and live-window integration owners are literate. | +| Electric and TrailBase | Complete | Electric replica and recovery models, installed-SDK HTTP delivery, PostgreSQL serialization, and TrailBase's controlled RecordApi/native-stream lifecycle are literate with their real-provider limits intact. | +| PowerSync | Fixed tests only | Patch conservation, effective-update receipts, metadata and falsey changes, declared-view keys, transformed schema output, logging, cleanup, and native SQLite reach are covered by fixed tests in `correctness.test.ts`; no oracle owns them (tracked in #2264). | +| SQLite persistence and native hosts | Complete | Persisted hydration/replay and ownership, shared-handle driver transaction laws, OPFS page and diagnostic state machines, and the 113-law native conformance manifest are literate. Native execution remains distinct from registration and shim evidence. | +| Offline execution | Complete | FIFO retry, scheduler eligibility, leadership replay, transaction settlement, and typed wire serialization are literate. The [interruption settlement oracle](https://github.com/TanStack/db/blob/main/packages/offline-transactions/tests/interruption-settlement-oracle.property.test.ts) moves one leadership loss, offline change, or disposal across individual microtask turns after one to three commits on disjoint keys, then recovers by regaining leadership, going online, or restarting over the same storage. It requires that no provider call or new outbox admission starts while fenced, that each admitted ID reaches the provider once and in commit order, that a fulfilled commit's row is already gone from storage, that commits settle with their provider outcome after a regain or reconnect, and that the outbox ends empty. A reach check requires interruptions before admission, between admission and the provider call, and after it, for each interruption. Mutants that drop the execution loop's fence, resolve before deletion, drop the admission check, or check admission before awaiting initialization fail it. Open there: shared keys, providers that answer later, and overlapping old and new executors. The settlement oracle's generated FIFO histories draw each transaction as one record (outcome; manual commit, auto-commit with the option set or omitted, or an `OfflineAction`; receipt read or not), use `when('settled')` as an auto-commit's receipt, and require that no failure escapes as an unhandled rejection, also when nobody reads the receipt, and that settlement logs no failure again and writes no debug output. An empty transaction on any commit path may precede each one; its receipt and completion waiter must fulfil without reaching the provider or the outbox. A manual transaction that writes a row and rolls back before it commits may also precede each one; its waiter must reject with an Error that names the rollback. A focused witness rolls a transaction back while it persists, for both provider outcomes: its waiter must wait for the executor and report the executor's outcome. After every transaction settles, a new waiter for each one must reject within a turn rather than hang. An unread receipt still settles with its own outcome when read afterwards, and a caller may register its completion waiter before admission on every path but `OfflineAction`. A focused witness fails the admission outbox write on every commit path and requires the receipt to reject with the storage error, without an unhandled rejection or a repeated log; a failed outbox write stops the executor, so generated histories do not continue past one. Open: the generated failure source is the mutation function only; manual commits always follow their own `mutate()` at once, so a pending manual transaction behind a failing shared-key sibling is not generated; a throwing `mutate()` callback is not generated, because core keeps its transaction pending; a rolled-back-before-commit transaction is generated only on the manual path, with its own key and its waiter registered after `mutate()`, so a secondary rollback of a pending transaction by a failing sibling, whose waiter gets the generic rollback Error, is not generated. | +| Frameworks | Complete | Shared live-query and infinite-query models are literate. Each framework keeps its own realm, ownership, and scheduling driver. | +| Structural values and ordered primitives | Complete | Structural hashing, deep equality, comparison, cursor denotation, index refinement, and query-identity output equivalence are literate. | +| Mixed-kind ordered prefixes | Bounded | Eager BasicIndex and BTreeIndex paths compare ascending and descending direct and live-query windows with an unindexed scan across mixed value kinds and paired source changes. One retained on-demand BTree history checks settled windows after a number/bigint transition, a finite request for a missing row, and a cursor request after index eligibility changes. Other provider paging histories remain open. | +| Boundary refinements | Complete | Cleanup/restart admission, metadata publication, retained state, acquisition cells, D2 source reconciliation, top-K support windows, and nested Query work bounds are literate. | +| Small structures and test mechanics | Complete | SortedMap, cleanup appointments, and guarded replay are literate. | + +### Guide-audit boundary handoffs + +The cleanup/restart owner checks ordinary subscription admission and deferred +acquisition resumption during held adapter cleanup. Its eighteen cases cross +loading/ready/error status, eager/on-demand sources, and direct, snapshot, or resumed +admission. The three-cut model requires rejection before subscriber ownership +or acquisition, followed by successful explicit admission after cleanup settles. +Twelve Effect receiving cases cross both source modes, all three prior statuses, and +cleanup on the first or second joined source. A late Effect returns disposed, +reports the cleanup once, and retains no subscription or cleanup observer. A +new Effect after settlement is admitted. The original implementation fails all +eighteen admission cases; a guard limited to fresh subscribers misses six +resumption cases. A guard without earlier Effect cleanup observation fails the +twelve receiving cases during construction. The owner covers these controlled +adapter cuts, with cleanup rejection and terminal-listener restart covered by +its existing histories. It does not establish arbitrary concurrent framework +schedules or actual provider shutdown. The [migration feedback review](oracle-reviews/2026-10-22-dbclient-migration-feedback.md) +records the rejection policy and exact calibration. + +The eight-item follow-up at reviewed head `16824f677` extends the same owners. +The cleanup/restart owner adds six plain/live-query logical-demand cases at +idle, ready, and cleaned-up cleanup start, plus six admission outcomes that +distinguish a pending cleanup promise from an actual replacement sync run. +The deferred-acquisition owner adds render-deferred eager/on-demand sources and +explicit deferred subscription/resumption histories across both query shapes +and one/two live-query levels. The React descriptor driver receives resumption +after a real abandoned suspended render through the existing startup judgment. +The observer-history owner adds eight failed-setup histories, with both modes, +one/two failures, cleanup rejection or real sync-start failure. Client, status, +and layout leases are checked immediately, then retry, publication and release. +Two late-listener seed failures retain a healthy peer and exclude the failed +listener from later publications. Eight stream-preload histories cross pending +or successful streams, both source modes and a render deferral. The real React +streaming test receives the on-demand premise while retaining server rows until +browser acquisition completes. +The pooled release owner adds twelve new/retained/released partition histories +through held loading/ready cleanup and two startup-cleanup reentry histories. +The subscription boundary owner adds 28 resumption/retirement histories crossing +loading, ready and acquiring notifications, cleanup, source mode and render gate. +The shared token-count model forbids restoring a retired owner. Deferred-demand +cancellation fixtures now create logical demand before explicit resumption; +render-owned queued loads remain covered by the DbClient fixture. These finite +extensions do not establish arbitrary async/provider schedules or new framework +error policy. The [follow-up audit](oracle-reviews/2026-10-22-dbclient-migration-feedback.md) +records original RED, hostile controls, and the unchanged rejection decision. + +A subsequent current-head summary exposed cleanup during ownership transfer. +The deferred-acquisition owner adds 192 surviving-owner histories crossing both +source modes, shapes, depths, ordinary/resumed/deferred readers, render gates, +source loading/ready cleanup, and explicit restart later or from terminal +publication. They check the complete post-cleanup status trace, ownership, +replacement acquisition, readiness and rows. The previous implementation fails +44 assertions; retaining ownership without fencing first-demand cleanup fails 16. The controlled grammar does not claim arbitrary recursive cleanup or async +provider work before acquisition. This owner retains those wider witnesses. + +The ten-finding high review adds generated error-provenance and partial-setup +histories to the existing owners. The deferred-acquisition owner crosses ready, +returned-error and thrown starts, before/during handoff, with defined/absent +causes. Its independent start-handoff judgment also feeds real React ordinary +and infinite hook drivers. Each hook now has independent fixed/random campaigns +and reach minima for returned/thrown source failures before/during commit, +including absent causes. They compare readable error/boundary behavior after +commit, one source start, and ownership immediately after unmount, before GC. +Independent startup owns a pre-commit throw; the later hook reads error status. +The restored broad-rethrow defect fails both observations for each hook. +Explicit DbClient preload continues to +report source startup failures. Pooled lifecycle histories add released +partitions whose synchronous late cleanup callback returns or throws, including +1–3 healthy status peers. The same pooled release owner now generates +bootstrap callback return/throw, Error/undefined, zero through three healthy +observers, callback position before/after those peers, and `gcTime` 1–3 through +both ordinary compiled and pooled core observer paths. Failed granular +bootstrap retains no unreturned ownership after the grace period; surviving +peers retain their source until release. Held source cleanup gives every +admitted wholesale observer one terminal error snapshot and callback despite +a sibling throw, then preserves the exact reported failure at settlement. +First-return, surviving-peer, cleanup-start, settlement and final-release/GC +cuts distinguish ownership rollback from all-peer status delivery. Original +production and repairs that implement only one of those obligations fail the +respective public comparisons. These are controlled core receiving paths, not +framework scheduling or remote-provider shutdown evidence. +Observer histories add early capability or sync +failure, one/two failed attempts, both modes and reentrant surviving peers, +then explicit retry/publication/release. First-party persisted readiness does +not throw at registration; that premise is a controlled core-capability claim. +The lifecycle owner adds held/immediate cleanup and actual post-transfer +notification throws across 1–3 demands, checked after replacement readiness. +Original and narrow-mutant assertions calibrate these cuts. All six new +properties have declared reach, paired fixed/random campaigns and guarded +seed/path replay. The [high-review audit](oracle-reviews/2026-10-22-dbclient-migration-feedback.md#high-review-of-ten-findings--2026-10-23) +records counts and finite limits. + +Two historical high-review claims, H04 and H08, remain `confirmed-open` +evidence gaps, with no reproduced contract counterexample or closure credit. +They are not accepted exclusions or refuted by the neighboring controls. For +H04, the pooled lifecycle owner needs a legal eager-source `subscribeChanges` +throw that reaches the partition's terminating catch and whose contract +promises same-partition, same-run recovery; current eager +`reportError()`/`reportHealthy()` histories keep both shared views ready +and recover their health. On-demand sources +are ineligible for pooling, so their throwing probes do not reach this path. +For H08, the lifecycle/deferred-acquisition owners need a synchronous +post-transfer notification throw that keeps retained demand detached past +supported replacement readiness; generated real throws currently acquire +afterward through the status listener. Resumption can skip its immediate +restart when that notification throws. An implicit retry without another +readiness signal is not established by the current contract. A broader +recovery promise needs an explicit decision; a legal failing trace under the +existing promise needs enforcement in these owners. +Arbitrary provider outages, callback programs and other frameworks' error +policies remain outside these new witnesses. The approved cleanup admission +rejection policy remains unchanged. + +The Collection lifecycle history owner includes a 26-cell cleanup-restart +matrix. It crosses explicit and default eager sources with absent or present +loaders, empty or nonempty replacements, and no interruption, truncate during +loading, or a failed first load followed by `reload()`, which must keep the +waiting caller for the new sync run. On-demand controls use +settled subset acquisition. A cleanup-detached eager demand remains active, its +`onLoadSubsetResult` promise stays pending through these pre-ready cuts, and it +starts no physical subset acquisition before fulfilling with the exact +replacement. The focused Query Collection test receives the successful-restart +law through automatic Collection GC, an eager Query Collection, and +`DbClient.preloadLiveQuery`. Demands first admitted after eager restart, +terminal eager failure, retired or concurrent eager owners, and same-turn +interruption schedules remain open. The broader generated lifecycle histories +cover those ownership operations only in on-demand mode. +Before the truncate repair, the four eager loader-present truncate cells +fulfilled at the loading checkpoint; their loader-absent controls stayed +pending. This distinguishes sync mode from the adapter's incidental shape. The +[issue #2163 audit](oracle-reviews/issue-2163-eager-restart-truncate-bb2122012.md) +records the exact-head ORC-001–014 outcomes, RED/GREEN calibration, review +ledger, and remaining limits. + +The includes-space owner (`packages/db/tests/query/includes-space-oracle.test.ts`) +now varies leaves per twig across 0, 1, and 10. Public leaf rows change from +0 to 10 to 100 while 13 Collection facades remain. This protects facade +ownership for the fixture's fixed root/branch/twig topology; route churn, D2 +demand retention, and a heap bound need separate witnesses in the includes +owners. + +The virtual-row-fields owner +(`packages/db/tests/query/virtual-row-fields-oracle.test.ts`) checks all four +public fields on row roots and keeps inline values in their selected shape. +Its finite 1,056-history attribution grammar crosses single/joined sources, +structured projections/spreads and functional projections/spreads/direct +source returns, and none/each/all reserved-field collisions. Each form crosses +direct output, QueryRef depths one/two, joined and functional QueryRef +consumers, either union branch position and a union consumed through QueryRef. +Eight further histories cross nullable later spreads, spread order and which +source has a pending write. Comparisons at +preload, a held optimistic source update, and settlement enforce the maintainer +rule: structured source spreads and direct functional source returns retain +source metadata; new objects and ordinary projections receive output Collection +metadata. Each collision is an independent axis, including `$collectionId` by +itself; selecting a reserved name cannot choose a source. The reach check +requires observations from joined and single pending sources, colliding projections, +direct functional returns, QueryRef and union pending observations, and a null +later spread. Authored ordinary fields can select source pending state without +changing output metadata. The original source-guessing compiler fails the +same comparisons. Supplemental regressions for two spread orders, unmatched +RIGHT joins, facade projection metadata and inline values live in +`virtual-row-fields.test.ts`. This boundary does not establish arbitrary +nested projection histories beyond these two QueryRef levels, every join kind under source changes, framework +receiving paths or provider behavior. + +The attribution driver compares both authored rows, every ordinary field and +all four virtual fields, then deletes the non-first row. Half its histories +configure independent projected keys; default-key differential evidence remains +supplemental. Another 264 histories populate both union branches with numeric +1 and string "1", different source Collection IDs and equal labels. Either +branch holds a write while its peer is deleted/reinserted. Branch order and +QueryRef consumption vary. Each direct/QueryRef × source/output attribution × +pending-side path must reach held, reinserted and settled comparisons. Fourteen +checker controls reject omitted/duplicate peers, missing ordinary IDs, wrong +keys, union-wide source attribution and stale pending/origin fields; reordered +allowed results pass. Evidence and review corrections are in +[implementation chunk 3](oracle-reviews/2026-10-28-pr-2171-oracle-chunks.md). + +A further 112 histories recompute changing left-join topology while a left +write persists, through join-key changes or delete/reinsert. They cross five +projection shapes, useful spread orders, independent success/rejection (including +both with distinct errors), and either settlement order. A right write overlaps +and receives a same-key queued sync confirmation. Full snapshots, every callback's +installed read, change/previous-value payloads and a downstream metadata projection +are compared at initial, held, topology, acceptance, settlement and later remote +cuts. Aggregate callback deltas reject whole-batch repetition; batch partition +remains permitted. Eight calibration cases reject stale reads/events, wrong +previous values, duplicate messages/batches, omissions and lost earlier failures. +Per shape/topology reach requires both receiving layers, both rejection states +and both settlement orders. See [chunk 4](oracle-reviews/2026-10-28-pr-2171-oracle-chunks.md). + +Another 132 histories receive the same metadata grammar through Collection +facades and inline toArray/materialize controls. Two parent routes share a +bucket, move to a populated or empty destination, retire the last route, then +reactivate while a child write persists. Success/rejection and later source +updates are observed. The primary includes owner supplies an independent +active-interval model: retained routes share one ready facade, retired handles +stay ready/empty, and a new interval gets a new handle. This assumes no +facade-local optimistic mutations. Every shape declares active empty readiness +and nonempty child receiving cuts. Six lifetime and four inline-value hostile +controls reject wrong observations. See [chunk 5](oracle-reviews/2026-10-28-pr-2171-oracle-chunks.md). +Functional callback-input observations now have a separate receiving boundary +in `virtual-row-fields-input-oracle.test.ts`, sharing source metadata judgment +with the existing root/Effect owners. Its finite 33 authoring descriptions cross +seven QueryRef/join/union consumers through root and correlated inline-child +compilation (462 histories), plus two direct-source controls. Each compares +immutable callback captures and their ordinary derived output at initial +readiness, held mutation, settlement and remote next use. Complete before/after +source facts permit retraction evaluation; startup LEFT joins may expose an +absent side, while established outputs require both typed-key rows. Reserved +metadata is judged by presence/readable value, with ownership flags diagnostic; +ordinary own undefined remains distinct from absence. Another 66 histories keep +nested inline array/materialized authored values, and 33 reject compiled +Collection inputs before callbacks. Per-history/path/cut reach is mandatory even +under unrelated replay seeds; unentered calibration-only runs stand aside. +Unchanged production exposes 203 correlated-child violations: new-shape fields +leak and source-spread collisions spoof genuine metadata. Root receivers and +nested inline preservation pass. All 2,153 declared states reach; 17 controls +and independent seeded/unentered/partial-selection checks calibrate this owner. +See [chunk 17](oracle-reviews/2026-10-28-pr-2171-oracle-chunks.md) for exact +fingerprints and independent evidence. Production repair is deferred under the +[RC oracle challenge](oracle-reviews/2026-10-28-pr-2171-oracle-challenge.md). +This extension does not establish arbitrary descriptors/prototypes, greater +recursive depth, same-source overlapping transactions, other dynamic +join kinds, truncate, provider scheduling, framework receivers or construction +failure cleanup. The neighboring includes publication owner removes virtual +fields from its own comparison; the new metadata timeline supplies this bounded +core publication judgment without claiming those broader paths. + +The generated persistence option type/admission owner is +`packages/db-sqlite-persistence-core/tests/persisted-options-type-oracle.ts`. +It generates row shapes (1–4 scalar, nullable, array, or nested fields), six +key domains, field optionality/readonly modifiers, utility signatures, numeric schema versions, +and network deadlines. Each shape retains 30 construction forms across +local/synced, schema-free/transforming schema, typed-spread/inline arguments, +and inferred/partial/full type arguments, including contextual schema callbacks. +Twelve configured forms cross eager/on-demand/network-first options with schema +and sync modes; network-first remains eager. Two schema-free forms supply a +phantom schema generic without a schema value and still preserve row input. +Eight modifier neighbors cross mutable/readonly first fields with schema and +persistence modes, bringing each shape to 52 core or 53 browser constructions. +Transforming schemas accept their output as input, as required by the schema +guide: their distinguished field narrows string | number to number, and output +optional fields remain optional on input. Exact input equality distinguishes +the two domains; inserting an output row is a legal neighboring observation. + +Independent type laws compare insert input, complete published row types, +exact keys, user utilities, added local persistence capabilities (including +optional leadership/reload signatures), option types, and contextual getKey / +markReady callbacks. The browser driver uses real Query Collection options. +Declared schema-compatible-input, readonly-input, readonly-output and +mutable-fields reaches occur structurally in every generated history. +Its independent closed capability model checks direct-write signatures, +observer state, refetch parameters and a non-any Promise-array envelope. A +supplemental before/after relation preserves the full Query utility types; +QueryObserverResult's internal payload has no independent model here. +Fresh-object sync rejection uses an exact unsuppressed overload diagnostic and +a legal neighbor that changes only sync, so a collateral option error cannot +substitute for the promised rejection. + +Core and browser `persisted-options.test.ts` runners share the grammar/model +and use their respective receiver tsconfig. Package and root `test:oracles` +commands select them. Both run 12 histories with seed 2259 and 12 with a fresh +seed, measure per-path reach, and accept the shared seed/path/property replay +interface (`persisted-options.types.core` / `.browser`). A shared semantic +builder retains source and declaration diagnostics while caching dependencies; +timeouts scale with the campaign multiplier, and histories yield for worker RPC. +This owner does not execute runtime parsing, adapter I/O, sync, or publication, +and does not check built declarations, npm resolution, other host packages, +every TS version, arbitrary extra-property structural assignment, or the +internal semantics of trusted PendingMutation/LoadSubsetOptions carriers. +The [type-preservation audit](oracle-reviews/pr-2259-persisted-option-types.md) +and `scripts/calibrate-persisted-option-types.mjs` preserve parent RED, GREEN, +legacy-suite survival, hostile-mutant evidence, and runnable overlay recipes. +The readonly-row overlay strips user-property readonly modifiers from get's +return while retaining readonly virtual fields. The prior grammar accepted it; +the modifier histories reject it at the shared exact published-row comparison. + +The maintainer chose rejection for local caller utility collisions. The same +owner's runtime admission grammar checks the three reserved names +acceptMutations, getLeadershipState and forceReloadSubset, individually and in +combinations, against both public core/browser wrappers. It crosses local/synced +options, schema presence, function/undefined values, and enumerable/non-enumerable/inherited +properties. Rejection must be an InvalidPersistedCollectionConfigError before +persistence resolution; ordinary local utilities and synced adapter utilities +remain available. Each fixed/fresh campaign measures all ten admission states +under `persisted-options.utilities.core` / `.browser`. No Collection is created, +and no sync, SQLite I/O, native-host or publication claim follows from this path. +The versioned audit records the user decision, original overwrite RED, GREEN, +and guards that check only own/truthy properties or omit a reserved name. + +Other existing fixed type checks cover adjacent compile-time boundaries: +`packages/db/tests/mutation-handler-type.test-d.ts` (fixed tests; no oracle owner (tracked in #2264)) checks sibling +Collection/key/utilities in mutation handlers; and +`packages/db/tests/query/virtual-row-fields-reference-types.test-d.ts` retains +concrete helper examples. The generated public type owner is +`packages/db/tests/query/virtual-row-fields-types-oracle.test.ts`. Its 24 finite +row descriptions vary key domain, scalar type, nested optionality and +discriminated union membership. Shared runtime/type authoring dimensions cross +five projection shapes and eight compositions; seven child shapes cross +the legal facade/toArray/materialize/findOne forms; array/Date facades are +rejected inputs. Each generated body also runs public query/index construction, +with an explicit index type and failure-preserving cleanup. Two admission +controls reject opaque facades and optionless indexes at their actual boundary. +A reusable recursive type signature checks +values, exact keys, required/optional and readonly modifiers per union member, +without rejecting duplicate equivalent union members. Public-source imports +compile through the actual TypeScript compiler; all diagnostics retain their +file, span and code. Seven hostile controls challenge value enrichment, +nullability, one-branch readonly loss, nested any/metadata/modifiers and Date +metadata any. Their rejection requires failed type-law constraints, not syntax +or setup errors. The normal test:oracles command selects this owner. + +Chunk 24 extends this same owner with `virtual-row-fields-receiver-types-oracle.ts` +and the native compiler support `virtual-row-fields-receiver-types-support.ts`. +Its 34 context families cross the same 24 source descriptions: 14 recursive +ordinary-selection callback paths, 16 direct/QueryRef child materializers and +four positive source-row neighbors. All 816 consumers admit without diagnostics. +The independent declared-shape law checks 4,608 actual callback, rest, parent, +query-output, Collection/get, inline and published-facade markers. All 6,240 +syntax/admission/marker reach states execute once. The callback input retains +required ordinary routeGroup until real rest destructuring removes it; nested +ordinary fields named like virtual fields keep their declared types. + +Unchanged production has 684 intended RED receiving histories: 672 ordinary +callback/parent/inline metadata leaks and 12 structured source-spread union +precision losses. The other 132 receiving histories pass. The original 24 +campaigns and the separate 374 strict chunk-21 callback markers remain intact. +The paired source/direct-facade neighbors pass except for the recorded union +spread defect. Forty-three compiler/checker/AST controls reject fabricated input +metadata, lost genuine metadata, routing or nested fields, wrong optionality, +modifiers, containers, absence and displaced or annotated inference markers. +The finite matrix stays mandatory under partial selection and unrelated seeds; +checker-only selection enters no native matrix. +Independent review found that a marker moved into an ordinary map helper +could observe a reconstructed clean record while the real functional input still +leaked metadata. The original syntax checker accepted that source, and all type +judgments passed. The shared-grammar calibration first reproduced that false +green. The checker now requires the marker's arrow to be the fn.select argument, +and requires a rest declaration directly in that same callback block. + +This static slice does not infer allocation identity or promise exact provenance +for opaque source-valued callbacks or top-level reserved-name collisions. The +executed equal-type direct/copy experiment records identical public callback, +query, downstream-input and output types. It does not change the runtime law or +weaken any existing strict ordinary-shape marker. Additional static receiving +functions are compiled, not invoked; runtime callback execution stays with the +existing metadata input owner. No production repair or closure is claimed. + +Unchanged production fails the nullable whole-row facade comparison in each of +the 24 descriptions: its type loses the empty-row alternative and presents +ordinary fields as required. The metadata runtime owner receives that law with +a legal correlated include, initially matched or unmatched, then two match +transitions. Empty facade rows have required output metadata; inline empty +values remain empty. That runtime path passes. Production type repair is +explicitly deferred until all RC oracle chunks finish. Built declaration/npm +resolution and adapter Promise admission remain receiving work under the +[RC challenge](oracle-reviews/2026-10-28-pr-2171-oracle-challenge.md). These source +type checks do not prove runtime parsing, I/O or arbitrary publication paths. + +The shared-driver fairness owner now distinguishes K=1 from K=2 at the second +hydrate and checks full logical persist FIFO order. Its Node and Chromium +production drivers apply one comparison, +`findSharedDriverFairnessLawViolations`. It checks hydrate admission and +completion, public rows rebuilt from the scenario seeds, persist FIFO order, +the non-preemptible running persist, and the K=1 cut at each hydrate. The K=1 +reference bounds completed persists only from above, so a synthetic preempted +running persist passes it and fails only the non-preemptible law. The +[Chromium OPFS receiving spec](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/e2e/shared-driver-fairness-oracle.opfs.spec.ts) +states no expected results of its own and adds a cold SQL read. It runs a +neutral history, a held-BEGIN storm with every persist before the hydrates, and +an interleaved storm with hydrates queued ahead of later persists. The +persist-first scheduling fault fails both storms at the shared comparison. This +closes the bounded real-OPFS handoff at logical adapter completion for those +histories. It +does not establish durable write order after reopen, naturally occurring +contention, a browser matrix, multi-context coordination, elapsed-time +latency, or unbounded eventuality. + +The D2 source reconciliation owner checks its helper and controlled Effect and +live-query consumers. Fixed truncate/replay and cleanup→preload graph-restart +cuts run on both eager-index and scan-fallback routes. Its generated histories +still do not cover arbitrary graph shapes or real provider event delivery; +the Electric real-provider E2E owner must supply that external premise before +a cross-boundary claim. + +PowerSync's correctness owner reaches native local SQLite and records an +SDK-delivered watcher callback after a metadata-bearing `Collection.update`; +registration is wrapped for observation while delivery uses the SDK callback. +The oracle checks the public and SQLite rows and the exact queued CRUD patch. +Its held watcher callbacks are a controlled delivery premise, and its trailing +no-op and tracked metadata-only +cases enter through the public transactor with synthetic mutations rather than +`Collection.update`. Neither path establishes remote backend upload or that +Collection updates can construct those zero-field mutations. + +The Query DB nested-shape work owner checks both an all-reachable finite tree +and an on-demand irrelevant-branch variant. At preload, one or three +disconnected branches leave the public tree and delivered rows unchanged and +cause exactly one acquisition attempt and one Query provider call per child +level. Provider scans, internal index traversal, facade allocations, and +elapsed time remain outside this owner; DB includes-work counts source +Collection reads and delivered changes, not internal index traversal. + +The DB includes-work owner +(`packages/db/tests/query/includes-work-counter-oracle.test.ts`) also generates +an ordered parent (a page, a `findOne` with or without an `offset`, or an +`orderBy` alone), optionally narrowed by `setWindow` before start and moved by +it later, with `distinct`, a static parent filter by `eq` or `inArray` on the +correlation field, and a static child filter on the grandchild correlation +field. Its child source Collections are local or on demand; the on-demand test +handler stands in for both the sync adapter and the provider. Child forms are +`materialize`, a child Collection, a child `orderBy`, a child `limit`, a child +`findOne`, nested includes, and limited nested includes. Histories insert, +delete, and move parents, insert and delete children, and move the window, with +60 base runs per campaign; a reconstruction check requires at least three +executed fixed cases for each premise, and every checkpoint asserts +`getWindow()`. A `findOne` keeps its compiled limit (1, or 0 for +`.limit(0).findOne()`); an ordered `findOne` has a movable window, and an +unordered one is a fixed one-row result that accepts only its compiled limit +at offset 0. Rows must equal +a relational model. Preload may load only the children of the shown rows' +groups, and a step only those of rows that entered the window and were not +shown before it. A static filter creates static child demand before the +query runs only when no enclosing query is windowed or a `findOne`, and either the query is neither windowed nor a `findOne`, or it is a `findOne` that shows one row for one named correlation value, with no `offset`, and offset 0 at start if a window was requested before start. Pinned witnesses show each case +the rule closes: a window requested before start (12 child rows for a bound of +2 before the rule), grandchild demand below a windowed parent, a `findOne` with +`limit(0)`, and a `findOne` moved past its one named value before start; a +detail page whose window before start is at offset 0 still starts its child +acquisition attempt at once. A focused test requires `setWindow` to throw +`SetWindowFindOneLimitError` for any other `findOne` limit and +`SetWindowRequiresOrderByError` for an offset move on an unordered `findOne`. A `findOne` detail page with on-demand parent and child starts +its child acquisition attempt beside its parent's. On main, a late page +delivered 120 child rows for a bound of 9. Routers that add the row after the +window fail the generated campaigns; routers that add every row in an edge tie +group fail a pinned collapse and the detail page; a child that takes over +`setWindow` fails the rows law. Focused tests require a window inside a `from` +or joined subquery not to answer `setWindow`. + +Recorded limit (#2151): while an index-backed parent top-K refills within one +graph run (a held row moved out, a tie group loaded at the window edge, a +forward window move), it can briefly hold a row outside the window it shows +once its ordered acquisition completes, and load that row's children; the +public snapshot stays correct. A refill checkpoint is a model observation that +combines an ordered acquisition and whether it completed: the parent Collection +delivers more change batches than the initial load or the step's own parent +write. The generated campaigns skip only its work bound. In the fixed campaign +(60 runs, seed 2137), the work bound checks 285 of 290 checkpoints strictly: +preload 60 of 60, parent inserts 45 of 45, parent deletes 34 of 35, rank moves +43 of 43, child inserts 36 of 36, child deletes 37 of 37, backward window moves +18 of 18, and forward window moves 12 of 16. Six replays pin the known traces +and allow exactly the one extra child row each shows; with no overshoot +allowed, each fails. Four pinned forward moves (local and on-demand children, +with and without `distinct`) allow none. Each generated campaign counts the +forward moves its own runs check strictly and fails if it counts none. + +Open in-scope paths, all owned by this includes-work owner unless noted: +refill checkpoints need the #2151 fix (holding include demand until the +ordered acquisition completes) before the work bound can check them; includes +inherited from a `from` subquery and inputs to `fn.select` still route from +every row before the window, and need generated forms under the work bound; the +edge tie group router is caught only by pinned cases, and needs a generated tie +at the window edge on a checkpoint without a refill; a child compile error after +the parent's `orderBy` still leaves the parent's auto-index (owner: compiler +tests), and needs a focused witness that the index does not outlive a failed +compile. + +The ordered-lifecycle owner also crosses deletion and retargeting of the first +window's join marker after that window is public but the live-query Collection +is still `loading` on child demand. The next ordered page ends at null; a later +unfiltered full-source request supplies the new eligible row. A finite join +model checks the final public window and the net delete/insert change events, +and each callback's rows must match its changes. Four more histories hold the +fallback provider Promise after root 3's writes apply, then retarget marker 2 +to root 2 before settling it, with pending or settled child demand. They check +both public cuts, final rows, and every callback, while allowing a coherent +root-3 window to publish before the second marker change. These controlled +histories do not assert synchronous timing after first publication. Four +neighboring histories retarget marker 2 to root 4 while that provider Promise +is pending, then write root 4 only after releasing the Promise gate. The +independent join model checks the final window and callbacks. The adapter +observes that the cursor-page acquisition lease remains active while the +full-source Promise is pending and retires after it settles; ignoring provider +settlement fails at that held lease cut. Other marker actions, overlapping +root-source changes, and provider rejection remain open. Real adapter delivery +and an on-demand React first commit need receiving witnesses. + +Three Ordered acquisition companions own distinct controlled cuts: +`packages/db/tests/query/load-subset-replay-refinement-oracle.test.ts` checks +held replay authority and public snapshots, and its generated ordered section +checks an ordered window under load failures and throwing releases; +`packages/db/tests/query/load-subset-source-readiness-refinement-oracle.test.ts` +checks source-generation readiness, progressive old-child retention, and +retired-route publication; and +`packages/db/tests/query/load-subset-transaction-refinement-oracle.test.ts` +checks before, during, and after-commit abort boundaries with applied receipts. +These fixtures do not establish real-provider cancellation or framework timing. +The replay-refinement status driver observes initiating keyed snapshot-request +receipts rather than inferring logical demand from truncate callback timing. +At each history command and final convergence, terminal errors must also +satisfy the independent retained-demand classification described below. +Its generated histories declare and measure initiating keyed-request rejection +and keyed-reacquisition failure separately. A fresh include request issued +during marker truncate can fail terminally; failed reacquisition of an +established include or marker request retains the last publication until +recovery. The status comparison does not independently establish correct +settlement of the initiating receipt. Peer scenarios retain one history-level +failure allowance, so per-receiver failure isolation remains outside this +comparison; the owner is this replay-refinement oracle, with a needed witness +that fails one receiver's current request while comparing another receiver's +status at the same checkpoint. This is a stated observation limit, not a +retirement of the per-demand error-ownership law. + +The ordered replay owner distinguishes replay renewal from an ordinary lazy +demand created during another subscription's replay. Its adapter records the +leases held at truncate, then matches subscription identity and independently +evaluated key predicates once per renewed lease. New owners and different keys +remain ordinary demand. Terminal-error assertions run after each history action +and at convergence. Named neighbors require a new include demand's failure to +be terminal and a settled joined demand's failed replay to remain nonterminal. +The CI seed `-1751969525`, path `67:11:10:12:1:8:7:7`, reaches the former. +The normal broad campaigns did not each reach both critical failure origins +in the declared-reach audit. A second property in the same owner, +`replay.subscription-demand-origin`, generates zero to three prior replays, +marked-row rank moves and ordinary/renewal failure schedules. Its 75-history +fixed and random campaigns must each reach new demand failing during marker +replay and a settled demand's failed renewal. The adapter witnesses non-aborted +failure independently of expected terminal status. A positive terminality check +rejects ignored ordinary failures. Source-wide, subscription-blind and +always-terminal controls also fail. Broad reach counts each of its existing +seventeen required dimensions once per history. The [declared-reach audit](oracle-reviews/2026-10-22-dbclient-migration-feedback.md#audit-against-the-declared-reach-rules-from-pr-2233) +records counts, exact replay coordinates, calibration and chronology limits. +This projection covers the grammar's five numeric keys and one-level includes; +it does not establish arbitrary predicates or provider-driven replay schedules. + +The ordered-work owner has a 96-cell optimistic-departure matrix over source, +joined, and anti-joined filters; update and delete; Collection and Effect; +acknowledgement and rollback; one- and two-row windows; and indexed and +unindexed loading. Its independent installed-ID model applies one overlay, +sorts, and slices. Two indexed controls install a replacement independently of +index and query shape. A non-prefix witness installs rows 3 and 1 while row 2 +is missing: deleting 3 must publish `[1]` immediately and then acquire 2. +The six-row provider ledger includes rejected leading and irrelevant trailing +rows, so useful bounded progress differs from full-source overfetch. Eight +source/joined histories settle before the held provider answer; eight more +mutate the joined-side marker for joined and anti-joined windows. Eight +complete-local-extent histories cross ascending order, offset, a tied head, +an exhausted window, and acknowledgement or rollback. Four on-demand +ascending limit-one histories cross joined/anti-joined filters with +acknowledgement/rollback. A rank-only acknowledgement and a combined +rank-and-membership departure rollback are adjacent controls. After an +acknowledged marker departure, restoring that marker must restore both rows +and its Collection or Effect publication. +Its held-load grammar also replays an unindexed descending joined window in +which an installed null-ranked row fills the visible window beyond the settled +provider prefix. After earlier deletes and held answers, the owner compares +public rows with an independent full-source sort; a boundary-blind refill check +leaves the next eligible filler row unseen and fails at the final checkpoint. +For signalling refetches, the no-new-full-source work assertion applies to +indexed numeric orders with finite ranks. Unindexed underfilled continuations +and unexpressible null or NaN boundaries may use full-source recovery; the +owner checks their row law but does not claim bounded transfer for them. + +At the immediate cut, both consumers must publish the recomputed installed-row +window, even if it is short. Missing extent must start a useful finite +acquisition while the mutation persists. A provider sync transaction queued +behind persistence stays private until application; its publication can occur +before the applied-receipt promise resolves. The oracle observes answer-before- +and answer-after-settlement schedules, callback-visible rows and publication +suffixes, receipt/provider state at the relevant cuts, and two later task +turns. Every pending indexed acquisition must be a bounded ordered page or a +single-rank boundary probe; a source-filter request must reject an ineligible +row at a relevant rank. It rejects a repeated identical request without +progress in the same settlement phase, a full-source request passed off as +bounded refill, and a broad request after a valid refill. The +request ceiling is an emergency guard, not a fixed-point proof. Effect +callbacks establish membership; Collection callbacks also preserve order. + +The reviewed base suppressed #2169's immediate departure and gave the +controlled indexed joined-delete histories no useful refill row. The current +loader repair prevents optimistic overlays from being classified as +authoritative source-order loss. The exact mismatch ledger is gone; all +exercised histories must satisfy the model. The [#2169 review record](oracle-reviews/issue-2169-optimistic-window.md) +contains the RED/GREEN evidence and scope. The Query DB +`optimistic-ordered-refetch-oracle.test.ts` owner runs #2169's mutation-handler +`utils.refetch()` with real QueryClient observers; restoring the old loader +classification fails its immediate `[2]` checkpoint. It checks the held and +settled callback-visible windows. It rejects an unordered request whose full +predicate scope is not one rank, even if its capped answer has one row. #2148's +exact historical provider replay is not published, although the controlled +ascending joined and anti-joined limit-one path is now exercised. With #2154 +integrated, the Query DB receiver also invalidates a shifted ordered page +while the optimistic update persists. It requires one loaded-subset report, a +bounded acquisition after that report, no retraction of the immediate `[2]` +window, and final `[5, 2]` rows. Disabling the loader's report-triggered +revalidation fails at the acquisition checkpoint. This fixed history does not +cover every refetch shape or a second optimistic change during held repair. +The installed ascending/offset/tie/exhausted +witnesses do not prove those provider-refill schedules. Provider rejection, +rank-only rollback and other rank-move histories, overlapping optimistic +transactions, exact callback payloads, and framework render timing remain +outside this controlled owner. The later Ordered +acquisition table retains #2148's historical replay ownership. + +The core `load-subset-oracle.property.test.ts` now distinguishes each exact +demand-key option through one-axis pairs; its derived-sync expected-failure +guard and controlled-adapter scope remain explicit limits. + +The QueryRef owner checks DISTINCT support through source deletions, +last-support removal, reinsertion, and person enable/disable. It checks nested +and flat aggregate joins through source updates, and ordered joined +`findOne()` through initial, singleton, empty, restored, and changed-join-key +cuts. An unordered `findOne()` QueryRef used on either side of a join keeps +one default-key row through deletion and reinsertion. The optimizer aggregate +owner checks the initial public inner join of a global aggregate with +accepting, rejecting, and absent outer predicates; a materialized-Collection +formulation supplies matching and nonmatching controls. Other `singleResult` +forms, join forms, and scheduling cuts remain outside these owners. + +The offline scheduler owner (`packages/offline-transactions/tests/KeyScheduler-oracle.property.test.ts`) +checks FIFO eligibility and retry order at the scheduler boundary. It does not +observe the executor's awaited outbox update between `updateTransaction` and +`markFailed`, or public caller settlement. Those receiving cuts belong with +`packages/offline-transactions/tests/transaction-settlement-oracle.property.test.ts`. +The transaction serializer owner uses controlled Temporal constructors and fake +storage; a claim about native Temporal or browser storage needs a receiving +witness in those runtimes. +The offline settlement owner follows the README's success boundary: the +configured `mutationFn` has returned and the storage adapter has acknowledged +outbox deletion. Its controlled storage histories hold and fail deletion, +stop the executor and queued peers on a storage failure, reject new admission, +and restart after a persisted `deletion-pending` phase. A marked row skips provider replay +even when `beforeRetry` filters all ordinary pending work; an old unmarked row +still replays. Adapter tests prove that IndexedDB transaction failure and +localStorage `removeItem` failure reject rather than masquerade as deletion. +When a caller removes an active row with `removeFromOutbox` or `clearOutbox` +while its provider is held, the caller remains pending until that provider +returns. The controlled witness then checks provider calls, public settlement, +and the empty outbox. The leadership owner checks that an earlier +`deletion-pending` row with the same persisted timestamp is deleted before its +later peer reaches the provider. This restart witness uses storage insertion +order to distinguish creation order; independent storage writers and native +browser hosts remain outside its claim. +These witnesses do not establish physical power-loss durability, a real +provider's idempotency, or the crash window between provider completion and +the phase write. The provider must honor the supplied idempotency key in that +window. Older application versions that do not understand the phase can also +replay a marked row. Native browser restart and multi-owner handoff of the +marked phase remain receiving witnesses for this owner. + +The same owner also needs a captured failing seed-and-shrink-path replay for +its generated settlement property. The +[deletion-settlement review record](oracle-reviews/2026-09-30-offline-deletion-settlement.md) +preserves its earlier executable revision. The +[fail-stop follow-up](oracle-reviews/2026-09-30-offline-fail-stop-followup.md) +records the revised storage-failure law, RED/GREEN witnesses, and limits. +The guarded replay owner (`packages/db/tests/oracle-replay.test.ts`) now checks +that named pagination and index campaigns execute only the requested replay +lane. An unrelated failing fixed campaign no longer runs first. The +[prep-PR review record](oracle-reviews/2026-09-30-prep-pr-followup.md) records +these four review repairs at their exact executable revision. + +### Recent fix-wave authority inventory + +This inventory records the permanent authority for the September 17 fix wave. It +distinguishes an executable oracle from a specialized real-provider authority and +does not award oracle credit for a filename alone. + +| PR | Classification | Permanent authority and campaign | +| ------------------------------------------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [#1831](https://github.com/TanStack/db/pull/1831) | Fixed tests; no oracle owner (tracked in #2264) | PowerSync's `packages/powersync-db-collection/tests/correctness.test.ts` (fixed tests; no oracle owner (tracked in #2264)) crosses real PowerSync receipts and native SQLite behavior. It runs in the package test campaign; portable declarations retain compiler authority. | +| [#1832](https://github.com/TanStack/db/pull/1832) | Equivalent specialized authority | `packages/electric-db-collection/e2e/sql-predicate-semantics-oracle.e2e.test.ts` and `packages/electric-db-collection/e2e/subset-sql-acceptance.e2e.test.ts` run through the package's real-provider `test:e2e` campaign. Compiler unit tests are collateral, not substitutes for either service boundary. | +| [#1833](https://github.com/TanStack/db/pull/1833) | Explicit oracle | `packages/db/tests/query/pagination-oracle.property.test.ts` owns inherited collection collation, actual `item2`/`item10` order, exact request options, hostile lexical/numeric controls, and both scan and auto-index paths. It runs in `@tanstack/db`'s `test:oracles` campaign. | +| [#1834](https://github.com/TanStack/db/pull/1834) | Explicit oracle | `packages/db/tests/query/cold-join-reconciliation-oracle.test.ts` owns join/predicate equality equivalence across the established value domains, binary/string and nullish controls, replacement histories, raw lazy demand, and scan/auto-index paths. It runs in `@tanstack/db`'s `test:oracles` campaign. | +| [#1835](https://github.com/TanStack/db/pull/1835) | Explicit oracle | The existing `packages/db/tests/collection-state-retention-oracle.property.test.ts` and `packages/db/tests/optimistic-transaction-oracle.property.test.ts` owners cover separate collection-state and transaction-history laws. Both were already registered in `@tanstack/db`'s `test:oracles` campaign; focused storage/local-only tests remain collateral. | +| [#1837](https://github.com/TanStack/db/pull/1837) | Explicit oracle | The offline scheduler, leadership replay, and serializer owners cover selective replay retirement, durable per-ID settlement, stale-read fencing, lifecycle recovery, native scalar encoding, and prior wire compatibility. They run in the package test campaign. Every generated campaign in these owners uses the shared `TANSTACK_DB_ORACLE_{SEED,PATH,PROPERTY}` replay interface, with one property per campaign (`offline.fifo-retry`, `offline.leadership.*`, `offline.serializer.*`, `offline.settlement.*`). | +| [#1842](https://github.com/TanStack/db/pull/1842) | No shipped-law case | The PR changed only focused observer tests and introduced no production behavior. `packages/db/tests/live-query-observer.test.ts` remains the correct evidence; no synthetic oracle or campaign claim is added. | + +[PR #1816](https://github.com/TanStack/db/pull/1816) preserves existing witnesses, +repairs false-green assertions and drivers, adds missing histories, and includes +narrow runtime fixes reproduced by the stronger tests. The later ten-area audit +found 59 actionable findings or optional suggestions: 46 repaired, one partly +repaired storage item, nine contract decisions, and three deferred suggestions. +These are **not counts of production bugs**. The earlier 13-item review is a +separate ledger, not another 13 unique defects. + +## Find an owner + +Paths below are relative to the repository root. Follow each suite's domain +comment and the current API/architecture contract before extending its model. + +For paced mutations, `packages/db/tests/paced-mutations-oracle.test.ts` owns +core strategy timing, admission, cleanup, and settlement. It does not cross a +framework render boundary. The React receiving owner is +`packages/react-db/tests/usePacedMutations-render-cuts-oracle.test.tsx`. Its +independent model selects the committed callback owner when a transaction is +admitted. The bounded grammar calls before and after a candidate render, +releases a suspended render to commit, and calls from a descendant layout +effect with delayed or immediate persistence, with and without StrictMode. It +compares ordered callback traces and public Collection rows at the mutation +cut, the trace after persistence, and mutate identity at the commit cut. It +does not establish callback affinity across a commit between transaction +admission and persistence, server delivery, arbitrary React schedules, or +behavior in another framework. The +[PR 2170 review](oracle-reviews/pr-2170-react-paced-render-cuts.md) records the +render-time and layout-effect mutant kills. + +For standalone descriptor queries, the primary owner is +`packages/react-db/tests/descriptor-query-binding-oracle.test.tsx`. It checks +one prebuilt Query with a reusable factory descriptor under two DbProvider +clients. Disjoint-key inserts and same-key updates with different client values +are compared against plain per-client maps after initial publication, each +write, and a mounted A→B→A provider switch. It checks the public `$key` against +each projected row ID, exact public key sets, and identity equality with direct +Collection formulations. +The same owner crosses join, nested FROM, union, and include placements of two +standalone descriptors under two clients with equal row keys and different +values. Complete selected rows are compared with plain per-client values after +initial publication, a child write in the first client, and a parent write in +the second. A first-client-only binding mutation fails at the second client's +initial public-row assertion. This finite matrix does not observe on-demand +requests or arbitrary write interleavings. +The receiving include also crosses shadowed and renamed child aliases while +projecting a captured parent value. It compares both clients' public rows at +the same three cuts. An alias-only parent lookup fails the shadowed initial row +while the renamed control passes. Dropping binding IDs during descriptor +materialization fails the bound-versus-unbound public hash comparison in both +alias forms. More include depths remain outside this cell. +The receiving outer-join history crosses RIGHT/FULL, correlation on the main or +joined source, shadowed/renamed aliases, eager/on-demand sources, and two clients +with equal keys and different values. Plain per-client maps predict complete +public rows after initial acquisition and five source writes, including an +absent main side, an absent joined side, and return of the main side. It also +checks each client's source starts, on-demand requests, and descriptor-binding +hash. An absent-main join-route mutant fails the public-row check at initial +acquisition. Multi-join receiving histories and arbitrary write interleavings +remain outside this finite matrix; the core alias owner covers its own bounded +multi-join grammar. +The same owner also checks a prebuilt Query consumed by `useLiveQueryEffect`: +two clients with the same row key report their own initial enter events, and +one mounted Effect reports the new client's row after a provider switch. +After that switch, an old-provider write produces no update event and a +new-provider write produces one. The event-history check rejects an Effect +that retains the old subscription. +It checks that a descriptor Collection first acquired by a committed Effect +appears in `DbClient.dehydrate()` after the first enter event. A separate +same-hash transition from a concrete query to an unbound descriptor query +checks that the hook reports the missing client before reusing prior rows. +The same React owner checks that a descriptor render suspended before commit +starts no source work. Its finite 24-cell grammar crosses later reader intent, +startSync true/false and eager/on-demand sources. Ordinary subscription admission +also crosses no cleanup, loading callback cleanup, ready callback cleanup, and +held ready cleanup. Preload and deferring readers retain no-cleanup controls. +The shared independent judgment in +`collection-subscription-lifecycle-grammar-oracle.ts` predicts adapter entries +and the complete public status trace at admission return and cleanup settlement. +A second ordinary subscriber reuses a live run or restarts a retired source. +Public rows without cleanup distinguish on-demand source preload, which does +not acquire rows, from ordinary subscriptions. The core lifecycle owner adds +24 cases crossing the four cleanup histories, no/unrequested/requested start +gates and eager/on-demand modes. The erroneous release-before-request repair +fails four core and four React cells at admission return. The final expanded +React grammar also rejects the original no-release implementation. Historical +calibration of a previously-requested-start mutant predates the final call +reorder and is not evidence against that mutant on the current path. +This does not establish arbitrary concurrent-render schedules, failed-provider +histories, or Electric txid delivery. The [migration feedback review](oracle-reviews/2026-10-22-dbclient-migration-feedback.md) +records the SSR admission repair. The maintainer chose a programming-error +rejection when an ordinary server hook reads incidental rows or readiness that +its first browser render cannot reproduce. Cold/loading SSR remains legal. +`packages/db/tests/ssr-snapshot-admission-oracle.ts` owns the independent +preparation judgment and history grammar. The descriptor owner drives React's +ordinary and infinite hooks independently. The Svelte server driver is +`packages/svelte-db/tests/ssr-snapshot-admission-oracle.svelte.test.ts`. +Each compares rejection, projected rows and status before browser commit across +no preparation, completed or pending query preload, and normalized source +transport, including ready-empty and partial-loading sources. Render-time query +captures carry their own readiness; normalized source rows alone remain +provisional. React also runs real `hydrateRoot`, observes recoverable errors, and +checks the committed browser result. Svelte's actual server-to-browser driver is +`packages/svelte-db/tests/ssr-snapshot-handoff-oracle.svelte.test.ts`: it loads a +server-compiled fixture and calls browser-compiled native `hydrate` or `mount`. +Its first hook rows/status before effects and later authoritative browser view +are compared with the shared judgment. Histories cross zero through three +rows, same/different browser rows, zero through two earlier authoritative +readers, first/late hydration, fresh mount and a child created after provider +mount. Each independently hydrated root supplies `DbProvider hydrating`; +nested same-client providers inherit that host fact unless explicitly +overridden. An inherited marker must preserve the late reader's server view, +while a fresh mount or later child must read live rows. Always using the server +getter, keeping the marker after mount, and ignoring it each fail a receiving +comparison. Both React and Svelte cross writes after server render and before +browser hydration, including a completed same-query browser preload. The older +Svelte server-only owner proves server reconstruction only; its second render +is not browser evidence. `hydration.svelte.test.ts` separately receives +normalized transport and normal warm-browser initialization through its native +scheduler. + +The shared admission owner also defines runtime ready/error outcomes, browser +explicit preparation and defined/undefined causes. Its receiving drivers are +`packages/react-db/tests/ssr-runtime-error-handoff-oracle.test.tsx` and +`packages/svelte-db/tests/ssr-runtime-error-oracle.svelte.test.ts`. React drives +ordinary and infinite hooks independently through `renderToString`, JSON and +`hydrateRoot`; Svelte uses the server/browser compilation and native host above. +Each checks every public lifecycle flag at the actual first hook view and the +committed browser view: the transported initial error/ready view is reproduced, +then browser error/ready becomes authoritative. The React driver also checks +that an error snapshot cannot fulfill a later explicit data preload. Runtime +`fail()` remains a returned error result; the explicit failed preload must +reject. These empty-source histories do not establish arbitrary error-detail +serialization, error identity or post-error row retention. + +Real React streaming checks +cold and warm Suspense discovery, opaque keyed/unkeyed logic, and prepared +non-streaming Suspense. Under the shared React identity law, unkeyed opaque +queries reject with `UnhashableQueryIRError` during preparation, before stream +admission. Both cold and warm receiving cases check that class; keyed controls +still render and transport their rows. Infinite identity checks callback-returned Collections +and explicit keys with matching and unbounded windows across builder, direct +Collection and callback-returned Collection inputs. The latter callback path is +JavaScript runtime coverage; it has no TypeScript overload. The React +mixed-reader streaming owner crosses zero through three rows and microtask +source publications before preload settlement. Histories place an ordinary +hook before or after a Suspense sibling, or omit it, with source readiness +synchronous or held until the ordinary server read. Held histories distinguish +Suspense registration before the ordinary loading capture from ordinary-first +discovery. An empty-source failure outcome makes that later stream reject after +the ordinary loading view was rendered; rejected Suspense supplies a fallback, +not a successful hook view. +Real React hydration receives JSON-copied snapshots and streamed promises from +the real Router adapter under its controlled SSR callback. It compares each +hook's first browser view with its own server view, even when local browser rows +differ. `packages/db/tests/ssr-stream-view-oracle.ts` owns this shared metamorphic +comparison over independently recorded ordered row names and status; a missing +browser observation fails. Mandatory reach distinguishes both reader orders, +Suspense alone, same/different browser rows, synchronous/held readiness and held +source failure. It crosses async hydration with a synchronous +React act cut that permits ordinary commit before Suspense hydration, while +transported promises can still be pending. Both schedules must preserve each +reader's first projected rows and readiness, including an ordinary loading view +whose sibling stream later failed. Emitting an update without a newer transport +revision and prioritizing that later error over the ordinary initial snapshot +fail the respective first-view comparisons. The Router's focused receiving +test separately checks replacement delivery after an initial loading record. + +`examples/react/start-ssr-e2e/e2e/router-stream-view.spec.ts` receives the same +law through the real Router serializer and HTML transform +(`attachRouterServerSsrUtils`/`renderRouterToStream`), manifest-driven `Scripts`, +`RouterClient`/`hydrateRoot` and Chromium. Its mini HTTP host holds the first DB +record and every later byte until a browser hook attempts hydration, preserving +byte order after the async entry and completed shell. Thirteen finite cases +cross initial-shell/later-boundary discovery, ordinary reader order, zero/one +row, same/different browser rows, and delayed first-record arrival. A completed +same-query browser preload with a newer local timestamp cannot replace the +older server view owed to hydration. Missing-record normal close and transport +failure report the exact Error Boundary cause without a successful browser +substitution or new adapter work after termination. A known pending result +remains pending across DB record-stream close and explicit local Collection +preload until the held server provider is released. Original no-wait and +close-known-pending designs fail these host comparisons. Waiting forbids a +successful substituted view before the server record; it does not prohibit +valid Suspense prefetch. + +The core receiving driver +`packages/db/tests/ssr-stream-record-wait-oracle.test.ts` adds paired generated +histories for missing, pending and explicitly preloaded local records, arrival, +normal close, transport error or cleanup, unrelated peer records, first attempts +after termination, and defined/undefined causes. The shared stream-view owner +supplies its independent lifetime judgment. Promise settlement/identity and +public observer retry cuts check that only the requested server record settles +a wait, known pending results survive normal record-stream close, and terminal +missing records cannot become local success or start new adapter work. This +later dimension coverage follows the original host RED gate. Histories also +cross public client cleanup, rematerialization and a new stream for the same +hash. Old wait promises remain settled, a new stream obtains a fresh wait, and +enabling the same open stream preserves its current pending demand. The sticky +failure implementation and an unconditional reset each fail the corresponding +checkpoint. Pending-result +disposal during client cleanup and timestamp policy after record consumption +are excluded from this grammar. The +[SSR review audit](oracle-reviews/2026-10-23-ssr-review.md) records RED on unchanged +production, GREEN, narrow mutants, and exact receiving replays. +The consumer bundle owner `scripts/ssr-bundle-oracle.mjs` checks the maintainer's +2026-10-26 SSR isolation decision at the current DbClient import boundary. It +enumerates ordinary, Suspense, and infinite React hook exports, with and without +DbClient. Final production bundles must exclude the entire observer transport +module and its admission/capture operations without DbClient; positive controls +must retain them. `pnpm test:ssr-bundle` runs after built artifacts in the +minified-DB CI lane. The original shared observer fails the absence assertion; +`--calibrate` forces transport into a plain bundle and fails that same assertion. +The package aliases and pre-bundle artifact freshness checks share one list: +DB, DB IVM and React DB, including TypeScript and TSX source inputs. Stale React +and IVM controls passed the former standalone command, then fail its named +freshness guard after correction; current output still passes all six consumer +programs. The inputs are built ESM; esbuild minifies the final consumer bundle. +Freshness compares source/config timestamps with the package entry, not +content-addressed provenance or manually falsified timestamps. +This finite build oracle does not establish every bundler, framework entry, +byte budget, or runtime hydration schedule. A client-only application that uses +DbClient still ships its transport methods: isolating that case needs an +explicit SSR API boundary and remains outside this import-level guarantee. +The [isolation record](oracle-reviews/2026-10-26-ssr-bundle-isolation.md) records +the size comparison, runtime checks, and calibration. +The separate `live-query-observer-snapshot-oracle.property.test.ts` owner checks +unchanged getter identity with and without a completed DbClient query. It runs +ordered/single-result and attached/detached histories through ordinary and server +getters. It does not model provisional hydration replacement or host scheduling. +Focused snapshot checks protect lazy state, one admission walk for an unchanged +revision, explicit preload after a loading capture, JSON-compatible non-streaming +transport, and pending-stream ownership. The observer history +owner generates throwing releases followed by explicit retries, in both modes +and with thrown undefined. This last premise is a controlled synchronous channel +release; first-party event listeners report their failures asynchronously. +Its hydration error-timing grammar also crosses error before attachment versus +after subscribe returns, granular/wholesale mode, all three portable seed +statuses, and defined/undefined causes. The first pre-attachment hydration read +must retain its server view; an owned provider error after subscription must +publish error and notify synchronously. Always deferring error handoff fails +the latter cut; original local-error precedence fails the former. This +distinguishes initial hydration from ordinary owned runtime publication. + +Limits: admission/runtime sources are synchronous and eager with zero through +three rows; the mixed-reader driver additionally holds readiness/failure. +Infinite receiving coverage uses a one-row initial page and its peek-ahead row. +Arbitrary asynchronous providers, later page-shape changes during a pending +hydration stream, persistence readiness, arbitrary concurrent-root hydration +schedules and custom dehydration filters remain open for these receiving +owners. The Router/Chromium witness covers its finite serializer/HTML/script +path, not a deployed Start application, Electric delivery, every React renderer +or every host schedule. The React mixed-reader owner still needs a partial +nonempty loading snapshot followed by failure; its failed-source histories are +empty. The Svelte owners do not cover a pending streamed boundary, deferred +source throws, arbitrary asynchronous providers, error-detail identity or +post-error row retention. Manual multiple hydration epochs remain unspecified: +Core-SC04 asks whether an older record arriving after active-record consumption +may replace a newer retained server record. The shared stream-view and DbClient +owners need an explicit ordering contract before adding consumed/unconsumed +and older/equal/newer record histories. Neither local-versus-transported version +coverage nor normal Router byte order resolves that question. Transport +deliberately discarding required state and rendering local Collection object +identity are outside the portable snapshot law. +The Date descriptor typing report remains an evidence gap. The reliable strict +compiler probe accepts the reported Date forms. The receiving owners are +`packages/db/tests/db-client.test-d.ts` and +`packages/electric-db-collection/tests/electric.test-d.ts`; extending them needs +the failing application assignment and its resolved dependency versions. +Its no-client diagnostic also covers explicit `queryKey` and legacy dependency +identity for both `useLiveQuery` and `useLiveSuspenseQuery` at first consumption. +A local hook-first SSR stream over a concrete +source rejects a later same-hash descriptor preload over a different source; +the same source is accepted. A hydration-first stream has no server Collection +objects in its payload. The first committed browser hook claims its local source +objects for the hash before source readiness; a later conflicting preload +rejects, and the public row changes from the server result to the browser +result after readiness. Removing the hook's attach claim fails the conflict +assertion while readiness is held. A pending hydrated stream also has a +Suspense receiving cut: its uncommitted read preloads the first local source, +so that source may start and claim the hash before React commits. A later +same-source preload succeeds while a different same-hash source rejects. A +mutant that skips the Suspense preload claim fails at the conflict assertion. +This differs from an ordinary hook render with no explicit preload demand. +This owner does not claim that a failed Svelte effect can recover in place +after a reactive query replacement. +`packages/db/tests/db-client.test.ts` supplies a +focused nested-query preload witness across two clients, including equal query +hashes and distinct dehydrated rows. +`packages/db/tests/db-client-preload-identity.test.ts` (fixed tests; no oracle owner (tracked in #2264)) covers the bounded +same-hash preload law within one DbClient: the second local preload reuses a +result only when its source Collection objects are the same at each query +position. Concrete then +descriptor, descriptor then concrete, and two concrete same-ID sources must +reject a conflicting second request without changing the first dehydrated row; +a same-ID pair swapped between union branches also rejects. A descriptor and +its own materialized Collection may reuse it. For a hydrated hash, the first +local preload claims ordered source-object identity; a later conflicting +preload rejects for settled and pending streams. A newer hydration chunk keeps +that claim. If the earlier local stream instead fails, a newer settled or +pending hydrated result starts without its source claim. A healthy observer +using a different same-ID source then attaches and reads its own row. The old +preload entry cannot claim the new stream. Different explicit query keys, +concurrent first local claims, and other failure/hydration orders remain +outside this finite history. +Observer preload checks that an active settled graph or pooled stream, and a +pending hydrated graph stream, reject a different same-hash source through the +returned Promise without a synchronous throw. Same-source pooled preload is +the neighboring success case. A controlled reentrant registration collision +also checks that a discarded stream Promise's later rejection has an observer; +it does not claim that an official adapter currently makes this reentrant call. +An ordinary observer's first subscription also checks an active stream claim +before retaining its listener. With equal hashes, same-source attachment and +public row read succeed; a different-source attempt and its retry both reject +while the first stream remains active. A failed first attach leaving a retained +listener fails the retry assertion. After the first observer's live result +becomes authoritative, public dehydration omits the consumed client stream. +The next observer may then attach with the same or a different source object +under the same hash and reads its own row; the earlier preload entry cannot +extend the consumed stream's source claim. Other attach failures and concurrent +subscription orders remain outside this control. +An adapter observer over a pooled view crosses the same-source and different- +source cases for an existing stream hash. The former completes its own preload +and reads the first source; the latter rejects before borrowing the first +stream. Pooled views have no query Collection config, so this receiving path +needs a separate source-identity witness. +Historical baseline at `858b174a6` (superseded by the chunk entry below): +The local live-query cache option owner is +`packages/db/tests/live-query-cache-options-oracle.ts`, with core DbClient and +React receiving drivers in the adjacent `.test.ts` and +`packages/react-db/tests/cache-options-oracle.test.tsx`. The maintainer-approved +law rejects different getKey/schema references from independent same-identity +callers while retaining inline options within a mounted binding. The finite +core grammar crosses each/both options, same/different references, distinct-key +controls, derived/keyed identities, pending/settled result reuse and explicit +preload/observer preload/subscription admission (144 cases). React supplies 24 +real-host initial-render cases. The existing source-ID owner continues to judge +rerender retention. This option owner does not claim arbitrary SSR transport +schedules, every framework or semantic equivalence of distinct functions. +Hydration carries no function references; only local consumers claim them. + +The [RC oracle challenge](oracle-reviews/2026-10-28-pr-2171-oracle-challenge.md) +demonstrates two open gaps in this option owner at `858b174a6`: a guard that +rejects only when every configured option differs passes all 144 cases, as +does a guard that overwrites the original claim immediately before rejecting. +Temporary independent-option and repeated-admission comparisons reject those +mutants with 12 and 24 assertion failures respectively; unchanged production +passes both comparisons. The checked-in grammar still couples changed options +when both are present and compares admission without subsequent reuse. Its +pending-result cell does not hold provider delivery, and its first caller is +always explicit preload. The same owner needs independent option presence and +reference histories, post-rejection observations, and option-specific +hydration/consumption/failure histories. The report assigns host receiving, +release, type, and harness improvements separately. None of these proposals is +new checked-in coverage. + +The follow-up [review coverage matrix](oracle-reviews/2026-10-28-pr-2171-review-coverage-matrix.md) +records current failures at the same head. Cached `getServerSnapshot()` accepts +conflicting options before subscription; the shared admission comparison fails +three conflict histories in core and the same three through actual React +`renderToString`, with compatible controls passing. A newer hydration that +replaces a locally preloaded record loses its row-option claim; all three +conflict histories fail after the replacement is positively observed. These +are open product defects, not merely proposed coverage. This cache owner needs +the SSR receiving path (O16) and replacement-claim histories (O04), followed by +production repair and GREEN/calibration evidence. Portable transport must still +carry no function references. Temporary probes are not checked-in coverage. + +The [chunk implementation record](oracle-reviews/2026-10-28-pr-2171-oracle-chunks.md) +now records the reviewed option extension on unchanged production. The shared +owner crosses independent reference/presence pairs with explicit/default keys; +its core driver runs 1,440 histories over five admission paths and actual held +write delivery. It checks two admissions, original reuse, every next callback, +exact ordinary values/keys, and refused-listener silence. React receives 288 +histories through real mounted hooks and server rendering; all 144 client +histories also compare after React's cached-seed handoff. Independent key +functions make lost original behavior observable. Declared reach distinguishes +single-option/both conflicts, added/removed declarations and distinct keys per +path. Core has 192 admission assertion failures (64 settled SSR, 128 newer +hydration replacement), React has 64 SSR failures; neighboring histories and +checker/cleanup calibrations pass. These remain open product defects: the +maintainer explicitly defers production repair until all oracle chunks finish. +This historical chunk left first-registrant variation, transport serialization, +first local claim after hydration and failure/consumption retirement pending; +the following entries supply their later evidence. Other applicable frameworks +and schema transformations remain limited. This extension does not +establish hydrateRoot or arbitrary concurrent rendering schedules. + +The same chunk record now adds `live-query-cache-lifecycle-oracle.test.ts`: +2,160 legal timelines through preload, observer preload and server-first local +claims over actual JSON-round-tripped server data. Pending delivery, replacement +before/after transport, exact rejection on initial failure, consumed results and +fresh declaration reuse have phase/schedule-specific conflict reach. Equivalent +server/local behavior has independent function refs. On unchanged production, +640 admission assertions fail and 1,526 tests pass, including six controls. +Failures remain open until the deferred production repair. The shared immutable +bound-options checker now receives Preact's six native histories as well as +React's 12; six hostile in-place mutations are rejected. Svelte's actual +client-backed effect receives all 144 cached-option histories. All three native +selectors pass. Portable first claims/failure/consumption are no longer merely +proposed coverage. Remaining cache limits at that chunk were first registration +without an existing stream, arbitrary transport schedules, schema transforms +and native partial-construction cleanup. Svelte SSR/hydrateRoot/continuing publications are +not supplied by its browser admission receiver. + +Chunk 12 adds first registration without a transported query result. Core runs +4,320 histories: explicit preload, streaming observer preload and server read +as first claimant, each received through all five admission paths with held or +settled source delivery. Each of the 15 routes reaches 288 first registrations +and 144 held deliveries. The actual React streaming server driver in +`packages/react-db/tests/cache-first-registration-oracle.test.tsx` receives +1,152 histories through ordinary/Suspense first reads and preload/server second +callers. It supplies no hydration seed or Router result. It checks every render +error, repeated admission, compatible reuse, then core observer continuation. +Core has 640 intended admission failures and 3,750 passes; the new React driver +has 192 intended failures and 960 passes. Flat source rows are copied away from +expected facts in all five cache drivers. A corruption mutant now fails the +ordinary-row comparison, rather than changing both sides. Omitting observer +registration options adds 448 core and 128 React assertion failures. Held +conflicting preload admission is observed before source delivery at a host-task +cut; a pending promise fails promptly and cleanup releases the provider. +Independent reviews and exact evidence are in the chunk record. Production is +unchanged. Schema transformations, Router transport, hydrateRoot, arbitrary +concurrent schedules and other framework first-registration receivers remain +outside this bounded evidence. Native partial-construction evidence is recorded +separately in chunk 11. + +`packages/db/tests/live-query-deferred-acquisition-oracle.test.ts` also checks +a two-source descriptor union when the first sync start fails: the second source +still starts and reaches ready on direct preload, and a preparation error stays +primary if a deferred source also throws during release. The controlled sources +start synchronously. At the synchronous preload error cut, even a caller that +opts to stream pending queries cannot dehydrate the failed query; a later +same-hash preload over a healthy replacement publishes its own row. Asynchronous +failure is also exercised with a later controlled `markError`: the pending +stream rejects and disappears from dehydration, the peer source is ready, and +a healthy same-hash replacement publishes its row. A mutant that dehydrates +errored records fails at that cut. Other asynchronous schedules and larger +source sets remain open. Retrying the same options after the +source entered terminal error reports that source error instead of reusing the +failed query's pending stream; it does not restart the source. +A replacement observer subscribed after the controlled asynchronous failure +can attach under the same hash and reads the healthy replacement row as ready, +without a stale stream error, before a new client preload. The prior +source-mismatch check fails that attachment assertion. The same history rejects +a different-source preload while its original stream is still pending. A second +fixed history crosses a synchronous source throw and then attaches a healthy +replacement observer before a new client preload; its settled snapshot contains +only the replacement row. Removing the failed-record source-claim guard fails +that observer attachment, while ignoring every existing stream's claim fails +the pending-stream rejection. The preload-identity owner also checks active +streams. These histories do not enumerate concurrent replacement observers, +other failure schedules, or later conflicting preloads while an observer has no +reusable client stream. +An observer preload over a config-free pooled view is also explicit demand: it +starts an idle eager source once, does not restart a running source, and reaches +a ready public snapshot in both cases, with or without an SSR cleanup client. +This bounded witness does not claim the same path for on-demand or asynchronous +sources. +`packages/svelte-db/tests/descriptor-query-release.svelte.test.ts` (fixed tests; no oracle owner (tracked in #2264)) +checks Svelte's two-source release after a first startup error and a direct +reader after a second descriptor factory fails. It observes source start counts, +ready status, and failed-hook subscription release. A fresh effect root after +the failed root is retired consumes an independent healthy query and its row. +A controlled Svelte runtime probe showed that an uncaught effect error does not +rerun the same effect when its reactive query changes; in-place recovery after +that error is not promised. This owner does not establish two-client Svelte +provider binding or ordinary reactive query replacement. Concrete- +config descriptors, on-demand acquisition, and those Svelte paths remain open +for this binding law; a +receiving oracle needs the relevant public observation for each. Vue, Solid, +and Angular have no DbClient descriptor-binding contract in this work. Their +query callers use concrete Collections. +`packages/db/tests/live-query-options.test.ts` checks that a client-aware +consumer binds nested, union-branch, and include-child descriptors during final +preparation. Construction retains descriptor refs in those positions, including +direct, nested, materialized, and conditional includes. A prepared builder is a +bound snapshot; a later descriptor join remains unbound until another +client-aware consumption. The focused core tests check source materialization +and sync starts before and after release of a render deferral set, plus the +missing-client error for direct consumption. The React descriptor oracle checks +public rows after a committed hook consumes such a continuation and zero source +starts when a render containing one never commits. These bounded checks do not +compare union or include rows under separate clients, or concurrent render +schedules. The original standalone Query remains the reusable two-client plan; +rebinding a previously prepared snapshot to a different client is not claimed. +`packages/db/tests/query/collection-ref-compatibility.test.ts` pins the +exported `IR.CollectionRef` shape for concrete Collections: `collection` is an +enumerable, writable own field that a spread copies. It also checks that an +unbound descriptor uses `IR.DescriptorRef` with no `collection` field. A bounded +source-position matrix checks collection, nested query, union, join, and include +IR traversal and conversion to concrete refs during placement cloning. This +focused shape test does not establish public rows for those query forms or +generic visitor behavior outside the named collectors. + +| Surface | Primary executable owners | Independent judgment and important limit | +| ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Ordered relations and BTree | [top-K relation oracle](https://github.com/TanStack/db/blob/main/packages/db-ivm/tests/operators/topk-relation-oracle.test.ts), [fractional-index window moves](https://github.com/TanStack/db/blob/main/packages/db-ivm/tests/operators/topKWithFractionalIndex-oracle.test.ts), [BTree/Map](https://github.com/TanStack/db/blob/main/packages/db/tests/btree-map-oracle.test.ts), [incrementalization laws](https://github.com/TanStack/db/blob/main/packages/db-ivm/tests/incrementalization-law-oracle.property.test.ts) | Independent ordered relations and cumulative signed output. Each generated incrementalization campaign sums the checker's delivery counts and requires one atomic delivery per checkpoint and more split deliveries than an unsplit run makes; a checker that stops splitting fails all six campaigns. Most ordered steps edit the previous world by one removal or insertion, and a reconstruction check counts promotions of retained rows over the executed fixed campaign (25 global and 21 per parity at 100 runs; independent worlds gave 2 and 2). A type test pins `join(other, type)`'s nullable pair sides for every join type. Top-K consolidation compares same-key values without hashing, including cyclic replacements and fresh transient cancellation. The fractional-index test checks an empty output message for a window move after all rows retract; it does not prove downstream lazy acquisition. Other hash-based operators retain hashing's declared domain. Algebra does not specify client readiness. The BTree/Map oracle checks point operations, neighbor pairs, and full, partial, boundary, exclusive-high, and empty range scans for integer keys and node sizes 4–8. It does not check balance or space, and it assumes consistent comparators ([code-weight BTree review](oracle-reviews/code-weight-btree-trim.md)). The generated groupBy law compares JSON values; a pinned contract test in `packages/db-ivm/tests/operators/groupBy.test.ts` records that after a delete, `min` and `max` return a value hash-equal to a remaining member, not necessarily the remaining instance (`-0` for `0`, an equal Date), which is why the query compiler keeps its own exact-value keys. It checks both insertion orders, reads the group's row by integrating output multiplicities, deletes the minimum and the maximum, and shows that an aggregate input that tells the rows apart returns the remaining instance, for -0/0 in both directions and for both `min` and `max`. The contract holds while every member has positive multiplicity. It pins the current behavior separately: the delete's retraction is the last record a shared hash entry gets, so the deleted instance comes back in either insertion order, for -0/0 in either direction and for an equal Date. For -0/0 and equal Dates the contract assertions only show hash equality, which is all the contract promises. Open: the incrementalization law's value policy compares JSON and clones by `structuredClone`, so no generated history sees -0 or Date identity; a value-identity policy with -0/0 and equal-time Dates would generalize this pinned contract. | +| Includes and publication | [cross-formulation](https://github.com/TanStack/db/blob/main/packages/db/tests/query/includes-cross-formulation-oracle.property.test.ts), [temporal](https://github.com/TanStack/db/blob/main/packages/db/tests/query/includes-temporal-oracle.test.ts), [Collection includes](https://github.com/TanStack/db/blob/main/packages/db/tests/query/includes-collection-oracle.property.test.ts), [architecture and complete suite map](https://github.com/TanStack/db/blob/main/packages/db/src/query/live/ARCHITECTURE.md#executable-contracts) | Per-parent/flat-join/partition relations, callback-time rows, nested values, and route histories. Fragmented lazy-demand consolidation covers delta growth and successful, rejected/retried, and obsolete replacements at the Collection request boundary; it observes exact key unions and abort checkpoints. A compiled-includes fixture makes adapter unload evict owned child rows and proves a rejected union cannot remove established public child rows. Retry after that terminal live-query failure remains covered at the acquisition boundary; no automatic in-place recovery is claimed. Observe raw promised order; fresh queries do not establish continuous publication safety. The temporal owner's source-readiness lane enumerates every legal order of delivery, settlement, source readiness, and one later change for a source whose Collection readiness lags its settled subsets. It checks live-query readiness, preload settlement, and visible rows for direct, single-result, ordered-window, include-child, lazy-join, and eager-root sources; `queryOnce` results for the direct and eager-root shapes; `toArrayWhenReady()` and `stateWhenReady()` results for a read started after rows are visible but before readiness, over the direct, single-result, and eager-root shapes (and the lazy join for `toArrayWhenReady()`); and Effect `skipInitial` deltas for the direct, lazy-join, and eager-root shapes, and every on-demand cell of every consumer is crossed with a truncate or (join and include) a parent rekey before settlement. Each on-demand cell also compares its consumer with a metamorphic twin over a source that is ready from its start, which does not rely on the model's eager/on-demand rule. A live-query observer test checks that a hydration seed hands off at subset settlement, and an Electric test pins that a ready progressive query's rows update only at the full sync's swap. Effects over an ordered window or an include have no lane. It rejects both a design that waits for every source's Collection readiness and one that ignores source readiness. Electric live-query tests are the receiving witnesses for a real progressive source and an on-demand source that is ready before its subset loads; a React Suspense test checks that Suspense and `isReady` release together. Other framework adapters have no Suspense witness. | +| Collection lifecycle | [mutation startup](https://github.com/TanStack/db/blob/main/packages/db/tests/collection-mutation-startup.test.ts) (fixed tests; no oracle owner (tracked in #2264)), [change-event history](https://github.com/TanStack/db/blob/main/packages/db/tests/change-event-history-oracle.test.ts), [sync publication reentrancy](https://github.com/TanStack/db/blob/main/packages/db/tests/collection-sync-reentrancy-oracle.test.ts), [history](https://github.com/TanStack/db/blob/main/packages/db/tests/collection-subscription-lifecycle-history-oracle.property.test.ts), [publication](https://github.com/TanStack/db/blob/main/packages/db/tests/collection-subscription-lifecycle-publication-oracle.property.test.ts), [replay](https://github.com/TanStack/db/blob/main/packages/db/tests/collection-subscription-replay-oracle.property.test.ts), [effect disposal](https://github.com/TanStack/db/blob/main/packages/db/tests/effect-disposal-oracle.test.ts), [GC and persisting transactions](https://github.com/TanStack/db/blob/main/packages/db/tests/collection-gc-transaction-oracle.property.test.ts), [GC retention boundaries](https://github.com/TanStack/db/blob/main/packages/db/tests/collection-gc-retention-oracle.property.test.ts), [shared GC model](https://github.com/TanStack/db/blob/main/packages/db/tests/collection-gc-retention-oracle.ts), [GC mutation projection](https://github.com/TanStack/db/blob/main/packages/db/tests/collection-gc-mutation-oracle.property.test.ts), [native idle receiver](https://github.com/TanStack/db/tree/main/packages/db/e2e/collection-gc), [initial GC review evidence](oracle-reviews/pr-2258-gc-retention.md), [second GC review and decisions](oracle-reviews/pr-2258-gc-retention-round2.md), [interaction and mutation receiving evidence](oracle-reviews/pr-2258-gc-retention-round3.md), [GC checker calibration](https://github.com/TanStack/db/blob/main/packages/db/tests/collection-gc-retention-oracle.test.ts), [Electric confirmation receiver](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/e2e/collection-gc.e2e.test.ts), [PR #1902 review](oracle-reviews/pr-1902-change-event-history.md), [batch-order decision review](oracle-reviews/change-event-batch-order.md), [absent-key insert review](oracle-reviews/2026-10-02-insert-for-absent-key.md) | Core Collection `insert`/`update`/`delete` admission while `startSync:false` is idle; complete public-row/change-message agreement across bounded one- and two-key histories plus replayable four-key campaigns, where every message must be valid for a strict consumer (insert only for an absent key, update or delete only for a present one; [absent-key insert review](oracle-reviews/2026-10-02-insert-for-absent-key.md)); a two-key deferred sync history checks each key's causal insert/update/delete trace while allowing any cross-key interleaving. Reversing all deferred messages fails that check; reversing each sync transaction's distinct-key messages passes. Queued duplicate admission and cancellation, ownership and phase histories, exact caller/error/publication evidence, and late completion and restart have separate witnesses. Generated deferred histories with cancellation or reentrant callbacks need a witness in the change-event history owner before claiming general batch-shape coverage. Every bounded change-event history, and about half of each generated campaign, runs again on a local-only Collection that ends with `reload()`: it must resolve with the Collection `ready`, the change-message mirror of valid messages must equal the public rows, the public rows must equal the history's kept rows (maintainer decision: a local-only reload keeps the user's writes, and `initialData` loads only into a Collection that starts empty), and a later write must reach both. The adapter that re-inserted `initialData` in each run failed 210 histories (a crash when a kept row changed, or a mix of kept rows and `initialData`); a reset to `initialData` fails 344, and loading `initialData` whenever the Collection is empty fails 45 (a history that deleted every row). Query write utilities and effect self-dependent disposal remain separate contracts. During a truncate, a ready callback can write to any key after a prior optimistic request. Each subscriber, with or without initial state or a filter, must receive valid messages after the Collection is ready and end at the Collection's rows. A ready callback that throws while entering sync surfaces when the sync function returns, whether it calls markReady or commits a truncate ([round 3 follow-ups](oracle-reviews/2026-10-05-round-3-followups.md)). Callback rollbacks, non-optimistic callback writes, and several writing callbacks remain open ([ready-callback truncate review](oracle-reviews/2026-10-05-truncate-ready-callback-delete.md)). The GC property (`collection-lifecycle.gc-persisting-transaction`) checks on a fake clock that automatic garbage collection retires a Collection only after `gcTime` with no subscriber and no persisting transaction, that the delay restarts when the last transaction completes or fails, and that explicit `cleanup()` still retires it while a transaction persists. Histories run over two Collections, with inserts through one Collection's `onInsert` or through a manually committed transaction that touches both. After each subscribe, unsubscribe, insert, settlement, and clock step it compares each Collection's `cleaned-up` status and each persisting row's visibility with an independent deadline model. `main` before the fix, a hold that never restarts the delay, a restart only on completion, and a restart only for the first Collection a transaction touched each fail it. The companion `collection-lifecycle.gc-retention-boundaries` property separates elapsed eligibility from host idle delivery. Its shared reference model checks the absolute 50ms startup/preload handoff, real last unsubscribe, pending versus persisting transactions, delayed commit, success/failure/rollback, surviving siblings, disabled GC, and old idle work after renewal. Generated configured delays are 1, 10, 49, 50, 51, and 100ms; observations include immediately before and at the independent deadline. Eager sync and write-started sync both run. A Chromium receiving driver uses the same model/checker with native idle delivery and cancellation and controlled elapsed timers. It records the actual budget core reads and checks public status at core delivery and a usable native observation opportunity. Exact wall-clock cleanup latency is not promised. Paced mutations, offline transaction restoration, live-query Collections, and actual Electric `awaitTxId` plus provider delivery are not generated by these GC owners. The boundary grammar now generates repeated warm preloads over queued idle and persisting holds, successful and failed cold preloads as first or last releases, both sibling settlement orders, and pending drafts held by subscriptions. Held adapter cleanup crosses settlement with surviving pending or persisting siblings, both before and after cleanup settlement. Exact key comparisons reject leftover settled rows and cleanup resurrection; tracked transaction identities still match unsettled transactions. The 250-history fixed and random campaigns require all 33 declared states. Alongside the 15 focused lanes, constructive interaction histories combine up to seven transactions and three subscribers, delayed commit, three persisting siblings, non-FIFO settlement and preload between releases. Automatic GC also starts held or rejecting adapter cleanup, checks synchronous row discard before adapter settlement, and rejects pending-sibling resurrection. The shared model additionally receives direct updates/deletes, shared update/delete and cancelled insert/delete ownership with a surviving peer update through `collection-lifecycle.gc-mutation-shapes`. Its independent source/intent projection checks exact keys and values across completion/failure/rollback, both Collection orientations and subscriber states; each 150-history campaign requires all 11 declared states. The maintainer chose first-usable-idle retirement as a fixture scheduling assumption, not a public liveness guarantee. The maintainer also chose caller-owned pending draft visibility: keep a subscription; restart after GC does not promise draft restoration. The boundary grammar therefore does not establish post-GC draft restoration, paced/offline restoration, or arbitrary cleanup/source-recovery histories. A real Electric/Postgres receiving driver now crosses gcTime 10/100ms with absent/last-unsubscribed owners, holds an assigned uncommitted txid across the unheld deadline, and observes pending caller, persisting phase, optimistic row, genuine confirmation, successful settlement, synced row, renewed GC and idle delivery. Its 12 paths retain four one-write cases and add eight two-write cases with separate real SQL transactions and both local settlement orders. A genuine acknowledgement releases only one hold; the other SQL transaction remains open beyond the first-release deadline. It shares the model/checker and rejects no-hold and first-release runtime mutants. Native host and real provider are witnessed separately; their combined browser/provider schedule, three-or-more provider writers, provider failure/recovery and persistence wrappers remain outside these bounded receiving histories. The Collection lifecycle GC owners still use fresh disjoint optimistic keys. Before claiming the combined same-key rollback-cascade or confirming-sync-queue law with GC, this owner needs a generated receiving history that crosses those semantic owners at retention and cleanup checkpoints. The history owner's demand-change grammar (`demandChangeLifecycleHistoryArbitrary`, a fixed seed and the random campaign `subscription-lifecycle.demand-change`) adds a third demand, a `replace` command (`replaceSnapshots`: a held replacement whose own load did not fail covers the owners it replaces, law 10), and a `change` command that retires several owners through one `releaseSnapshot(...wheres)` call; the model judges an open replay once after the whole change (law 9). Declared reach: a change during a replay, a replacement covering a failed owner, and a change whose verdict between releases would differ. Per-release judging, first-release-alone, and batching only two-owner changes each fail it. A covered owner's failed replay load reports `loadSubset:error` whatever its settlement order (the model's existing reporting rule; suppressing the last settler fails it), and a prompt cancellation of a covered owner settles the replay as any settlement does. An aborted held replacement covers nothing: the abort ended its load before it applied (maintainer decision: an abort ends the load; law 10), so its replaced owner's failure keeps the replay failed (two 30-times counterexamples, now `keeps a replay failed when a replacement aborted after its replaced owner failed` and its peer-loaded twin; RED on the first simplification, which counted the aborted replacement as covering; a mutant that drops the check fails both). The controller always aborts and releases a segment together, so it never makes that case. An abort ends the load (maintainer decision): the history model's `abort`, and a truncate that replaces an attempt, stop that attempt's gating at once in both `manual` and `reject` cancellation; an owner whose attempt the abort ended before it settled owes the replay nothing, and a rejection after the abort decides nothing (a failure settled before the abort stays). The publication model finishes a replacement at an abort, and the replay oracle's newer attempt drops the older attempts' pending loads. A synchronous throw from `loadSubset` is a rejection (maintainer decision): an acquisition that core releases, also in the acquisition-cell and replay witnesses. RED on 2bc79fc7c: 18 lifecycle tests, 1 refinement test. Mutants that fail: a replay that waits for an aborted load (14), an aborted rejection reported as `loadSubset:error` (19), an aborted load that still holds readiness (17). | +| Paced mutations | [virtual-clock oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/paced-mutations-oracle.test.ts) | `createPacedMutations` with queue front/back options, bounded waiting capacity at zero and one, cleanup draining in FIFO/LIFO order after a held write, cleanup timing at the configured wait for immediate writes, admitted writes after separate Collection cleanup, debounce and throttle schedules with explicit and omitted options, and first-leading throttle execution at epoch zero. Finite histories compare optimistic rows, returned transaction identity and receipt outcome, execution order and virtual-clock time. Held writes verify queue serialization. Leading-only throttle and debounce witnesses reject skipped calls immediately with their named dropped-call errors, including omitted edges, both edges disabled, and same-row rollback while a prior write is held. Capacity witnesses cover overflow rejection, distinct-key optimistic rollback, same-key admitted-write survival, and in-flight versus waiting admission. Cleanup witnesses check repeated queue cleanup, eventual admitted receipt settlement, direct queue strategy rejection of new callbacks, public post-cleanup rollback with `QueueDisposedError`, pending debounce transactions that wait until the last call's quiet edge after separate Collection cleanup, and a trailing throttle timer that runs at its regular edge after separate Collection cleanup. Deferred custom queue and batch callbacks check compatibility with `void` and `false` execute results, respectively. Frozen options verify factory non-mutation; a mutable option witness checks that debounce uses its construction-time trailing setting. Throwing `onMutate` histories check leading-edge reuse before any row change, failed-row restoration when a pending transaction already has admitted work, eligible leading writes behind a held handler, and throttle pending merges at the throw, start, receipt, and later-timer cuts. New debounce/throttle admission after cleanup, manual commit of a strategy-owned transaction, longer throwing/reentrant bursts, failed persistence, broader capacities and schedules remain outside this owner. | +| React paced-mutation render cuts | [render-cut oracle](https://github.com/TanStack/db/blob/main/packages/react-db/tests/usePacedMutations-render-cuts-oracle.test.tsx), [PR #2170 review](oracle-reviews/pr-2170-react-paced-render-cuts.md) | `usePacedMutations` publishes callbacks only from committed renders and does so before descendant layout effects, while callback-only commits preserve mutate identity. An independent committed-document model crosses ordinary and StrictMode schedules with mutation before or after a suspended candidate, mutation after the candidate commits, and delayed or immediate mutation from a descendant layout effect. It compares ordered callback traces and public rows at the mutation cut, the trace after persistence, and mutate identity at the commit cut. Render-time publication, layout-effect-only publication, and split insertion/layout publication mutants fail at the intended comparisons. A commit between transaction admission and persistence, arbitrary React schedules, server delivery, and other frameworks remain outside this receiving owner. | +| Optimistic state | [same-key composition](https://github.com/TanStack/db/blob/main/packages/db/tests/optimistic-composition-oracle.test.ts), [history model](https://github.com/TanStack/db/blob/main/packages/db/tests/optimistic-history-oracle.ts), [generated histories](https://github.com/TanStack/db/blob/main/packages/db/tests/optimistic-transaction-oracle.property.test.ts), [outcomes](https://github.com/TanStack/db/blob/main/packages/db/tests/optimistic-history-outcomes-oracle.test.ts), [publication](https://github.com/TanStack/db/blob/main/packages/db/tests/optimistic-history-publication-oracle.test.ts) | Optimistic state drops at settlement and held sync transactions publish with it ([settlement-drop review](oracle-reviews/2026-10-03-settlement-drop.md)); whole-row active overlays, metadata and prior-value events. A raw subscriber without initial state checks that each truncate batch inserts a key once ([2026-10-02 review](oracle-reviews/2026-10-02-truncate-raw-subscriber.md)). Never rebase a pending snapshot merely to simplify the model. Generated histories also write source batches inside mutation handlers, before they return; the Collection must own the request by then ([handler source batch review](oracle-reviews/2026-10-01-handler-source-batch.md)). The same-key transaction lane also authors a delete followed by a reinsert, which must be visible optimistically and net to the truth-table request ([state mutation round 2](oracle-reviews/2026-10-02-state-mutation-round-2.md)). Source rows are written as `insert` for a key the source lacks, and batches may delete keys the source holds ([state-stack mutation gaps](oracle-reviews/2026-10-02-state-stack-mutant-gaps.md)). Batches may also delete a key the source never held. That delete leaves an active request in place ([absent-key source delete review](oracle-reviews/2026-10-02-absent-key-source-delete.md)). A separate partial campaign keeps the default partial row update mode, where source updates may omit a field and merge into the source row, not into an optimistic row ([round 3 review](oracle-reviews/2026-10-05-state-mutation-round-3.md)). An aborted open batch restores the driver's own source rows as well as its keys, so a later partial update still omits the field ([aborted-batch review](oracle-reviews/2026-10-08-partial-update-aborted-batch.md)). A failed synchronous manual `mutate` callback is now checked after same-key update, distinct insert, and same-key delete, with and without earlier intent, plus two two-Collection cases, one with a restoration listener failure. The throw cut compares public rows and pending payload; the later commit checks only surviving intent. A restoration listener error retains the callback error as the aggregate cause and does not stop other Collection restoration. These witnesses allow transient notifications during the callback and do not cover nested throwing callbacks or async work after the synchronous scope. After every step the driver also checks, by transaction identity, that the Collection tracks exactly its unsettled transactions, so a failed or rolled-back transaction leaves the map ([failed transaction retention review](oracle-reviews/failed-transaction-retention.md)). The [transaction ownership oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/transaction-ownership-oracle.property.test.ts) owns release across two Collections: merged-away pairs, conflicting rollbacks with a throwing subscriber, asynchronous success and failure settlements with a throwing subscriber, rollbacks from a `truncate` listener during a sync commit, a reused transaction id, and a `mutate()` callback that throws, checked in the same call stack and after promises flush. It also checks the settlement error shape and that a settled transaction's set of tracking Collections is empty; its mutations still name their Collection, so neither owner claims the Collection is collectable. Focused [restoration ownership](https://github.com/TanStack/db/blob/main/packages/offline-transactions/tests/restoration-ownership.test.ts) witnesses check that repeated offline `mutate()` calls add to one transaction, that a completed restoration settles `when('settled')`, and that a restoration one Collection cannot track leaves no Collection tracking it. Its grammar also has direct writes (`insert`, `update` and `delete` outside a transaction), optionally with a throwing subscriber that may have been sent the rows first; it captures each direct write's own transaction and requires it to fail when the admission throws and to complete otherwise, counts handler calls (none for a rejected write), and keeps a mirror subscriber on each Collection that must match the rows after every step. A write whose admission throws rolls back only its own transaction and rethrows. The [change-event history oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/change-event-history-oracle.test.ts) generates local-only direct writes that meet a throwing subscriber, registered after the mirror, and requires the call to rethrow with its transaction completed and the rows following the model. Focused witnesses cover a local-only subscriber that writes the key it was told about, and a subscriber that throws a value that is not an Error; whether a throwing subscriber may stop a later subscriber is not decided. Open (approved limits): a direct write made inside another write's change subscriber reports its subscriber failure to the outer write; on a local-only Collection a direct write whose subscriber throws completes on the fast path but rolls back on the transaction path, and the change-event oracle generates only the fast path; a direct write admitted from inside a sync-commit listener, where the rollback's recompute is deferred, is not generated; the generated ownership histories read every receipt themselves, so only a focused witness (rolling back an empty manual transaction) checks that an unread receipt reaches no `unhandledrejection`; two Collection instances with one id, a Collection cleaned up before its transaction settles, and offline restoration have focused witnesses only; no generated history combines a held completed row with a second Collection. | +| Drafts and native values | [proxy](https://github.com/TanStack/db/blob/main/packages/db/tests/proxy-oracle.test.ts), [detachment](https://github.com/TanStack/db/blob/main/packages/db/tests/proxy-detachment-contract-oracle.test.ts), [iteration](https://github.com/TanStack/db/blob/main/packages/db/tests/proxy-iteration-contract-oracle.test.ts), [revert](https://github.com/TanStack/db/blob/main/packages/db/tests/proxy-revert-oracle.property.test.ts), [native methods](https://github.com/TanStack/db/blob/main/packages/db/tests/proxy-native-methods-oracle.property.test.ts) | Native-operation controls, exact patches and actual stored rows; alias/cycle/adversarial-key histories. Set and Map reorder histories check draft patches and stored iteration order after replacement and clear-and-readd; RegExp replacement/reversion histories check `lastIndex`. General native-mutator and symbol-write support is not established by a plain-object oracle. The revert owner generates assignment, revert, delete, nested, symbol-key, and `for...of` histories and checks `getChanges()` and the draft against an independent draft-equality model: ordered Map and Set contents (including Sets inside Map values), RegExp `lastIndex`, array holes, and symbol keys inside nested values. Map and Set mutators and top-level symbol writes are outside its grammar; array mutators appear only in the mixed block below. Its round-trip grammar also replaces a field with a new object and restores the original value through a nested write. The law requires every changed key, and permits a written key whose final value equals its original to be reported with that value: a false positive repeats the stored value, and a false negative loses a write ([review](oracle-reviews/proxy-revert-replaced-object.md)). A further revert block mixes array `push`, `pop`, and `reverse` with assignments, a nested object replacement, and a retained handle, and checks that a mutator through a replaced handle changes nothing; pinned witnesses require a native write through a Map or Set value, a typed-array view, or a frozen draft to be reported, and a nested revert inside a Map or Set to keep the container's other write; Map and Set mutators are not mixed with assignments, and an equal-valued assignment keeps a retained handle attached (declared). The proxy owner's stored-function law checks, against a native row, that a function stored as data reads back as stored by every read path and that a stored method sees the draft as `this`, so its writes are tracked. The detachment owner pins which key classes a draft copies: enumerable string keys and every symbol key. The revert owner also generates typed arrays (including a subclass that does not forward its constructor argument), URLs, and a class with only private state as written values, with a property that writes two of them over one field. The native-methods owner generates rows (holes, `undefined`, duplicates, nested arrays, empty rows, typed `-0` and `NaN`) and calls every method of `Array.prototype` and `TypedArray.prototype`, in fixed and random campaigns with replay; it compares the result, whether the method returned the array itself, the identity of each original object, and the published row with native values, and a new built-in method fails until it has arguments. The proxy owner's `defineProperty`, stored-function, and frozen-draft laws are bounded tables against native rows. Its `defineProperty` law compares result, value, descriptor, and changes, for values and accessors. The iteration owner checks that array iterators see a push, removal, index write, or length cut made while they are open. Open: a draft reads a class instance of the original row as a plain object, so private-field getters read `undefined`, and a built-in method called through `this` on a nested Map or Set draft rejects the Proxy receiver; a mutator that changes nothing publishes an equal value; writing back a keyless class instance of the original row is a change, and `DataView` setters are untracked ([draft proxy review](oracle-reviews/code-weight-draft-proxy.md)). | +| Query DB and observer | [ownership](https://github.com/TanStack/db/blob/main/packages/query-db-collection/tests/ownership-lifecycle.oracle.test.ts), [persisted direct-write window](https://github.com/TanStack/db/blob/main/packages/query-db-collection/tests/persisted-direct-write-window.oracle.test.ts) ([review](oracle-reviews/issue-2029-direct-write-lock-window.md)), [load lifecycle](https://github.com/TanStack/db/blob/main/packages/query-db-collection/tests/load-subset-lifecycle.test.ts) (fixed tests; no oracle owner (tracked in #2264)), [observer histories](https://github.com/TanStack/db/blob/main/packages/db/tests/live-query-observer-history-oracle.property.test.ts), [retained snapshots](https://github.com/TanStack/db/blob/main/packages/db/tests/live-query-observer-snapshot-oracle.property.test.ts) ([review](oracle-reviews/pr-2043-retained-observer-snapshots.md)) | Real QueryClient boundary, applied-settlement barriers for existing and cached demand, mutation-handler Collection identity, terminal fetch-record lifecycle, and a per-listener eligibility ledger, not a duplicate dispatch queue. The bounded handler grammar crosses insert, update, and delete through handler parameter and mutation-alias access. All calls explicitly refetch; former `refetch`/`clearError` coordinates remain only as historical replay labels, preserving all 12 identity and 12 mutation-overlap executions. The ordinary [health scenarios](https://github.com/TanStack/db/blob/main/packages/query-db-collection/tests/ownership-lifecycle.test.ts) observe exact public causes and settlement. A held refetch retains the current public error. A failed initial load, including a failed initial Collection application, remains a loading error for that sync run regardless of later fetch results. After readiness, a pending retry retains the prior failure reason; failure reports the later cause, and successful result application clears it. Query retry attempts and an older held success are crossed with newer failures, including reuse of one Error value in one clock tick. These scenarios supplement the oracle; they do not claim removed utility fields or error counts. Native SQLite persistence, concurrent retries across multiple tracked Queries, mutation-handler fetch-boundary settlement, and deferred result application remain outside this witness. The mutation overlap grammar crosses both start orders. Check reentry, peer survival, FIFO and disposal independently of final rows. A retained observer snapshot shows its capture-time rows on a later first read of `data` or `state`. Direct-write witnesses now distinguish an idle pending eager cache seed (success with no new freshness timestamp) from an in-flight pending request, and require update, delete, and upsert to reach both eager Collections through their shared physical Query, in both acquisition orders. They check both public Collections and the Query cache immediately after each write. A held older shared fetch also crosses B's release and reacquisition, then compares both public rows at refetch and load settlement; mixed select wrappers and three or more co-owners remain outside this witness. The load-lifecycle tests also restore an intermediate checkpoint that keeps an explicit subset refetch pending until a post-write request applies, and rejects a no-cancel mutant at the replacement-request assertion. . Two sync-metadata session witnesses fail on `30175be48`: a claim that inserts a row keeps applied non-owner metadata, and a claim during a held retention cleanup publishes the cleaned owner (see Sync metadata session) | +| Observer hydration under terminal statuses | `packages/db/tests/live-query-observer-hydration-oracle.property.test.ts` | ARCHITECTURE.md law 16 and the glossary "crash": while a hydration seed is active, a Collection `error` or `crashed` shows in the snapshot, attached or detached, and is announced; an attached observer that sees the failure ends the seed, so cleanup then shows `cleaned-up` and live rows, not the stale failure or the seed. Decision (not a law-16 claim): `cleaned-up` and restart under an active seed keep the seed as `ready`, because cleanup is not a failure and the next sync run's readiness hands off. Generated histories cross both modes, attach order, live writes, `markReady()`, `fail()`, `crash()`, cleanup, and restart, with declared reach; replays pin the crash, fail-then-cleanup, and crash-before-attach traces. Limits: rows under a failure status are not claimed; the source is a plain controlled Collection, not a live-query graph or an on-demand subset; streamed client errors, handoff timing, and framework adapters stay with the focused observer and adapter tests; the property is not in the `oracle-config.ts` path-replay registry. | +| Query DB SSR dehydration | `packages/query-db-collection/tests/ssr-dehydration-oracle.test.ts`, [review record](oracle-reviews/issue-1950-ssr-dehydration.md) | A plain Query cache control, a live on-demand compound predicate, a signal-only request, and an ordered cursor with a custom comparator all retain their row through QueryClient dehydration, reconstruction of Seroval's emitted stream payload, and Query Core hydration from that payload. The on-demand query functions still receive the original IR, comparator, and signal; serialized metadata retains enumerable user metadata but excludes request options. The original implementation fails the compound and signal cases at the stream checkpoint. A serializable stream-only request-options leak passes the former chunk-count check but fails the current payload assertion. This owner covers successful Query cache entries at the installed Query Core 5.90.20 and Seroval 1.5.0 versions. A TanStack Start integration owner still needs the reporter's Router 1.171.33 and Seroval 1.6.8 path, browser Collection resume/refetch, and request cancellation across SSR. Query subset and pagination oracles remain owners of predicate/order/cursor meaning and supported value types. The issue's `shouldDehydrateQuery` workaround replaces Query Core's success-only default and admits pending/error queries; any documented workaround must preserve that default filter. | +| Query DB ordered refetch | `packages/query-db-collection/tests/refetch-ordered-prefix-oracle.test.ts` | Issue #2136: after preload, and after `utils.refetch()` or a Query invalidation settles, an on-demand Query Collection behind a LEFT-joined, ordered, limited live query must show the window over the current backend rows. The design under test: query-db reports `invalidateLoadedSubsets()` when an ordered page changes keys and either has a later page or ends earlier in the order than before, and each ordered loader runs its bounded prefix repair. Its model is a pure top-k of marked backend rows. A fixed and a random campaign generate rounds of marker flips (which serve continuation pages), backend changes while the provider lags, a second live query (`observe`) whose own demand can load a page's new row first, optional Query invalidation with further changes, and a refetch, across one or two order terms, a numeric or locale-collated string order, limits 1–3, offsets 0–1, and a static or function Query key. With an unchanged backend, a repeated refetch must serve no more rows, start at most one first-page request, and report nothing. A refetch or invalidation must never start a new full-source request. A coverage law runs at the same checkpoints: every backend row from the start of the order through the window's last row (every row, when the window is short) must be in the source Collection with its current values, so a latent gap fails before a marker exposes it; a named replay of the issue trace with the moved row unmarked passes the window law and fails only the coverage law when Query DB never reports. The grammar can hold an optimistic update across a refetch, with the checkpoint moved to its release, and a premise-directed campaign under a hold (other rows observed, a row moved ahead of the page's end, a new row further ahead, a write to the last-sorting observed row) requires a report during a hold in every case and fails a skip that reads rows held back behind the transaction; the general campaigns did not. The general grammar holds optimistic deletes as well as updates. The generated campaigns declare reach (minimum 3) for a lagging refetch after a continuation, one during a hold, and a refetch that reported while the window was full, where the loader decides between ignoring the report and repairing. A named replay of a refetch during a held delete left the window short after release, also with this PR's report and overlap changes reverted, until the fix that resumes a continuation after a synchronous answer; no window is checked during a hold, where #2148 and #2169 remain open. Reports (`loadedSubsets:invalidated`) are bounded: a step reports at most once, and only when some ordered request was answered again with different rows; named replays require no report when a one-page window, or a window's last page, only loses a row, and a sync-boundary replay requires a report when a page ending on a row without an order value has its end move earlier (on the earlier gate, which read that page as empty, it reported nothing), and another requires a report when the provider returns a one-page window's rows in reverse order (the earlier gate took the last array row as the page's end). When a page's end moves earlier, Query DB reports. A loader whose window lies inside its own first page skips the repair only while its page read past the window or it read the tie of the current end; after an insert into a top-N list moves the end, a named replay requires the loader to prove the window again with bounded reads, no full-source read, and an unchanged Query count. A filtered one-page window whose page end moves past an unseen row must still repair (found by the random campaign; a loader that always skips fails it, the f9 tie replay, and the ordered-work oracle's signalling replays). The skip also refuses while the source holds accepted sync rows back: a replay refetches while an optimistic update persists, so the report arrives before the refetched rows are visible, and a skip that reads those older rows fails it. The ordered loader state oracle checks that a prefix read issued before an ordering invalidation, which settles after the bounded repair, does not retire the repair's prefix; the repair-first order failed before that fix. A `loadSubset` call that acquires a released, invalidated page can report, because Query then refetches that page on its own. A failed repair shows only the last window or the right one. Named replays cover the issue trace after a refetch and after an invalidation, with a page-capping provider, with another live query loading the page's new row first, with an empty later page, with a page whose end moves earlier while a tie request holds its old last row, a second refetch after an invalidation, a repair failure, and a provider that honors neither cursor nor offset. Never signalling, the old overlap-to-full-source rule, ignoring empty later pages, reporting on any page change, removing the repair loop guard, and reporting once per page each fail it by assertion. A replay makes a page's application fail and checks the window shows its last result or the right one, then the next refetch restores it; recording a page before its rows are accepted cannot be distinguished, because the failed attempt reports on its own. Open: the report laws bound reports but do not require each one; a page whose end moves earlier reports even with unique order values, where no row can be missing; it does not check whether a repair waits for in-flight work; the generated campaigns use an uncapped cursor provider only (an offset-only provider and a page-capping provider each hit a loader defect that reproduces on main: #2182, #2183); the generated campaigns do not check the window after a marker flip (a named replay checks the trace where the loader stopped refilling after a joined-side removal, which the repair-retirement fix below closed); a failed repair still recovers through the loader's full-source retry; interval, focus, and reconnect refetches share the invalidation path but are not driven; NaN order values have no executable witness; overlapping `refetch: true` acquisitions on one Query hash are counted, so the first to finish does not clear the loop guard for the other, but no witness separates that from a plain set; a Query refetch that joins a running `refetch: true` acquisition on the same hash does not report, and no witness reaches it (#2185); follower tabs of a multi-tab persisted collection never receive the report (#2174). The offset-only and page-capping loader defects have `it.fails` witnesses in the oracle. A finished bounded repair used to discard its boundary when its retirement made Query DB remove a row the loader had sent; the loader then restarted paging with a smaller first page that could not retire the repair's prefix, so every later refetch read both. A named replay found by the random campaign (at four times the run count) and an ordered loader state witness fail on that, and the fix also closed the joined-side refill defect. At four times the run count, the random campaign found a short window that stopped paging before the last row of the order: a report's repair released continuation Queries, and Query DB answered their reacquisition synchronously from its cache, so the graph turn inside each request asked for more while the loader was still requesting and nothing resumed the staged continuation. Only the coverage law failed. A named replay and an ordered loader state witness (a synchronous answer whose writes the graph already processed must continue the chain) fail on that, and pass with the fix. The loader side of the report also has a second owner: the ordered-work oracle's `refetch-signal` provider re-runs each served request alone and reports when any answer changed, across that oracle's generated plans, and a signalling refetch must start no full-source request unless a bounded page in that step ended on a null or NaN rank, which takes the documented full-source fallback (a random seed found that case). Never signalling and the old overlap-to-full-source rule each fail it. It excludes a peer live query, which can keep a short window after a refetch (#2155). The persisted-collection oracle checks that a source's report reaches the Collection through the managed cache-generation wrapper and that a retired source session's report is dropped. Remaining work for this class is tracked in #2186. | +| Ordered acquisition | [pagination](https://github.com/TanStack/db/blob/main/packages/db/tests/query/pagination-oracle.property.test.ts), [ordered work](https://github.com/TanStack/db/blob/main/packages/db/tests/query/ordered-work-oracle.property.test.ts), [ordered lifecycle](https://github.com/TanStack/db/blob/main/packages/db/tests/query/ordered-lifecycle-oracle.property.test.ts), [ordered loader state](https://github.com/TanStack/db/blob/main/packages/db/tests/query/ordered-source-loader-state-oracle.test.ts), [graph scheduler](https://github.com/TanStack/db/blob/main/packages/db/tests/query/scheduler.test.ts), [issue #1880 ordered-repair review](oracle-reviews/issue-1880-ordered-repair.md), [issue #1882 custom local collation review](oracle-reviews/issue-1882-custom-local-collation.md), [issue #1898 relation-filter review](oracle-reviews/issue-1898-relation-filter.md), [PR #1909 external review](oracle-reviews/pr-1909-external-review.md), [eager ordered-window repair review](oracle-reviews/perf-ordered-window-repair.md), [reversed index reads](https://github.com/TanStack/db/blob/main/packages/db/tests/reverse-index-oracle.property.test.ts) | Complete finite provider results, inherited locale and bounded/unbounded custom collation with exact request options, query-level inheritance across source aliases, real lexical/numeric disagreement, custom comparator ties, scan/index paths, pending windows, ties/nulls, lease ownership, Effect callback gates, including an indexed source update during Effect startup, and documented repair timing. Direct LEFT-joined filters cover local finite prefixes, pending child demand, child removal, and anti-join publication with custom full-source and unindexed fallback loading; unrelated include demand cannot stall the root continuation, and an empty joined replay wakes held Effect callbacks; remote relation hints remain outside this owner. Graph scheduler checks coalescing, dependency order, synchronous loader input, and repeated-alias failure ownership. The ordered-work oracle checks that a synchronous root refill reaches D2 before joined publication; a mutant that omitted the post-loader graph step failed at the second child demand. Custom collation proves local full-source acquisition only; it does not establish provider cursor capability or backend collation fidelity. Sibling-source input during a synchronous ordered continuation returns to graph work before another ordered acquisition. Request completion is not proof of unrequested source extent. A window move during existing joined demand waits for its root continuation, including an independently held root request; current demand failure rejects the move, while retirement and an obsolete rejection do not. A bounded ordered repair resumes its refill after joined demand settles. The ordered-source-loader state test checks that an explicit failed-request retry blocked by joined demand retains its generation until it can issue a request. Multiple simultaneously pending joined plans during a window move and a public-query failed-retry overlap still need dedicated witnesses. The ordered-work oracle also checks an eager indexed window over generated upserts and deletes: the rows equal the filtered, sorted model, and each change delivers a bounded number of source rows; an unbounded or always-full repair read fails it. Its filter dimension adds a function filter and a LEFT-joined filter, and a campaign fixed to the joined premise checks that a window refilled past a distant row the query already received holds the first eligible rows. Joined filters also run as anti-joins, change eligibility through the marker or the root row's join key, and drive an Effect beside the Collection. A full-join filter keeps every root row and adds a null-ranked rootless row for each marker no root row matches; on main a bounded root prefix showed markers whose root rows were not yet loaded as unmatched rows, and restoring the old inner-or-right full-source rule fails eight tests. Null placement is a generated dimension, with the index built to match, and a query that ignores it fails six tests. A full-join campaign has few or no filler rows, a matched null-ranked root row and at least one orphan marker in every case, so its windows reach the null group where rootless rows and null-ranked root rows tie; a reconstruction check samples exactly the cases its fixed-seed run executes and requires such windows (19 rootless and 7 mixed null-group windows of 40 at one times the run count), and the Effect runs beside the Collection. Full joins read their full source, so only the rows law covers them; paging them under the old join rule fails the window law. Pinned cases cover a null-ranked root row past a prefix that ends in the null group. Paging a nulls-last full join passed this oracle but was not adopted: it cost more per delete when the window reached the null group, which no work law here measures, and #2130 tracks full-join paging. Joined filters can match a row through several markers, and the model keeps one result row per matched pair; a model that collapses them fails. The eager harness also runs an on-demand source whose provider follows the LoadSubsetOptions contract (offset only without a cursor, the applied receipt returned). Its test provider combines a sync adapter and its provider (glossary model alignment rule 2); a held request is a physical acquisition whose provider answer is delayed, a served range is the prefix and boundary one answer establishes (a model abstraction), and the peer is a second live-query Collection. The provider sorts by the requested order and keeps its own ledger of sent rows. A forwarding provider sends live changes only for rows it sent or rows that match a served subset, and its served rows obey the work bound; a loader that asks for 100 extra rows fails it. A refetching provider, like query-db-collection, sends nothing until a refetch. The refetch keeps every served ordered range whole, as the LoadSubset contract requires: it sends every row from the start of the order through the last order value a limited page returned, and rows in no range leave; the rows law over the model applies once no change is pending. After every step on either provider, the Collection must hold exactly the rows in the provider's ledger, and the query and the Effect must show their window over those rows; a mutant that drops marker removals fails this law at steps 0 and 1 of the fixed refetching campaigns, earlier than the model's rows law, and in one more campaign. A holding provider answers each load only at an `answer` step, from the rows at that moment, so live changes arrive while loads are in flight. While a load is in flight, the query and its peer either keep the window they showed at the previous checkpoint or show distinct ledger rows that pass the query, in its order; the Effect is not checked then. That is weaker than the architecture's barrier rule, under which a repair or window move keeps the last public snapshot until it settles: a barrier law fails today on on-demand joined plans, where a visible-delete repair and a held `setWindow` publish before they settle (#2131; the law and traces are kept for that fix). The provider records a request as served only once it answers it, and an abort (or an adapter unload) of a held request follows `LoadSubsetOptions.signal`: a cooperative provider installs none of its rows and its load fulfills at once, while a provider that cannot cancel in-flight work (`late` cancellation, generated) answers it anyway and its load fulfills after those rows become visible. A `release-peer` step cleans up the peer while its loads are held, which aborts them; each request counts once although its acquisition release both aborts and unloads it. The fixed held campaign reaches 5 aborted held requests and 1 late answer, and its reconstruction check requires at least 3 cooperative cancellations and 1 late answer. A held load rejects when its applied receipt rejects, and the step then fails, because no signal or cleanup rejects a receipt in these histories; that branch is unwitnessed. The held campaign pins a forwarding provider, so `release-peer` never reaches a refetching provider; that branch is unwitnessed. A request whose signal is already aborted when the provider receives it is never reached; that branch is unwitnessed. The held campaign uses a forwarding provider and an indexed source filter or a joined filter, deletes three visible rows and then upserts an eligible row with an id no other command uses; its reconstruction check runs in the same test as the fixed-seed campaign and reads its per-case counts: 35 of 40 cases issue a held request, and every one of them changes rows while it is in flight. Loading must reach a fixed point: past a request budget the provider rejects and records it, and the initial load, every step and the end of each history assert the record is clear (a loader that re-requests a prefix it already holds can loop without reaching the provider, which this cannot see). A bounded step makes at most four provider requests, and each consumer's initial proven-prefix load is bounded separately, in total transfers (a repeated request for the same prefix counts) and in distinct rows (at most the source's size); a loader that asks for 100 extra rows on every page fails that bound and the initial-load work law. Loader mutants that never resume after a boundary request, or that ignore a load request while another load is pending, fail it; a mutant that drops a window move blocked behind a pending load still survives, even with the settlement check on held moves; whether it is equivalent is open. Open (#2148): on an on-demand source with a joined or anti-joined filter, an optimistic delete of the visible row leaves the window short until the delete persists, because the next row is never loaded. On an on-demand source with a source filter, and on an eager source with any filter, the same delete refills the window correctly; these are the contrast cases. Owner: this oracle. Needed witness: the optimistic-delete dimension (kept as a patch for #2148) in the grammar, plus a pinned replay of the anti-join trace. A refetch that re-runs each page alone, as query-db-collection does today, leaves a row that moved between two pages unloaded; random seeds found that trace, a named replay fails on it, and a query-db receiving witness reproduces it (#2136 tracks the provider fix). An Electric-style provider, which skips updates to rows it never sent, is not modeled; a mocked-stream witness shows a row moving into a served ordered range never arriving under the default replica mode (#2138). At one times the run count, the campaigns reach 87 refetch steps: 39 change the window, 6 remove a visible row, and 13 lead to a further provider request. A random seed found a refetch that empties a joined window, after which the loader refills it over several page requests and publishes `[]`, one row, then the final rows; a named replay keeps that trace for #2131. No incorrect final window has appeared on that path, and no loader mutant tried so far is caught only by it; the work law does not cover it. For an on-demand joined or anti-joined filter, the initial load must not request the whole source when the first window's worth of source rows in query order all pass the join and no boundary value is null or NaN; 35 executed cases meet that premise, and a compiler that reads the whole source for every joined filter fails eight tests on this law alone. The rows served by that initial load stay within the prefix, its boundary ties and a refill, for each consumer. Later steps of those plans have no work bound. An unindexed join filter over it left the window empty on main, until the plan counted the rows it still needs; restoring the old guard fails the campaigns and a pinned replay. A step that changes the visible rows must publish, and its subscriber starts with every visible key, so a removal-only change reaches it; mutants that drop update-only or removal-only publications fail. A step may publish one intermediate window per provider request it made, and only its last publication must show the final window, because a repair or refill can publish after each round trip (#2131); the content of those intermediate windows is unchecked. The model keeps its own copy of the markers, and full-join histories can insert the root rows of orphan markers. A premise-directed campaign inserts a fresh row and then changes an initial row's eligibility alone, so the row leaves the window only through the join; it and a named replay fail when a window row past the loader's settled boundary counts toward a full window. One case in four adds a second live query over the same sources with a larger window, and its rows law holds at every checkpoint; the work laws do not apply to those cases. #2155: a second live-query Collection over a refetching on-demand provider kept a short joined window after a refetch removed a tied row (the peer showed ranks `[0, 0]` where the model shows `[0, 0, 100]`). The loader fix that resumes a staged continuation after a synchronous answer the graph already processed closed that trace. The pinned replay of that indexed trace now expects the right window. Still open: an unindexed peer over a refetching provider keeps a short joined window (the peer shows `[100]` where the model shows `[100, 101]`), which also fails without that fix; each campaign therefore still excludes the peer and refetching pairing, as its last map. Owner: this oracle. Needed witness: a pinned replay of the unindexed trace flipped to pass, and the pairing restored to the grammar. Limits: Effects are checked for rows after each step, not for publication counts or positions. Histories move the first query's window with `setWindow` (150 moves at one times the run count, 56 on demand), and its rows law and ledger law then follow the moved window; the Effect and the peer keep theirs. On a holding provider every window-move promise must have fulfilled after the final drain. A refetching provider models query-db's unload: it drops the unloaded request and removes rows that no remaining request's last answer holds. The per-step work law applies only to forwarding, immediate, single-consumer cases: it skips refetching, holding and peer cases and move steps; a two-term indexed move scanned the source four times, which no law here bounds. Multi-term joined plans stay outside this harness, on both paths; on the on-demand path, the provider's transfer is bounded only for indexed single-term source-predicate plans. Joined windows compare order values, so a wrong row tied with the expected one passes; a window row tied with the boundary counts as proven before its tie request settles, which no witness exercises. A boundary that cannot advance past a window row would repeat one page per new key; reading the boundary's range locally should always reach that row, but no witness shows it. Joined ties are compared as any valid first rows; whether source-filter ties may be fixed to the source key is open. The same grammar includes NaN and null ranks and a second-order term, and bounds both rows read and rows delivered, including the initial load and index refills; a scan law limits each change to one pass over the source. A pinned case checks that an indexed descending window places nulls first, and a lifecycle witness checks that an eager full-source window is ready with its rows at creation when its source answers synchronously. The initial fallback cells check indexed and unindexed locale strings with distinct and tied values, plus a null boundary after a warm ordered request. The warm null-boundary case crosses preload-started and constructor-started live-query sync runs: the former completes at the synchronous cut, while the latter resumes deferred on-demand acquisition asynchronously and publishes after settlement. They require an unfiltered full-source request, no public row at fallback entry, and the expected row and status at the immediate readiness check. A Promise fallback stays loading at that checkpoint. A React receiving witness checks an eager locale-string `findOne()` at the first layout commit. A Query DB receiving witness checks warm cached subsets: both raw adapter calls return literal `true` with the source rows visible at each return, and the live query is immediately ready without a fetch. The same fallback cells observe subscriber changes from before the first source write: literal-true and Promise fallbacks each publish one nonempty final-window insert after the fallback settles, and the callback sees the complete row. Promise cells show no nonempty event at the immediate cut; a duplicate-event fault fails all eight cells. Other adapters and a real on-demand React first commit remain unwitnessed. Full-source work after initial publication and repair remain outside this synchronous guarantee. A separate post-publication oracle crosses marker deletion and retargeting with literal, Promise, and held-applied-receipt fallback responses. It also settles the child demand before holding the receipt, so the public live-query loading flag isolates the root fallback. The model checks final rows and net events, while event replay checks every callback snapshot; a retained or empty intermediate window is allowed. A loader fault that ignores the pending receipt fails at the held loading-flag check. Two literal-true cleanup-failure histories require that the replacement still publishes and the adapter error reaches the public query status without making the unrelated marker commit throw; the provider's applied receipts are recorded and compared outside provider callbacks. The Promise case checks eventual coherence on an asynchronous callback path, without claiming a separate timing cut. Four held-Promise histories apply fallback writes, change another join marker before provider settlement, and compare each callback and the final winner; an injected stale extra publication fails the event comparison. Other marker actions and overlapping root-source changes remain open; the provider has no later write or rejection, so this schedule does not establish when the loader claims full-source authority. The reversed-index oracle checks every reversed read's order and filter work for both null placements. | + +| Indexed predicate filtering | `packages/db/tests/query/index-path-collision-oracle.test.ts` | A bounded cross product of numeric values checks exact public keys before and after adding a nested-field BTree index. Direct predicates vary argument order, bound inclusivity, reversed operands, and bound field. Public Collection subscription and live-query callbacks run with and without the index. Selected-field ordering compares a direct JavaScript sort with a public `$selected` callback; restoring the dotted-key proxy cache made that comparison fail. The original compound grouping failed four indexed cases while scan controls passed. The original callback proxy caches also failed both unindexed public routes. A hostile cleanup control preserves the primary mismatch and secondary release error. Arbitrary path segments, nullish values, custom collation, and incremental publication need separate witnesses if claimed. | +| Lazy target path identity | `packages/db/tests/query/compiler/lazy-targets.test.ts` | A focused same-source `UnionFrom`/`coalesce` witness requires both [`a.b`] and [`a`, `b`] demand targets. Restoring dotted-string deduplication drops the second target at the compiler boundary. A public on-demand adapter and row-publication history still need a separate witness. | +| Correlated include path identity | `packages/db/tests/query/includes-context-transport-oracle.test.ts` | The fixed path witnesses live in `packages/db/tests/query/includes-context-transport.test.ts`. A flat parent field and nested parent field reach one-level and nested `toArray` results independently; dotted ancestor aliases remain distinct through a grandchild route. Initial results and parent updates have exact public-value checks. A conditional projection gives [`a.b`] and [`a`, `b`] different include results and checks parent-key changes plus later child inserts. Removing the compiler's unique route-key allocator loses a public child result; restoring dotted-string deduplication at either builder site loses a distinct parent value. These fixed witnesses do not establish arbitrary path segments, every recursive source form, or every materialization form. | +| Alias scope identity | `packages/db/tests/query/includes-oracle.property.test.ts` (`includes alpha-renaming across lexical scopes`), `packages/db/tests/query/includes-scope-identity-oracle.ts`, `packages/db/tests/query/includes-alias-shadowing-oracle.test.ts`, `packages/db/tests/query/includes-outer-join-route-work-oracle.test.ts`, `packages/db/tests/query/validate-aliases.test.ts`, [issue #1975 review](oracle-reviews/issue-1975-scope-identity.md) | Four topologies place a source beside a same-named source: a `from()` subquery with an include, a joined `unionAll()` branch with an include, a collapsed top-level pure wrapper with a join subquery, and an include directly on a joined `unionAll()`, which may reuse a branch alias. The grammar crosses zero to two joins, LEFT or INNER, zero to two predicates, plain, ordered-and-limited, or DISTINCT bodies, include forms, and eager or on-demand finite providers, after preload and after source writes. Includes read their source directly, through a join, through a `unionAll()` with an anchor join, or through a `from()` subquery that reads the parent row. One in four scenarios draws any naming; same-scope and union-branch repeats are rejected while ancestor shadowing is admitted. Each checkpoint compares the canonical naming with plain recomputation, the generated naming with the canonical naming, and per-Collection `loadSubset` WHERE clauses. The exact-output companion independently recomputes parent-child rows for direct, QueryRef, union, and implicit-join forms across two equality operand orders, shadowed and renamed aliases, eager and on-demand children, initial publication and parent/child writes. The recursive forms keep a required outer lockId correlation and an independent captured lockName predicate; the union has a nonempty right branch and a later two-branch overlap. It checks enumerable user and virtual key names, user values, multiplicity, sibling helper reuse, a fresh parent/inner QueryRef declaration across a source update, direct, child union-branch, parent union-branch, parent QueryRef, and nested-include rejection of ancestor/descendant builder reuse, one nested grandparent capture, functional callback child input, grouping, captured whole-row and nested-path spreads across shadowed and renamed aliases, eager and on-demand children, and parent/child writes, and query identity. Focused follow-up cells check same-path group keys under shadowing and renaming through parent and child writes, alpha-renamed pure wrappers at public-row and optimized-identity cuts, and indexed lazy demand for direct or wrapped nested includes through anchor and user writes. Wrong-query controls that drop either inner predicate or substitute a parent-shaped callback value fail at the initial public-row comparison. A finite follow-up crosses direct includes and joined QueryRefs with shadowed or renamed aliases, eager or on-demand sources, and child IDs equal to or distinct from an outer filter literal; it checks public rows and on-demand child admission after preload. Another follow-up projects a captured parent rank through a child QueryRef and checks public rows after preload, a child-rank change, and a parent-rank change. The original compiler misrouted the outer filter in four cases, and the original optimizer lost the captured binding in two cases at their first public-row comparison. Every pinned witness and both campaigns fail on `18abceee4`; each of the four optimizer re-mint sites fails a witness when reverted. Arbitrary nested includes, include-inside-subquery forms beyond the checked LEFT joined QueryRef, other join-subquery topologies, `having`, outer joins beyond the bounded RIGHT/FULL matrix, publication events, unload, ordered windows, other lazy join targets, and temporal demand remain outside these owners; they need witnesses before an alias-scope closure claim covers them. Ancestor/descendant builder reuse is rejected before correlation extraction; one helper builder remains legal across a parent QueryRef, union branch, or joined QueryRef and a sibling include child. Six placement/source-mode cells check construction, exact rows after preload, and a source update; the joined placement includes an unmatched manager that would expose a missing helper join. The original source-tree guard rejects all six. A discarded enumeration of a captured proxy cannot change the selected query identity or rows before or after a child update; the retained-marker implementation fails that identity check. The [PR #2079 follow-up](oracle-reviews/issue-2072-alias-shadowing-followup.md) records its RED trace, the repaired spread and nested-QueryRef cells, and the remaining compiler and optimizer evidence gaps. The [ad5e0108 review](oracle-reviews/issue-2072-alias-shadowing-ad5e0108.md) adds a self-join with an inner alias matching the outer joined source, a joined child whose local source competes with inherited parent context in matched and unmatched LEFT joins, captured/local spreads, grandparent whole-row capture, and direct captured-alias selection. These finite cells compare public rows after preload and selected writes; an alias-only lazy-target mutant fails the eager self-join comparison. The reported on-demand failure did not reproduce with the controlled source. A further finite matrix crosses nested FROM or JOIN placement, field or whole-row capture of a shadowed outer joined alias, shadowed or renamed spelling, and eager or on-demand acquisition. It compares source-role rows after preload, child deletion and movement, joined-row update and movement, and parent update; on-demand cases compare nonempty request sets. Disabling projected binding lookup fails all eight cells at the initial public-row assertion; an alias-only lazy-target mutant survives this matrix, so it adds no broader lazy-target claim. A bounded outer-join extension crosses RIGHT and FULL joins, main- or joined-side correlation, direct and QueryRef joined sources, shadowed and renamed aliases, eager and on-demand sources, two parent routes, and seven initial or post-write cuts. Its independent source-role model checks exact public rows, including both absent sides; on-demand cells also require source acquisition. A join-operand extension crosses an ancestor term combined with a local main or joined term, or serving as the whole main key, with equality reversal, alias renaming, source mode, and five public-row cuts. The [outer-join follow-up](oracle-reviews/issue-2072-alias-shadowing-outer-joins.md) records RED and GREEN evidence. A foreign captured join operand must reject: a finite provenance matrix checks root and include placements, matching or different alias spelling, and eager or on-demand sources at construction or preload settlement; valid local and captured-ancestor operands remain controls. A two-LEFT-join history now checks that a later routed QueryRef or captured operand cannot restore an absent shadowed local source, across alias renaming, eager/on-demand mode, two parents, and local and second-joined arrival/removal. It also selects and updates a parent label while joins are matched, checking that the main route retains that ancestor value. A nested joined QueryRef control projects its join key from an inner tag source while the post alias shadows or renames an outer user alias; an independent plain-row model and installed on-demand post/tag rows are compared at preload, a user-key move, and a tag update. A wrong Collection/SourceId pair loads an irrelevant post at preload and fails the installed-row check. Other nested source shapes and real-provider request schedules remain outside this control. The route-work owner compares public rows and assembled parent-route work as irrelevant joined rows grow for direct main-correlated RIGHT/FULL joins in both source modes; an unconditional-route mutant grows from two to six pairs at preload. RIGHT joins correlated on main now join the outer-join row matrix, beside joined-side correlation and FULL controls. An include child rejects a bound join operand declared inside its parent QueryRef or union branch, across matching/renamed aliases and eager/on-demand sources; the original source-tree collector resolves preload instead of rejecting. A joined QueryRef rejects a captured source declared beside it in the enclosing query: the matrix crosses root/include placement, matching/renamed inner alias, and eager/on-demand sources; four true-ancestor controls assert rows, and the permissive sibling-scope mutant fails all eight invalid cells while those controls pass. The primary oracle now extends the hidden-source rejection through two QueryRef or union levels, with matching/renamed aliases and both source modes; a source-tree-binding mutant fails all eight added cells at the rejection cut. An unrelated bound reference is rejected in WHERE, GROUP BY, HAVING, ORDER BY, direct SELECT, and conditional SELECT across matching/renamed aliases; legal local controls and a grouped include with captured ancestor predicates, HAVING, ordering, shadowing, and parent/child writes prevent a blanket rejection. It also rejects foreign references moved into extracted include parent filters or into the removed correlation equality, each across alias spelling and source mode with valid parent controls. Disabling expression validation fails twelve non-join cells; omitting parent-filter or extracted-correlation validation fails four cells each. A separate plain relational model checks RIGHT-to-LEFT, RIGHT-to-RIGHT, FULL-to-FULL, and FULL-to-LEFT two-join chains under shadowing/renaming, both source modes, main/joined/tag correlation, and six source or ancestor writes; a later-FULL-as-LEFT mutant fails two initial public-row comparisons. The route-work owner now compares public rows and parent-route assembly counts at preload and four main-source writes while irrelevant joined rows grow. Other expression forms, arbitrary join-subquery and lazy-target shapes, deeper captures, other chained outer joins, real-provider schedules, and broader source-work bounds remain open under this owner. The alias-scope owner also checks outer grouping through a nested QueryRef and joined source with shadowed or renamed inner alias, eager or on-demand acquisition, and four source/tag writes at five public-row cuts. The tag insertion duplicates one joined source and the plain model counts both copies. A controlled rewrite probe reached an unbound pushed WHERE inside the QueryRef while the bound GROUP BY remained outside; an inner GROUP BY blocked that pushdown. A matcher mutant that ignored binding IDs failed two same-path group cells at the first public-row cut; a dropped-pushed-predicate mutant failed all four new cells by publishing an extra group. This refutes the reported WHERE-rewrite-to-GROUP-BY-matcher route in those shapes; another lost-provenance route needs a concrete fixture. Base/head work probes confirmed an extra pure-wrapper stage after #2079, but the same full-source outer-WHERE request and keyed lazy-join request on both revisions. No wrapper-stage work budget or safe binding remap is established. Bound root refs caused two route-metadata symbol checks per evaluation on the current head versus zero on base, while captured-ancestor rows remain protected by the primary oracle; a compile-time fast path needs a justified work budget and a preservation witness. The same function-form subquery placed twice compiled two results and invoked its callback four times for two source rows on both revisions. Cache comparison read more query properties on the current head, and a stateful callback produced placement-specific values. A callback identity or purity rule and work budget are needed before cache-equivalence changes. | +| Join equality and cold acquisition | `packages/db/tests/query/cold-join-reconciliation-oracle.test.ts` | Independent recomputation for cold acquisition plus direct join/predicate equivalence across established equality domains. Binary/string and nullish classes, replacement histories, raw on-demand values, and both scan/auto-index paths are explicit; the compound lane also covers tuple equality, term order, all four join types, controlled cold sources, and keyed histories. Positive acquisition witnesses require a satisfiable tuple. | +| Aggregate selects | [aggregate select oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/query/aggregate-select-oracle.property.test.ts) | Generated select shapes of aggregate queries with and without `groupBy` by `c.k`, the pair `c.k`, `gt(c.v, 1)`, the triple that adds `gt(c.v, 2)`, `upper(c.k)`, `inArray(c.k, ['a', 'b'])`, a Date-valued `coalesce` or a `NaN`-valued `add`, rebuilt per use (literals, parent fields, the group key, the raw field `c.k`, source fields, `count`/`sum`/`max`, `add`, `caseWhen` with expression or object branches, nested objects, spreads, a nested include) and a HAVING condition that may compare an aggregate, selected as a field or not, at the top level, directly or through a `unionAll`, and in an include whose source may shadow the parent alias, through parent updates and source inserts, updates (including key changes that move a row to another group and route) and deletes. A shape throws `NonAggregateExpressionNotInGroupByError` exactly when a non-key source field appears outside an aggregate in its select or HAVING, or the shape spreads a row or holds a nested include; otherwise every published group equals the model and keeps the select's field order. The same grammar checks the opt-in operator laws: a grouped query whose select and HAVING hold no aggregate throws `GroupingOperatorRequiredError` when its keys are passed bare, and works through `groupKeys()`; a bare query builder throws `IncludeOperatorRequiredError` as a field, inside a nested object, or in a conditional branch; a wrapped include inside a nested object throws (a conditional branch may be an include; the includes-collection oracle publishes one, and a compiler that rejects one fails both that oracle and this one). When several errors apply, any one of them passes. Includes are wrapped in `toArray()`, `include()`, or `materialize()`. In three generated histories of six, the driver strips one kind of operator code, the grouping compiler, the include runtime, or the grouping compiler of the `orderBy` aggregate only, from the IR it built. One history in three is a forced history that strips the grouping compiler everywhere or from `orderBy` only, with no, one, or two group keys in turn, so every campaign reaches each path and compiles it through `new BaseQueryBuilder(ir)`; IR without its operator code must throw the coded error for the missing operator. A runtime-stripped history gives its include a child query that is valid on its own, so other errors cannot hide the missing runtime. Half the grouping-stripped histories give the top-level query a select of `count()` alone, with an aggregate HAVING when the generated shape has one, so a model error set of exactly `groupingRequired` witnesses the grouped, one-group, and HAVING paths. The top-level and include queries may order their groups with one or two `orderBy` calls, each on an aggregate or on a number or string select field through `$selected`, strings with the documented `en-US` collation: published rows and each parent's `kids` fall into blocks of equal values in that order. An `orderBy` aggregate that no top-level field selects throws `AggregateFunctionNotInSelectError` (now stated in the guide's "Ordering by SELECT Fields"), and a stripped `orderBy` aggregate compiles with the compiler that `groupKeys()` or a select or HAVING aggregate brings. One history in four takes ordered shapes from a grammar whose shapes compile, because generated ordered shapes compile in about one case in forty; the source starts with a key `A`, where locale and code-unit order differ. The property declares twenty-one states with `createOracleReach`, and each campaign of 160 histories reaches each of them; without the forced select, the one-group and HAVING states were never reached, and a mutant that throws the wrong error class only on the implicit path passed three campaigns. Pinned witnesses in `hand-built-ir-operators.test.ts` cover the old `IncludesSubquery` argument order, an aggregate only in `orderBy` without a select aggregate, and the mixed `orderBy` replay. The compiler before the order step took the query's grouping compiler fails the generated `orderBy` dimension (`top compiles`); an order step that ignores direction, keeps only the first `orderBy` call, or sorts strings by code unit, an include query that ignores direction, a group step that keeps only the first one or two keys, and a check that throws the wrong error class only beside HAVING each fail it (the last passed two of three campaigns before the alone states). A mutant without the include-runtime check, which publishes `kids: null`, and one without the grouping guard, which throws a `TypeError`, both fail it. A mutant that skips the grouping-operator check fails it with a `TypeError`, and one that treats a bare builder as an include fails it with the wrong error class. Open: an `orderBy` term on a raw expression other than an aggregate, and on boolean, Date, `NaN`, or object fields, whose order the docs do not state. The operator laws are generated only for aggregate and grouped selects; for plain selects, `includes.test.ts` pins a bare builder as a field, nested, and in a conditional, because this model publishes one row per group and needs a separate row-per-source model for them. Open: the random campaign's declared reach of "a string field orders A against a lowercase key" can miss on an unlucky seed. One full run on 2026-10-28 missed it (0 histories at 160 runs); 15 reruns reached it. The reach check runs after the campaign, so the miss records no replayable seed. Needed: a grammar weight that makes the case common, or a reported seed for a missed reach. | +| Query optimizer semantics | `packages/db/tests/query/optimizer-semantics-oracle.test.ts`, [aggregate review](oracle-reviews/2026-09-28-optimizer-aggregate-pushdown.md), [QueryRef repair](oracle-reviews/2026-09-30-queryref-publication-repair.md), [residual review](oracle-reviews/2026-10-01-pr-1977-residual-convergence.md) | Independent sum recomputation and a materialized-Collection formulation check the first public snapshot of a nested aggregate under a left join. Direct, arithmetic-wrapped, and conditional aggregates distinguish safe from unsafe pushdown; a grouped-key control covers the grouped boundary. Matching and nonmatching global aggregates also cross a nested QueryRef inner join with absent, accepting, and rejecting outer predicates. The matching initial cases fail on the pre-repair revision and pass after the computed-projection lookup repair. A separate fixed residual-convergence grammar checks exact predicate and outer WHERE clause counts at `optimizeQuery` return and after re-optimization: LEFT, RIGHT, INNER, and FULL joins; equality, greater-than, nullable-field `isUndefined`, same-source OR, cross-source equality, clause order, separate/AND form, two active predicates, two residual sources, and an existing residual marker. Plain-array LEFT/RIGHT and three-source join models check first public rows, including unmatched rows. Arbitrary expressions, more complex join chains, and incremental optimizer/publication histories remain outside this owner. | +| QueryRef operators and user-value boundaries | `packages/db/tests/query/subquery-user-value-oracle.test.ts`, shared `subquery-literal-value-oracle.ts`, supporting `subquery-literal-value-driver.ts`, correlated `subquery-literal-correlated-oracle.test.ts` / `subquery-literal-correlated-oracle.ts` with concrete factory/driver support, supplemental `subquery-user-value.test.ts` and `transactions.test.ts`, [repair record](oracle-reviews/2026-09-30-queryref-publication-repair.md), [chunk 19](oracle-reviews/2026-10-28-pr-2171-oracle-chunks.md) | The independent relational models own joined DISTINCT with outer WHERE, aggregate-source updates and joined findOne histories. Other fixed container/no-select/functional/predicate-literal examples are supplemental ordinary tests, not independent oracle scope. Chunk 19 adds one ordinary-JavaScript literal model with 17 forms × two positions × root/QueryRef receivers: 68 histories and three cuts each. It checks complete ordinary rows, event bags/current/previous values, event-time reads and immutable QueryRef callback inputs, with all 204 receiving and 102 callback states reached once. Strict exact-driver admission passes; unchanged production has eight undefined-to-null REDs (P11), ten empty-container-loss REDs (P12), 50 passing new histories and six passing relational neighbors. Twenty-four hostile and four lawful controls pass; partial selections still fail mandatory reach. Chunk 20 receives the same literal law through toArray, array/singleton materialize and Collection facades: 272 exact histories, all 3,264 path/cut states once, 200 main passes and 72 intended P11/P12 REDs; 33 checker controls and six independent facet controls pass. Legal QueryRef children have their own correlation WHERE and preserve the authored routeGroup input until actual projection removes it. Facade layouts are ordered, startup callbacks permit canonical empty values but owe complete ready inputs, and all 272 titles remain distinct for precise selection. Chunk 21 adds a separate source-public type judgment (`subquery-literal-types-oracle.ts` / `.test.ts`, supporting `subquery-literal-types-support.ts`) for those exact 340 authored receivers: 1,870 actual QueryResult, Collection-row, contextual-input, inline and facade/API markers. All consumers compile without diagnostics under TypeScript 5.9.3, and all 2,550 syntax/admission/marker states execute once. Unchanged production has 280 intended RED histories: 40 raw-nullish precision failures and 272 contextual-input or propagated-inline metadata failures, with 32 overlapping; 60 native histories and 55 checker/source controls pass. Independent review exposed erased call/construct signatures on records; the recorder now retains those declared capabilities and eight existing empty/nonempty × root/nested record controls reject invented signatures, after all eight failed under the original checker. Only the separate four virtual fields at named query-output or published/source Collection row roots are excluded from this ordinary-value judgment; callbacks and inline children remain strict. The full static callback metadata grammar remains open under existing `virtual-row-fields-types-oracle.test.ts`, with the literal sources as receiving witnesses of its accepted P10 law. Source-public inference does not establish declaration portability; an exported inferred factory currently gives TS4058 and is preserved for O22, including exported Collection and built/installed controls. Broader mixed-expression admission, sparse authored arrays, symbols, ordinary-record readonly modifiers, descriptor flags, arbitrary recursion and provider/framework schedules remain open with their sequential receiving owners. Metadata attribution stays with its separate owner; the four reserved fields are excluded only at each observed root row. No production repair or closure is claimed. | +| Opaque backend pagination | [window oracle](https://github.com/TanStack/db/blob/main/packages/query-db-collection/tests/cursor-pagination.oracle.test.ts), [cache histories](https://github.com/TanStack/db/blob/main/packages/query-db-collection/tests/cursor-pagination.cache-oracle.test.ts), [cache publication](https://github.com/TanStack/db/blob/main/packages/query-db-collection/tests/cursor-pagination.publication-oracle.test.ts), [browser acquisition boundaries](https://github.com/TanStack/db/blob/main/packages/query-db-collection/tests/cursor-pagination.boundary-oracle.test.ts), [QueryCollection integration](https://github.com/TanStack/db/blob/main/packages/query-db-collection/tests/cursor-pagination-oracle.integration.test.ts) | Full filter/sort/slice reference, opaque token transport, actual Query cache expiry/invalidation/GC, forced refresh during growth, protocol failure publication/recovery, bounded slice work, nested cancellation/replacement, reader abort, browser retry defaults, manual-write cache isolation, and production window publications. Stable backend sequences; not snapshot guarantees for changing endpoints. Peek-ahead remains enabled. | +| Experimental no-peek continuation | [test-only live-window probe](https://github.com/TanStack/db/blob/main/packages/query-db-collection/tests/cursor-pagination-oracle.no-peek.integration.test.ts) | The candidate uses an authoritative fact only for the same complete output prefix and otherwise requests the extra row. It remains test-only; the shipped opaque-pagination owner keeps peek-ahead. See [experiment limits](https://github.com/TanStack/db/blob/main/packages/query-db-collection/tests/cursor-pagination/NO-PEEK-RESULTS.md). | +| Electric and TrailBase | [Electric histories](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/tests/electric-oracle.property.test.ts), [recovery histories](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/tests/electric-recovery-oracle.test.ts), [held resume snapshots](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/tests/electric-resume-snapshot-races.test.ts), [PostgreSQL semantics](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/e2e/sql-predicate-semantics-oracle.e2e.test.ts), [TrailBase contract](https://github.com/TanStack/db/blob/main/packages/trailbase-db-collection/tests/ORACLE.md) | Installed SDK delivery/framing, independent predicates, exact subscription arguments, restart/reset lineage, held certification and durability races, source-order publication before durability, and late errors. The queued-presence property runs identical fixed/random generators plus isolated seed-and-path replay across insert, update, delete, and truncate callbacks. The recovery fixtures use a mocked ShapeStream; they do not establish live Electric-service framing or native persistence-host behavior. The TrailBase lifecycle oracle's early-demand lane checks that an on-demand load issued before the change listener runs does not fetch until the listener runs or cannot start, through a real live query and the subset boundary: 16 histories cross listen, subscribe rejection, reader failure, abort, and cleanup with a service change before the subscription; the adapter from before the wait fails all 16. Its page-event lane checks that a page read before a concurrent change event neither reverts nor resurrects that row, and its stream-end lane pins that the adapter does not resubscribe: 17 histories, 6 of which fail on the adapter before the page fence. A generated page lane (`trailbase.page-event`) crosses two-page loads, cursor and offset paging, overlapping on-demand loads, and up to four changes among page releases. A held-commit lane checks that a page never fails a load for an older event that core holds while a user mutation persists; deciding insert or update from visible rows fails it. After a generated history drains, the Collection holds exactly the rows a page returned or an event touched. Both page lanes deliver events for changes made before the list call at once, during the page, or after it applies, and check that the listener survives a late insert for a row a page already holds. The retry-decision property (`electric.retry-decision`) generates histories over full-log and progressive waits, with and without `onError`, crossing synchronous and asynchronous retries and give-ups, a throwing `onError`, a later error after a retry, an Electric client give-up, an external abort before or during a decision, new demands, the first up-to-date, a persisting optimistic transaction that holds the retried snapshot and later settles, and cleanup. The retry-decision grammar also crosses eager and changes-only sources, an external signal already aborted at sync start, and a status listener that cleans up inside the failure; it checks that a readiness wait rejects with the same cause as every waiting demand, and that such a cleanup leaves a rejected demand rejected. It also holds the acceptance of the first up-to-date commit across later errors, retries, give-ups, and aborts, and checks that the Collection becomes ready with the snapshot's rows when the commit is accepted, and fails with the acceptance's cause when it is not. The descriptor-isolation oracle holds that acceptance across a managed-cache rotation and checks that the retired session's acceptance settles nothing, whether it resolves or rejects, and that the replacement session's snapshot decides readiness. A progressive snapshot's truncate applies at once; a demand whose snapshot arrived before an external abort settles when its rows apply, and cleanup after the abort rejects it. An independent model checks the status, every demand's settlement and cause, and row visibility after each step: a source that still tries stays `loading`, and only a give-up fails its waits. `main` before #2127 and mutants that fail before the decision, skip the failure on a throw or give-up, or skip `markError` on a client give-up or an external abort each fail it at an assertion. An installed-client lane in the SDK delivery oracle drives the same model (`electric-retry-decision-model.ts`) through the real Electric client and HTTP: a retried 503, a response without Electric headers, and an exhausted 50-retry budget, in eager, full-log, and progressive modes, with a synchronous or asynchronous retry (18 cells). Skipping `markError` on the client's give-up fails the 12 give-up cells; failing before the decision, or `main` before #2127, fails 15; the 3 survivors give up in the same continuation that decides, so an early failure is not observable there. A mutant that settles a held demand on acceptance instead of visibility fails the held replays. Excluded: stream steps while `onError` decides or after a final failure, which the client cannot produce. The source-release property (`electric.sdk-source-release`) checks that cleanup removes every `visibilitychange` listener that the installed client registered for a sync run's streams: one to three runs in eager, on-demand, and progressive modes, each answering up to three requests with row-only delivery, an up-to-date, an HTTP 409 reset, or a retried 503, then staying open, giving up, aborting the external signal, or holding an asynchronous retry/give-up decision through cleanup and later runs, with restarts through `preload()`. It declares separate cleanup reach for partial delivery, reset before a later up-to-date, and a pending error decision. The minimal partial/reset histories cross all five endings in every sync mode through the same driver. It compares the live listener count with the number of active runs after each step. The recorder tracks ordered callback/capture identities with duplicate-add semantics; all 8,421 add/remove histories of length zero to three over two callbacks and five capture spellings compare its count with native EventTarget dispatch before teardown. Recorder teardown independently removes retained registrations after the comparison; bounded callback-subset enumeration checks actual EventTarget dispatch and preservation of a primary failure. `main` before the fix, and mutants that release only while the stream is unaborted or only without a final initial failure, fail it at cleanup; the generated managed-rotation property (`electric.sdk-rotation-release`) checks the approved Electric deadline before replacement `ShapeStream` construction. It rotates one to three times in one on-demand sync run, so a replacement session is itself retired. Each old HTTP snapshot arrives during held rotation, before replacement snapshots, or after their settlement. It declares reach for all three delivery cuts, initial/replacement session retirement, and demands awaiting replacement source responses. A transparent constructor observer records zero retired listeners immediately before the real SDK constructor; both retained demands stay pending while rotation is held and before any replacement response, then fulfill after their source snapshots apply. Fresh public/current-generation durable rows, late old-response rejection, and final cleanup comparisons remain. Each round also remembers its retired storage target and checks that the late response's absent key reaches neither that target after delivery nor after replacement settlement. The seventh declared state requires this retired-target observation. This constrains the expired session's row; unrelated retained rows remain allowed, and a separate run with a valid claim can still use a retired generation. Assertion inputs copy mutable observations so cleanup cannot change their failure payload. The browser listener is observed in jsdom; React Native's AppState adapter and overlapping rotations are outside this generated lane. | +| PowerSync | [tests](https://github.com/TanStack/db/tree/main/packages/powersync-db-collection/tests), `tests/correctness.test.ts` (fixed tests; no oracle owner (tracked in #2264)) | Applied receipt positions crossed with held peers, native SQLite/SDK and cleanup evidence. A metadata-bearing `Collection.update` passes through an observed SDK watcher callback to the public and SQLite rows and an exact queued CRUD patch. Held callback ordering and synthetic zero-field cases remain controlled-only, and remote upload has no receiving fixture here. Run the focused owner with the package's `test:oracles` command. A timeout mutant proves a progress failure, not every value assertion. | +| SQLite persistence and native hosts | [persisted histories](https://github.com/TanStack/db/blob/main/packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts), [reset/resume histories](https://github.com/TanStack/db/blob/main/packages/db-sqlite-persistence-core/tests/sqlite-core-adapter-oracle.test.ts), [setup reopen](https://github.com/TanStack/db/blob/main/packages/db-sqlite-persistence-core/tests/collection-setup-reopen-oracle.test.ts), [reload class](https://github.com/TanStack/db/blob/main/packages/db-sqlite-persistence-core/tests/reload-class-oracle.ts), [dual-adapter resume snapshots](https://github.com/TanStack/db/blob/main/packages/db-sqlite-persistence-core/tests/sqlite-resume-snapshot-oracle.test.ts), [Browser composed-owner histories](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/tests/per-collection-coordinator-oracle.test.ts), [Browser coordinator RPC](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/tests/browser-coordinator-oracle.test.ts) (fixed histories and the source-commit scheduler-order test live in `packages/browser-db-sqlite-persistence/tests/browser-coordinator.test.ts`), [shared-driver fairness](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/tests/shared-driver-fairness-oracle.test.ts), [driver contracts](https://github.com/TanStack/db/blob/main/packages/db-sqlite-persistence-core/tests/contracts/sqlite-driver-contract.ts), [Node shared-handle scheduling](https://github.com/TanStack/db/blob/main/packages/node-db-sqlite-persistence/tests/node-driver.test.ts), [OP-SQLite shared-handle scheduling](https://github.com/TanStack/db/blob/main/packages/react-native-db-sqlite-persistence/tests/op-sqlite-driver-oracle.test.ts), [browser OPFS lifecycle](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/tests/opfs-page-lifecycle-oracle.test.ts), [worker diagnostics](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/tests/opfs-worker-diagnostics-oracle.test.ts), [request order](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/tests/opfs-request-order-oracle.ts), [Electron IPC and composed owner](https://github.com/TanStack/db/blob/main/packages/electron-db-sqlite-persistence/tests/electron-ipc.test.ts), [113-law manifest](https://github.com/TanStack/db/blob/main/packages/db-collection-e2e/src/fixtures/persisted-conformance-manifest.ts), [persisted sync problems](https://github.com/TanStack/db/blob/main/packages/db-sqlite-persistence-core/tests/persisted-sync-problem-oracle.test.ts), [persisted reload replacement](https://github.com/TanStack/db/blob/main/packages/db-sqlite-persistence-core/tests/persisted-reload-replacement-oracle.test.ts) | Core cache/remote rejection/peer/reopen histories, atomic reset/resume lineage, key-set evidence, dual-adapter races, queued coordinator reload/gap notifications during startup, unscheduled startup/reset overlap, and exact driver results. The persisted sync-problem oracle (`packages/db-sqlite-persistence-core/tests/persisted-sync-problem-oracle.test.ts`) owns how the wrapper reports failures of its own remote work and to which sync run: a later successful remote request ends the wrapper's remote failures (maintainer decision: a coarse clear, by any remote request that succeeds, the same one repeated or another; 8 histories failed before it, and a wrapper without the clear fails 7); 35 histories over eight sites (coordinator release, the adapter's own `unloadSubset` throw or reject, scoped refresh ensure and release, rotated reacquisition through a routed or single-process coordinator, and a reload's cleanup release), each crossed with ready or loading and with the current run or a run that `reload()` retired before or after the new run's `markReady()`. A remote failure is a ready Collection's failure reason and never a crash; before the first ready it is only logged; an `unloadSubset` breach surfaces uncaught; a retired run's failures change nothing in the new run and never reject its cleanup. HEAD failed 29 histories; mutants that crash, remove the generation fence, or let a cleanup release reject each fail. Limits: controlled adapter and coordinator doubles; no Browser or Electron follower routing; no coordinator retry queue. The persisted reload-replacement oracle (`packages/db-sqlite-persistence-core/tests/persisted-reload-replacement-oracle.test.ts`) checks over 40 histories that the wrapper forwards `begin({ snapshot })` and `truncate({ markReady })`, that its restore and metadata commits replace nothing and do not complete a reload, and that durable rows equal visible rows after a replacement. HEAD's wrapper failed 15; a dropped snapshot, a non-durable snapshot, dropped truncate options, and a restore marked as a snapshot each fail. Limits: eager Collection only, in-memory adapter. The setup-reopen owner checks that a fresh adapter's first read repairs an interrupted setup, every subset of missing indexes, triggers, and bookkeeping rows, and a schema change to the fresh-install schema with rows kept or reset; complete setup costs at most one driver call over a cached read. It excludes cache-generation claims and concurrent adapters; the held-registration resume cases cover a collected generation at the setup DDL and at the registry read. The reload class owner keeps the reload law, model, histories, driver loop, and comparison in one module, with four production drivers: the Node Browser driver (real Browser persistence and coordinator, one connection per tab), the Node Electron driver (real renderers and main handler with an in-process `ipcMain` double), the real Chromium OPFS page, and a real Electron app (renderer over real IPC to a main process running the Node persistence over `node:sqlite`). Histories cover 1 to 4 eager or managed Collections with any subset of missing derived setup facts, a schema change, a database missing a migrated column or index, lock grants 1 to 5 tasks after the request, statement latency where the host controls it, a local write during another startup read (issued before or after its grant), a write held in flight at another Collection's grant, a follower write, a write from a third tab, takeover (also while a follower read is held after an earlier follower or third-tab write, and while a follower that knows the departed leader's route has a source commit queued behind a held subset read, which it flushes after the grant or which the departed leader received and never answered), restart, and a tab closed while its terms wait. It checks skipped and repaired Collection and database setup, that no term runs ahead of work queued on its tab at its grant unless a leader need is live in that election (a need that an earlier election served exempts none), that needs elect and settle, that a takeover's queued source commit settles within one RPC timeout (one timeout and a retry when the departed leader received it), and an exact per-reload budget: one registry read per adapter, one row scan per tab, two startup snapshots per eager Collection (metadata before the source starts, rows at the baseline hydrate, both required by the Electric resume laws), no rescan after a commit the tab already knows, and no database setup statement when it is complete. The implementation on origin/main fails every fixed history: the original 24 on all four drivers, and the 5 that cover per-election needs on the Node Browser driver. Production on this branch passes all 29 on all four drivers. The Node drivers reject 14 production mutants by assertion, including a lasting need latch, an earlier served need from this tab or another, a need still waiting at the grant that is ignored, and a grant that keeps the departed leader's route. The per-election cells in `browser-coordinator.test.ts` (fixed tests; no oracle owner (tracked in #2264)) also reject those latches and a delayed heartbeat from the departed leader that restores its route during an election. Real Chromium rejects the never-waiting barrier across its histories; the real Electron app rejects it in 10 of the 29 fixed histories; in the others, real IPC finishes the startup reads before any grant. Hold seams patch the adapter in the process that runs it. Not owned here: a barrier that skips queued but not yet running regular work, which driver transaction order and the writer lock hide in reload histories; the shared logical scheduling oracle owns it. The real Electron app uses `node:sqlite` because Electron's ABI cannot load better-sqlite3; a native better-sqlite3 build for Electron is not run. The shared core-adapter contract checks custom local comparator dispatch for ascending, descending, and comparator-equal public-key ordering at `loadSubset` completion. Host packages register this contract, but the local sqlite3 CLI run does not prove native host execution. Browser composes public source commits with per-collection elected-owner routing and covers the complete committed-transaction wire partition through deterministic Node transport seams. Remote-subset histories distinguish logical demand, physical acquisitions, exact acquisition leases, and released replay tombstones. Electron composes source commits with a per-collection renderer owner, IPC adapter, real SQLite, and reopen checks. The shared-driver fairness owner checks K=1 complete-logical-hydrate scheduling with identical fixed, random, and seed-plus-path campaigns. It records public rows, raw dequeue reach, and a persist-first FIFO hostile control. Its Chromium OPFS fixture refines the provider boundary but does not establish a browser matrix, elapsed-time latency, unbounded eventuality, or multi-process coordination. Same-handle Node and OP-SQLite tests cover transaction admission. Controlled OPFS page/worker histories cover ownership and diagnostic-cause retention. The reset/resume owners use sqlite3 CLI and in-memory node:sqlite seams; they do not prove multi-process WAL, mobile/Tauri, or other native-device execution. Distinct database handles rely on SQLite lock admission rather than one in-process queue. React Native hosts without async-context propagation must use the transaction driver supplied to the callback for nested work. Fake workers and synthetic page events do not prove native handle release or real bfcache admission. The Browser composed seams are not real multi-context/OPFS-worker execution; the Electron harness is not an actual Electron process unless its explicit runtime-bridge mode runs. An ownerless elected node suppresses core routing, while a follower may route demand to the elected owner. The follower generation-notice and rotated-reacquisition lanes state the rotation law: a rotated follower holds the new generation's commit notices until the owner has reacquired every active demand, then ends scoped recovery and applies them. Production does not yet end that recovery, so their cells that require it are pinned as failing under the known gap [#2139](https://github.com/TanStack/db/issues/2139); the random generation-notice campaign is omitted until the fix, because some of its histories pass today. Cells that still pass cover failed reacquisitions (scoped recovery stays and the failure is reported), a superseding rotation as leader, and a scoped recovery without rotation, with or without an active demand (untrusted durable rows stay hidden). Other cells cover a follower whose claim expires while its ensure is in flight and an owner reset (the follower requests its demand again). The follower remote-demand lane checks that a follower's load, and a live query over its demand, settle only after the owner's broadcast applies here, for a broadcast that applies at once, one queued behind another subset's hydration, and one while the cache claim is expired; the claim-rotation placement is pinned under #2139. A failed apply rejects the load, errors the query, and fails the source. A renewal that fails transiently at the reply rejects the load but keeps and retries the demand, without releasing the owner's acquisition; a rotation during the reply wait is tracked under #2139. The flush-side expiry check and arrival-order queueing have no killing witness yet. Removing the follower's wait on either ensure path, its terminal check, or the queued flush's terminal marking each fails it. The Electron IPC readiness oracle checks a fresh or reopened renderer, a successful or failed IPC hydration, and an upstream load that succeeds, fails, or is released during hydration, against a model of the three readiness checkpoints; a renderer that replies before the main-process read or treats an IPC failure as empty fails it. The owner is a controlled coordinator double, not two real coordinators; host coordinators retry only classified transport or admission failures while demand remains retained. The manifest excludes progressive and move suites; registration and shim runs are not device execution. The per-collection coordinator oracle's real-tab lane runs a follower and an owner tab with real Browser coordinators over Node's BroadcastChannel and Web Locks and one real SQLite file: 16 histories over the owner's source (custom or Query Collection), the broadcast's placement, the follower's apply outcome, and the consumer, plus a rotation cell. Main fails all 16 histories; an owner tab for a rotated follower generation is not reachable there. The Electron IPC readiness oracle adds a follower and an owner renderer with Electron coordinators: 4 histories over the apply outcome and the consumer, in process and through a real Electron main process; main fails all 4. | +| SQLite expression-index planning | [Node expression-index oracle](https://github.com/TanStack/db/blob/main/packages/node-db-sqlite-persistence/tests/expression-index-oracle.test.ts) | RFC #1659 invariant 8 owns identical persisted-index and runtime-expression shapes. Independent expected keys are checked against direct captured SQL, adapter results, and named-index plans. Generated BigInts use SQLite's signed range; one fixed case checks legacy oversized-value reads. Other limits: bounded unqualified JSON paths/scalars, Node BetterSQLite, and no null, arbitrary raw SQL, or native-host planning. Run the package's `test:oracles` campaign. | +| Offline execution | [scheduler](https://github.com/TanStack/db/blob/main/packages/offline-transactions/tests/KeyScheduler-oracle.property.test.ts), [leadership](https://github.com/TanStack/db/blob/main/packages/offline-transactions/tests/leadership-replay-oracle.property.test.ts), [settlement](https://github.com/TanStack/db/blob/main/packages/offline-transactions/tests/transaction-settlement-oracle.property.test.ts), [serialization](https://github.com/TanStack/db/blob/main/packages/offline-transactions/tests/transaction-serializer-oracle.property.test.ts), [web connectivity replay](https://github.com/TanStack/db/blob/main/packages/offline-transactions/tests/connectivity-replay-oracle.test.ts), [review record](oracle-reviews/pr-1879-visible-replay.md) | Declarative FIFO eligibility, per-transaction outcomes, durable state and typed wire trees. The web connectivity owner checks a persisted write under a false browser online hint, then visible replay by event or explicit retry, at provider-call, outbox, and caller-settlement cuts. It uses controlled browser globals and fake storage; it does not prove actual browser network detection, genuine offline retry timing, multi-tab leadership transfer, or React Native behavior. Issued work may finish after ownership loss, but new work must not start. Exactly-once network execution is not promised. | +| Frameworks | [React conformance](https://github.com/TanStack/db/blob/main/packages/react-db/tests/conformance.test.tsx), [React pagination](https://github.com/TanStack/db/blob/main/packages/react-db/tests/infinite-query-conformance.test.tsx), [shared framework contracts](https://github.com/TanStack/db/tree/main/packages/db/tests/conformance), [Vue synchronous publication](https://github.com/TanStack/db/blob/main/packages/vue-db/tests/useLiveQuery-publication-oracle.test.ts), [Preact conformance](https://github.com/TanStack/db/blob/main/packages/preact-db/tests/conformance.test.ts), [Preact binding witnesses](https://github.com/TanStack/db/blob/main/packages/preact-db/tests/useLiveQuery.test.ts) | Exact exposed rows/pages and each framework's own lifecycle cuts. Vue's synchronous watcher checks insert, update, and nonterminal delete through supplied Collection and identity query inputs; it does not cover an empty final result, multiple changes in one transaction, or query recompilation. A React witness does not prove Preact/Vue/Solid/Angular/Svelte scheduling. Preserve their receiving registrations. The Preact driver observes each result at commit through a layout-effect probe under an error boundary, because Preact runs passive effects after paint and `act` drains only those it scheduled; it has no Suspense, server-snapshot, or DbClient witness. Its binding witnesses check that a write between render and subscription publishes and that an equal `queryKey` or structurally equal query keeps its live-query Collection. Subscribing in a passive effect instead of a layout effect survives both, as allowed scheduling freedom. The query-identity laws (1.0, maintainer decisions 2026-10-22 and 2026-10-23) run through every live-query driver and the React, Preact, Vue, and Svelte infinite drivers, branching on each driver's public `comparesQueryIdentity` policy. In a binding that compares identity (React and Preact), a query without a nonempty `queryKey` whose IR has no stable hash throws `UnhashableQueryIRError` at mount and at recompilation; an empty key counts as none, and a nonempty key runs the query. React no longer accepts dependency arrays. Such a binding claims each source Collection ID it compiled, so A(x) -> C(y) -> A(x) is legal while a later B(x) throws `SourceCollectionIdReuseError`. A pairwise model judges 32 enumerated histories over A(x), B(x), C(y), and a disabled step, with declared reach for a swap after an intervening query, a swap after a disabled step, and a revisit after an intervening query. Reach labels name the steps between the claim and its test, so a direct swap does not count; removing the C(y) step fails reach. Claims that keep only the latest query, or reset at a disabled step, fail it. React retains two shared-suite registrations, but both now call the same derived-identity API; the registration still named `react (dependency list)` does not establish a second production path. The earlier five failures from skipping a dependency-list check at first compile were evidence for the removed API. A nonempty `queryKey` skips the claim: React and Preact witnesses swap a same-ID source under a changed key, and a key reader that ignores keys fails both. One query over two same-ID objects uses overlapping but different rows; an engine mutant that resolves each source through the Collection-ID map, so the last object serves both join sides, fails the live and infinite scenarios. A Svelte hydration witness checks that a `DbClient`-backed query with a dependency list hydrates `queryKey: []` exactly as it hydrates an omitted key. The infinite suite runs the same pairwise grammar over A(x), B(x), and C(y), 18 histories with declared reach for a swap and a revisit after an intervening query; it has no disabled or page-shape step, because one infinite handle changes either its query or its page shape. A new binding starts unclaimed. The former React dependency-list/`DbClient` witness is historical; it does not establish current reuse or hydration behavior. The shared conformance rejection scenarios currently check that, after a rejected rerender, two reads each throw or hold no rows. Reactive drivers accept an unhashable query, as described below. React unmounts a tree whose render throws. Preact keeps such a component mounted, so its driver mounts the hook under an error boundary, which unmounts it; a Preact component with no boundary keeps its last committed rows and is not witnessed here. It guards a future driver whose binding stays mounted; the `DbClient`-backed Svelte binding, which stays mounted, is checked field by field in fixed [descriptor release tests](https://github.com/TanStack/db/blob/main/packages/svelte-db/tests/descriptor-query-release.svelte.test.ts) (no oracle owner; tracked in #2264). A reactive binding (Vue, Solid, Angular, Svelte without a `DbClient`) runs the same histories and must publish the current input's rows: the unhashable query runs and recompiles when a captured value changes, and B(x) shows B's rows, never A's. One query over two same-ID objects is likewise rejected by a comparing binding and read through an inner join by a reactive one. The same mount-and-recompile split is not yet generated for `DbClient`-backed Svelte or React hooks beyond the receiving witnesses below. The [React source ID reuse oracle](https://github.com/TanStack/db/blob/main/packages/react-db/tests/source-id-reuse-oracle.test.tsx) owns the release law for `DbClient` descriptors: a rejected query releases every sync start it deferred. React exercises applicable causes (source-ID collision, unhashable IR, a descriptor factory that throws, a query rejected during compilation, a single-result infinite query, and a render abandoned by a sibling throw or suspension) through live, infinite, and Suspense hooks with derived identity or changed explicit `queryKey`, plus ordinary-hook configurations that disable reclamation, at initial mounts or rerenders and through four readers. Explicit keys bypass source-ID and derived-IR rejection; single-result rejection applies only to infinite hooks, and abandoned-after-hook histories exclude Suspense because its preload already starts the sources. Svelte crosses collision, unhashable, factory, compile, and abandoned-setup causes with eager and lazy sources and three readers, and its collision history reads every public result field separately after the rejection; dropping the guard from one getter, such as `isLoadingError`, fails all six collision cells. The earlier dependency-list Suspense cache-claim failures are historical. Current ordinary [source-ID tests](https://github.com/TanStack/db/blob/main/packages/react-db/tests/source-id-reuse.test.tsx) check separate client-scoped Suspense hooks and an initial suspended retry with different same-ID sources; they use the current derived-identity API. These fixed histories are supplemental tests, not a dimension of the claim grammar: that grammar's disabled step has no Suspense form, because `useLiveSuspenseQuery` rejects a disabled query. React ordinary/infinite hooks and Svelte's compiled query release at their render boundaries. The current React Suspense rejection histories fail within its shared ordinary-hook preparation; removing only the outer Suspense wrapper's catch is an unreached mutant, not a demonstrated kill. The former dependency-array SSR history was removed because its API is absent from the RC; it had executed no cells in the preceding grammar. Routing Svelte's getters around the compiled query fails its collision cells; `preload()` overrides a deferral, so those cells pass without the release by design. Declared reach: each enumerated release history asserts that the rejected query ran the shared descriptor's factory once before the reader, and a history that binds the failing descriptor first fails that check. A render that never commits (React: a sibling throws after the hook returns, or the render suspends; Svelte: setup throws after the hook returns) releases its deferral when garbage collection reclaims the live query it built (maintainer decision 2026-10-23), or after the unsubscribed GC floor when the hook's config disables that reclaim (`gcTime` 0 or `Infinity`, or `startSync: false`); both release grammars add these causes, React adds the three config paths and Svelte a `gcTime: 0` setup, and waiting only for the reclaim fails those cells; removing the reclaim release fails exactly them, and releasing at the end of render instead fails the descriptor binding owner's no-work-before-commit law. Known limit, owner the maintainer: the reclaim follows the 50 ms unsubscribed GC floor, so a render whose commit React holds past that floor releases its deferral, and its source may start, before it commits; that render then subscribes to a cleaned-up live query, which restarts. No history holds a commit past the floor, which needs a controlled React scheduler or fake timers; this joins the existing held-commit open cell in the infinite-query publication row. A keyed step records no claim and checks none (maintainer decision 2026-10-23): for a comparing binding the claim grammar adds keyed steps AK and BK over the same-ID objects, 108 histories with declared reach for a claim that crosses a keyed step and a keyed step after a claim; a keyed step that claims, or that resets earlier claims, fails it for the current React derived-identity path and for Preact. Each controllable driver records a publication log (`publication-log.ts`) at the end of each synchronous turn, plus the value on screen at the first paint after each `setParam`, once the framework has flushed and before any later task (maintainer decision 2026-10-23). After a same-ID swap, a reactive binding's every logged value holds only the new source's rows or none: a binding that keeps the old rows published until a later task fails it in Vue and Svelte. A value replaced within one synchronous turn is not logged, because the browser never paints it; Solid and Svelte publish the previous query's rows once inside the swap's own turn, for any input change, and pass. Accepted design (maintainer decision 2026-10-23): React claims source IDs during render, so a concurrent render that is abandoned before commit keeps its claim, as the React oracle states for a render that suspends, because that render's live query may still own the source until cleanup. Same-ID source objects stay legal; the claim, not `createCollection`, rejects a swap. Trace: committed C(y); a transition render reads B(x) and is interrupted; an urgent render reads A(x) and throws `SourceCollectionIdReuseError`. The abandoned-transition history has no witness. Adapter-specific `queryKey` change semantics stay with each adapter's own tests; React's infinite hook also has a focused witness that a `queryKey` array changed in place recompiles, because only React hands the caller's array to the shared window binding. The shared lifecycle histories distinguish adapter-created projections from caller-owned retention: omitted and explicitly `undefined` `gcTime` use the 1 ms adapter policy; `gcTime: 0`, a positive 100 ms retention, and supplied Collections keep caller authority. They observe retirement after unmount, replacement, disabling, and positive expiry, plus survival before that expiry. The shared suite also checks initial-query readiness over a lagging on-demand source, for a list and a `findOne` query and for the infinite query (React, Preact, Vue, and Svelte): not ready before the subset settles, ready at settlement while the source is still `loading`, and still ready after the source becomes ready. Solid's Suspense over that source has its own witness. A Solid Suspense witness also checks that a source's `reportError()` and `fail()` after readiness keep the boundary's rows shown and the query `ready`; a hook whose status follows the health tree fails it. A resource that suspends again on a tree change is not observable at that cut, because its refetch over a ready query settles before a fallback renders. A separate Solid two-resource Suspense witness holds one ready projection past the 50 ms unsubscribed floor while its sibling remains pending, then checks both commit and boundary abandonment release the temporary preload owner and preserve ordinary 1 ms teardown. | +| Backend Collection E2E | [shared provider suites](https://github.com/TanStack/db/tree/main/packages/db-collection-e2e/src/suites); [Electric registration](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/e2e/electric.e2e.test.ts); [Query registration](https://github.com/TanStack/db/blob/main/packages/query-db-collection/e2e/query.e2e.test.ts); [TrailBase registration](https://github.com/TanStack/db/blob/main/packages/trailbase-db-collection/e2e/trailbase.e2e.test.ts) | Electric, Query, and TrailBase register reusable predicate, pagination, join, and live-update checks at their provider boundaries; Electric also registers the move suite. These are receiving paths for core query and Collection laws, not framework render cuts. | +| Window-controller overlap | [shared infinite-query conformance](https://github.com/TanStack/db/blob/main/packages/db/tests/conformance/infinite-suite-oracle.ts), [React driver](https://github.com/TanStack/db/blob/main/packages/react-db/tests/infinite-query-conformance.test.tsx), [Vue driver](https://github.com/TanStack/db/blob/main/packages/vue-db/tests/infinite-query-conformance.test.ts), [Svelte driver](https://github.com/TanStack/db/blob/main/packages/svelte-db/tests/infinite-query-conformance.svelte.test.ts), [Preact driver](https://github.com/TanStack/db/blob/main/packages/preact-db/tests/infinite-query-conformance.test.ts), [review record](oracle-reviews/dec-03-window-overlap.md) | Four or five ordered source rows, two window-settlement orders, and public snapshots after each settlement, a subscribed observer update, a detached controller read, and explicit recovery. Insertion and removal of the fifth row distinguish both continuation directions while an earlier error remains visible. The detached read follows a source change with no controller subscriber; its preloaded Collection retains the five-row window. The exact overlap uses the exported DB controller in each package realm. Framework hooks expose no controller preload, so this cell does not establish a hook scheduling path; direct hook paging remains in the adjacent shared scenarios. An unsubscribed controller has no notification claim. | +| Infinite-query publication | [shared publication laws](https://github.com/TanStack/db/blob/main/packages/db/tests/conformance/infinite-suite-oracle.ts), [React render cuts](https://github.com/TanStack/db/blob/main/packages/react-db/tests/infinite-query-render-cuts-oracle.test.tsx), [Svelte hook tests](https://github.com/TanStack/db/blob/main/packages/svelte-db/tests/useLiveInfiniteQuery.svelte.test.ts), [review record](oracle-reviews/2026-10-05-infinite-first-paint.md) | Each driver records every value its framework published: React layout commits, Vue pre-flushes, and the Svelte construction value plus pre-effect runs. The shared suite checks that a synchronous source shows its ready first page on the first value, for a query callback and for a supplied collection that has not started, that every ready value in a fixed-source, fixed-query scenario equals the source prefix for its own page count, including an equal dependency replacement that keeps page depth, and that a mount requests an on-demand first window once, for a query callback and for a supplied collection whose finite window is wider than or equal to the one the hook needs, judged by the rows the on-demand source holds after mount. Under deferred acquisition, an on-demand first value is either the ready first page or a value with no rows, by framework: React and Svelte publish before the hook subscribes, Vue after. `preloaded-collection-first-paint` checks the documented route-loader pattern: a limited live query preloaded before mount shows its ready first page on the first value in every framework and asks the source for nothing more; a preload that does not count as demand never settles there. Pre-commit work has no shared form, because Vue setup is its commit. The React driver owns renders that never commit, StrictMode double renders (one first-window request on React 19), retries after GC, a mounted replacement whose startup error must reach the error boundary, and a collection kept from an abandoned render that enters terminal error before its query is selected again, each as under `useLiveQuery`. The React source ID reuse oracle drives the infinite hook too. Svelte owns construction and superseded recomputes before the subscribing effect. In both, the hook acquires an on-demand source as often as `useLiveQuery` does under the same history, for either input form, direct or behind a live-query Collection. On-demand requests from renders that never commit are now owned by the live-query deferred acquisition oracle; the React render-cut oracle checks that an abandoned render makes no acquisition. Open cells, owned here: React 18 StrictMode, where neither hook keeps refs across the double render; a React commit held after the 50 ms unsubscribed GC floor, where a controlled-premise controller witness shows no non-ready value but no React scheduling witness exists; ready values in scenarios that write the source or replace the query, which need a per-step source model; a join of eager and on-demand sources, which needs a zero-acquisition check before commit; a supplied collection with no `.limit`, which sends one unbounded request on `main` too, because the controller cannot narrow it before the source request; and Svelte `state_unsafe_mutation` when a start inside `$derived.by` synchronously writes rows a peer is waiting for, shared with Svelte's `useLiveQuery`, which a two-component probe reproduces and the deferred-acquisition follow-up removes. | +| Structural values and ordered primitives | [hash values](https://github.com/TanStack/db/blob/main/packages/db-ivm/tests/hash-oracle.property.test.ts), [hash identity](https://github.com/TanStack/db/blob/main/packages/db-ivm/tests/hash-identity-oracle.property.test.ts), [MultiSet consolidation](https://github.com/TanStack/db/blob/main/packages/db-ivm/tests/multiset-consolidate-oracle.property.test.ts), [hash graphs](https://github.com/TanStack/db/blob/main/packages/db-ivm/tests/hash-graph-oracle.property.test.ts), [mixed hash graphs](https://github.com/TanStack/db/blob/main/packages/db-ivm/tests/hash-mixed-graph-oracle.property.test.ts), [hash retry](https://github.com/TanStack/db/blob/main/packages/db-ivm/tests/hash-failure-retry-oracle.property.test.ts), [murmur stream](https://github.com/TanStack/db/blob/main/packages/db-ivm/tests/murmur-stream-oracle.property.test.ts), [comparison](https://github.com/TanStack/db/blob/main/packages/db/tests/comparison-oracle.property.test.ts), [deep equality](https://github.com/TanStack/db/blob/main/packages/db/tests/utils-oracle.property.test.ts), [cursor](https://github.com/TanStack/db/blob/main/packages/db/tests/cursor-oracle.property.test.ts), [indexes](https://github.com/TanStack/db/blob/main/packages/db/tests/index-update-oracle.property.test.ts), [BTree Map](https://github.com/TanStack/db/blob/main/packages/db/tests/btree-map-oracle.test.ts), [query identity](https://github.com/TanStack/db/blob/main/packages/db/tests/query/identity-output-shape-oracle.test.ts), [LIKE semantics](https://github.com/TanStack/db/blob/main/packages/db/tests/query/compiler/evaluators-oracle.test.ts) | Independent flat values, graph topology, algebraic laws, structural object keys in code-unit order (the flat-value, identity and consolidation grammars include keys that tie under locale collation), murmur byte framing against a byte-at-a-time reference (block mixing, the finalizer and symbol framing have no owner there), Map/group/sort recomputation, expression denotation, custom comparator dispatch/reference identity/index compatibility, LIKE wildcard refinement, compiled output bags, declared value-pair agreement between D2 equality keys and IR equality operands, and Collection collation with exact public scan/index order and one-index reuse across repeated reads. The auto-index cells cross BasicIndex and BTreeIndex; scan cells omit indexes. Both paths cover lexical and numeric `en-US` locale defaults plus an explicit ordinary locale override of a numeric default. An unindexed six-row work cell bounds Collection collation resolution to at most two reads per ordered scan: one index-path check and one scan clause. Other locales and ordered histories are outside this owner. The identity pair grammar covers primitives, signed zero, NaN/invalid Date, valid Date/timestamp, binary/Buffer, Temporal, nested references, symbols, and functions; fixed and sampled pairs do not prove every JavaScript value or hash collision freedom. Ref proxies are expressions rather than literal values in this lane. The deep-equality owner also pins that `deepEquals` ignores Map and Set insertion order, RegExp `lastIndex`, array holes, and typed-array class, the rules draft equality keeps. It also pins that objects of another class differ, that plain and null-prototype objects are one class, that a class instance without enumerable keys equals only itself, that URLs compare by `href`, and that typed-array elements compare like numbers. The hash-values owner checks equal-hash consequences for independently constructed flat values. Distinct type identities are checked by the hash-identity owner through `equalHashValues`, including forced digest collisions; distinct 32-bit digests are not promised. The hash-identity owner checks `hash` and `equalHashValues` against one spec-level identity model: primitives with signed zero, NaN, and bigint; reference leaves (functions, registered handles, `File`, binary over 128 bytes); Date, binary, Temporal, and RegExp headers; array length and holes; Map and Set insertion order; and object keys in any order, for acyclic values and for cycles through arrays, Map values, Sets, and plain objects. Map/Set order sensitivity is pinned current behavior, not a promise. Arrays from another realm, RegExps with a `NaN` `lastIndex`, getters, proxies, other cycle carriers, and pairs that differ only in a back-edge target are outside its grammar; pinned cases keep equality's cross-realm length check and strict RegExp field comparison. `hash-work.test.ts` pins how visited values and array/RegExp headers count toward the structural work cap, and that equality copies a shared Map's entries once per distinct pair ([code-weight hash dispatch review](oracle-reviews/code-weight-hash-dispatch.md)). Custom string indexes do not optimize ordinary ranges, and custom cursors remain unsupported. The LIKE owner covers boolean string matching and bounded work, not nullish three-valued logic or a general Unicode collation contract. Unsupported composite cursors reject. The MultiSet consolidation owner checks keyed, single-type, and structural identity, signed zero and NaN in keyed and single-number data, the retained first record, zero-sum removal, and input record contents. It does not assert output order. Its keyed grammar includes cross-type key and value collisions, non-finite numeric keys, the `\|` delimiter, symbol and function identity, and a direct-object/object-tuple delimiter control from #1948. Eight named collision witnesses and both generated campaigns are RED on `c879ba6d` and GREEN with the repair ([#1948 review record](oracle-reviews/issue-1948-keyed-consolidation.md)); the [code-weight consolidation review](oracle-reviews/code-weight-multiset-consolidation.md) records the earlier exclusion. The keyed reference uses direct value/reference equality, including SameValueZero for numeric keys. The unkeyed fallback keeps its original key and value domains. The direct `MultiSet` owner does not prove which `@tanstack/db` query shapes produce primitive keyed values or mixed-type source keys; that needs a live-query production witness. The index owner enforces one invalid-comparator law across both BasicIndex and BTreeIndex: for each comparator, accepted numeric prefix (including the empty prefix), and probe operation (add, update, eq/range lookup, take, build), the operation throws exactly when it receives a `NaN` or non-number result and never when every comparison is valid. A `signed infinity` comparator is the valid control, and a custom collation `compare` supplied through `compareOptions` is checked the same way. Two BTree Map split histories check inserted-key reads, exact whole-range payloads/order, size, and extrema, proving split placement follows the insertion index without a post-mutation comparison. A rejected index add or remove leaves the index refining its accepted rows; update and build are not atomic. At the Collection boundary, both index types and both write paths (optimistic insert, sync commit) crash the collection: status becomes `error` and the next mutation throws, so a usable collection never holds rows its subscribers were not told about. Open: a sync source can still commit writes on a collection in `error` state, which stores unpublished rows; this is existing `markError` behavior and needs a lifecycle-owner witness. Comparator transitivity is outside this evidence. | +| Mixed-kind ordered prefixes | [mixed-kind public-order oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/query/mixed-index-order-oracle.test.ts) | A separate unindexed ready Collection computes the public ordered two-row prefix for the same keyed rows as an eager BasicIndex or BTreeIndex Collection. Five histories cross number/bigint, boolean/number, object/string, and array/object values, with insert, update, and delete transitions into and out of mixed domains. Ascending and descending reads use one ascending physical index, so descending checks exercise ReverseIndex. After preload and after both source copies commit each step, exact direct and live-query IDs must match the scan. Same-kind checkpoints verify that safe indexes still serve ordered reads. The original type-name index order failed eight ascending public-prefix checks. A second numeric source Map models a finite on-demand provider: a bigint enters and leaves the visible prefix, deletion requires an uninstalled row, and an offset window reaches a cursor after the retained BTree index loses ordered-plan eligibility. Public IDs match the independent provider order at each settled window; controls withholding either requested row fail those assertions. This owner does not define a global precedence among unlike values, cover in-place value mutation or arbitrary mixed-kind pairs, or prove that every retained on-demand traversal is safe after its source domain changes. | +| WHERE predicate publication | [WHERE predicate publication oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/query/where-predicate-publication-oracle.property.test.ts), [`in` evaluator](https://github.com/TanStack/db/blob/main/packages/db/tests/query/compiler/in-evaluator-oracle.property.test.ts) ([review](oracle-reviews/perf-in-constant-set.md)) | An independent Kleene evaluator judges `eq`/`not`/`and`/`or` predicates over strings, a normalization-prefixed string, booleans, numbers, `NaN`, a valid Date, `null`, a missing field, and virtual fields. Snapshot histories with pending optimistic inserts compare a live query, direct subscribers with and without initial state, and `currentStateAsChanges`; change histories apply multi-key sync transactions, including reinsertion of a key deleted earlier, and compare every consumer's key set after each commit, on scan and `BasicIndex` paths. Peer subscribers share one field with different literals, plus one on a virtual field, so every published change must reach exactly the subscribers whose predicate it can satisfy; routing mutants that ignored the previous value, ignored the field, delivered a change twice, or routed while stale published rows awaited reconciliation fail here. Fixed witnesses cover a layout-only publication reaching a filtered subscriber as one empty batch, a filtered subscriber's empty Collection-readiness batch, retraction of a vanished row after eager cleanup and restart, and the new row plus exact subscriber update payload when a same-key row stays TRUE. A stale-live-value mutant survives the generated membership checks but fails the payload witness. A focused [property-visibility test](https://github.com/TanStack/db/blob/main/packages/db/tests/query/where-prefilter-property-visibility.test.ts) compares the public unindexed snapshot with the enriched-row contract for inherited, non-enumerable, enumerable-own, and nested getter paths; the pre-fix stored-row shortcut failed three of its four controls. Other property-descriptor and stateful-getter histories remain outside those fixed controls. Hostile mutants for FALSE-for-UNKNOWN `eq`, `or` or number-literal subscription prefilters, a prefilter that ignores the previous value, a skipped readiness batch, a skip while stale published rows await reconciliation, and an unindexed snapshot scan that reads only synced rows while an optimistic insert or delete changes visibility passed the prior `@tanstack/db` suite and fail here. Change routing withholds a dropped row from an `eq` subscription before its sent key could be recorded, so a mutant that records dropped rows as sent is equivalent for routed predicates in this oracle. A focused `collection-subscription.test.ts` witness checks that a dropped insert or update, alone or beside a matching change, cannot advance a limited subscription's page offset under a routed `eq` and an unrouted `or` predicate; the unrouted cases beside a matching change kill that mutant, and the unrouted `alone` case does not. A loose-equality prefilter is an equivalent mutant: it only skips less. Skipping during truncate replay also survives; the replay's baseline diff re-derives the same retraction, so the guard keeps the prior dataflow without its own witness. Generated cleanup and restart histories for filtered subscribers remain open for the lifecycle publication owner. No-op updates, other comparison operators, Temporal and binary operands, joins, ordering, optimistic updates, and truncate are outside this owner. It runs in `@tanstack/db`'s `test:oracles` campaign. The [2026-09-30 review](https://github.com/TanStack/db/blob/main/docs/contributing/oracle-reviews/2026-09-30-where-predicate-and-join-keys.md) records each ORC outcome. Change routing was later removed in favor of pooled live queries; the routing mutants above are historical, and the same peer laws now check plain subscription dispatch. The unindexed snapshot prefilter was restored for compiled queries as one stored-row `eq` test on a string or boolean literal. Its property-visibility test also covers the reverse direction: a stored row with an inherited or non-enumerable field must still match `isUndefined` on that field, which a scan that evaluated the full predicate on stored rows fails. The `in` evaluator owner checks `in` against an independent linear reference over Dates, `NaN`, `-0`, BigInt, bytes, Temporal values, objects and nullish items, for constant and row-supplied lists. | +| Joined result keys | [Joined result key oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/query/join-result-key-oracle.property.test.ts) | A nested-loop model recomputes the pair set of an inner, left, or full join over source keys drawn from plain, comma-bearing, bracket-bearing, and quoted strings, numbers beside the strings that print the same, both infinities, and `NaN`. After preload and each synced group change, the published rows must equal the model's pairs and the result key count must equal the row count. The comma-joined key encoding fails its two pinned histories and both campaigns; plain `JSON.stringify`, which prints infinities as the missing-side `null`, fails the pinned infinity history and both campaigns. A pinned history in which a `NaN`-keyed pair leaves and re-forms fails when the join index compares source-key prefixes with `===`. Joins over subqueries, more than two sources, custom `getKey`, and optimistic mutations are outside this owner. It runs in `@tanstack/db`'s `test:oracles` campaign. The [2026-09-30 review](https://github.com/TanStack/db/blob/main/docs/contributing/oracle-reviews/2026-09-30-where-predicate-and-join-keys.md) records each ORC outcome. | +| D2 Index storage | [Index refinement oracle](https://github.com/TanStack/db/blob/main/packages/db-ivm/tests/index-refinement-oracle.property.test.ts) | A plain-`Map` model sums multiplicities under a type-and-value identity in which `NaN` equals `NaN` and `-0` equals `0`. Histories of up to twelve additions to two keys mix prefixed arrays with `NaN`, `0`, `-0`, `1`, `'1'`, `1n`, and `'a'` prefixes and unprefixed values, with cancellation and reappearance. After each addition, `get` and `has` must equal the model. Each step also checks that `get` returns what `getIterator` yields, in its order, since the two walk the storage separately. Comparing prefixes with `===`, which disagrees with the `Map` that holds them, fails three pinned histories and both campaigns; the `-0` control passes under both. Every history also runs with `prefixIdentity`, where values under one key with the same prefix are one value; mutants that merge unprefixed values or ignore the option in a prefix map fail. The [structural identity oracle](https://github.com/TanStack/db/blob/main/packages/db-ivm/tests/structural-identity-oracle.property.test.ts) checks that `structuralIdentity` agrees with D2 value identity for equal representations (array subclasses, class instances, null prototypes, key order, non-enumerable symbols) and independent values, including separator-bearing strings and keys, enumerable symbol keys, Dates, RegExps with numeric `lastIndex`, Maps and Sets with zero to two entries (the second Set member is a fresh tagged array, so primitive deduplication and unwrapped reordering are outside this lane), arrays with an optional enumerable `note` field, binary values, holes, deep nesting, and a cyclic value; an independent normal-form model decides equality and is checked against `equalHashValues`. The model expands `deep` shorthand into nested arrays. A pinned equal `deep`/array pair and adjacent-depth control guard the model boundary exposed by seed 1158727847/path 255. The equal-pair grammar crosses same-spec values with explicit-array/`deep` equivalent specs; the independent-pair grammar uses two specs. Both run fixed and fresh campaigns with guarded one-case seed/path replay. Pinned same-description symbols and equal-body Map/Set/object pairs expose collisions that random independent pairs rarely reach. Mutants that stringify symbols, collapse those three object-kind tags, drop a string's or key's length or an array's length, or omit Date and RegExp headers fail it. A first-entry-only Map/Set mutant and an array-identity mutant that ignores enumerable non-index fields fail the pinned comparisons; lowering the depth cap is equivalent, because the cap only bounds work. Open: reference-valued RegExp `lastIndex` can give D2-unequal values one defined identity and merge `groupBy` contributions ([#2192](https://github.com/TanStack/db/issues/2192)); the structural-identity and incrementalization owners need a production repair and receiving witness. Temporal values, other opaque reference values, large binary values, getters/proxies, and mutation between observations are outside this generated grammar. groupBy's use of it is owned by the incrementalization law: its string-extrema lane fails a string identity without a length prefix, and a focused groupBy witness fails when the group key is left out of the identity. Compaction, presence tracking, and structural payloads are outside this owner; a focused test checks that an index built without presence (as reduce builds its indexes) throws when presence is read; the [reduce fast-path oracle](https://github.com/TanStack/db/blob/main/packages/db-ivm/tests/operators/reduce-fast-path-oracle.property.test.ts) compares the fast path's emitted entries one by one with the general path's, over keys that move between the paths, hash-merged outputs, -0, and zero and negative multiplicities, while an unchanged `NaN` output is owned by a focused reduce test; the join operator tests and incrementalization law own operator behavior. | +| Pooled live queries | [pooled live query oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/query/pooled-live-query-oracle.property.test.ts) | An independent `eq` model (nullish UNKNOWN, Date as timestamp, `NaN` equal to `NaN`, `-0` equal to `0`) gives membership; a live-query Collection compiled for the same query gives order, values, and status. Histories mix sync transactions, optimistic writes confirmed or rolled back, peer mounts and unmounts, and source cleanup and restart, including cleanup while a write is pending and a mount between cleanup and restart. Wholesale observers must match after every step, granular changes must match a reference observer by type, key, value, and previous value and replay to the model's rows, the layout revision must advance when keys reorder, and observing must never build the live-query Collection. Peers may add a residual `not(eq(...))` conjunct, which the model evaluates itself; a view that ignores it, reports an unfiltered `entries()`, or delivers a row entering or leaving it as an update fails it. Other residual operators use the same compiler evaluator but are not generated. Peers may also order their rows by `id` or by a `g` that can be null, with explicit `nulls`; order comes from the reference. A comparator that ignores direction or `nulls`, an order left out of the partition key, and key order instead of the query's order each fail the pinned reorder history and both campaigns. A partition that ignores a row's previous group, orders by arrival, skips normalization, skips the initial snapshot, follows the source's status after cleanup, or never terminates fails it. Two shared conformance scenarios (`eq-filter-rows`, `eq-filter-peers`) run the pooled path under React, Vue, Solid, Svelte, and Angular; a partition that ignores a row's previous group fails both under every adapter, and `eq-filter-peers` checks through `subscriberCount` that each adapter shares one source subscription per partition while mounted and releases every source subscription after the final framework unmount. The [resource-lifetime oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/query/pooled-live-query-gc-oracle.test.ts) compares when a pooled query and a live-query Collection release their sources across `gcTime` 1, 100, 0, and `Infinity`, with and without a subscriber, and with no `gcTime`, where both use the live-query Collection default, checks that a partition waits for its views' longest `gcTime`, checks that a view resubscribed after its partition released follows later source writes as its live-query Collection does, including a view with a residual conjunct, and checks that a partition that subscribes again still serves new mounts from one source subscription. A generated cleanup-admission lane (`pooled-live-query.cleanup-admission`) crosses 2–3 views of one partition, observed or not, read by a subscribed live query or not, and view cleanups with or without `dependents`; it checks the model's rejection rule and rows and status against live-query Collections. A view that gives up its partition share when its cleanup rejects fails it. A view whose own cleanup fulfilled must show `cleaned-up` with no rows (law 14, maintainer decision), checked against the model and the live-query Collection; a view that keeps its source's status or its group's rows fails it. Further focused witnesses check that a partition keeps groups only for values that have rows or watchers, that a detached reader never sees a dropped group's revision reused, keep a pooled query's public Collection live while the view is observed, accept it as a query source, and check that a pooled query turns terminal when source cleanup starts while the adapter's cleanup is still pending, as a live-query Collection does. A detached wholesale reader of every view, kept after its peer unmounts, must match the live-query Collection at every checkpoint, and generated histories cross the dependents cascade with readers on watched and unwatched groups that hold rows (declared reach); a cleanup that empties groups without advancing their revisions, or advances only watched groups, fails it. Every view and its live-query Collection also carry an app status listener that throws; each listener gets its own call and the throw is reported uncaught, so a view that lets a listener's throw stop the others or reject the source's cleanup fails it. Generated callback-boundary histories also compare ordinary compiled and pooled core observers across return/throw, Error/undefined, zero through three healthy peers, callback position before/after peers, and `gcTime` 1–3. Failed granular bootstrap retains no unreturned ownership after grace; a held cascade cleanup (`cleanup({ dependents: true })`) gives every admitted wholesale observer one terminal `cleaned-up` snapshot/callback despite a sibling throw, and an ended partition stops following its source's health, and preserves the exact failure at settlement. Original production and independent rollback-only or all-peer-only repairs fail the respective ownership or cleanup-start comparisons. These controlled core receiving paths do not establish framework schedules, arbitrary callback programs or provider shutdown. A pinned oracle witness checks that an `eq` path whose getter throws excludes the row on both paths; a pooled path that rethrows fails it. The pooled oracle's grammar never releases a partition, so release and resubscribe histories rely on these fixed witnesses; a fixed delay, a missing unsubscribed floor, a last-`gcTime`-wins rule, and releasing at `gcTime` 0 each fail it. Removing the partition's final-listener release survives the mounted row checks but fails the shared framework unmount checkpoint and core release-timing owner. The [2026-10-01 review](oracle-reviews/2026-10-01-pooled-live-queries.md) records each ORC outcome. The shared adapter conformance suite checks the opt-in boundary in every framework: queries without `pool` keep a source subscription each in the function, config, and parameterized forms, and a pooled parameterized query follows its parameter between pool groups beside an unchanged peer. Pooling every query by default, a pooled identity that ignores literal values (React then reuses the old query), and an Angular `params` form that drops `pool` each fail it. `pooled-param-change` also reads each driver's publication log: every value published after a switch holds only the new group's rows. Each driver also logs its keyed `state` map at the same points, and the law applies to it. A pooled view that serves the previous view's rows until a later task replaces them passes every final-state check and fails only the log, in all six drivers. A view that sends the previous rows as its first change set fails the `state` log in Vue; Solid rebuilds its map from a snapshot within the same turn, and Angular builds its map from `entries()`, so that mutant cannot reach them. The `invalid-pool` scenario requires `InvalidPoolOptionError` for `true` and for `false` (the value adapters pass to say a hook cannot pool), alone, beside `gcTime`, beside `queryKey`, on a parameterized query, and on a disabled config or parameterized query, in all six drivers; a check in only one of the three readers of `pool` fails it in four of them, and Angular's `params` form failed the `false` and disabled cases until it checked its pool first. The shared `grouping-operator-switch` scenario re-renders a grouped query without `groupKeys()`, and `compile-error-after-rerender` re-renders to a select that mixes a source field with an aggregate; both require the error and no rows after it. A binding that keeps its last compiled query when compilation throws fails both in Solid, Svelte, and Angular; one that throws once and then serves that query fails the no-rows check in Solid and Svelte, and cannot occur in Angular, whose `computed` rethrows a cached error until an input changes. In `grouping-operator-switch`, React and Preact kept the grouped query before query identity held operator code, and Vue served the previous rows before its compile error stopped reaching readers through a throwing computed (also on `main` at `5649a07d4`). | +| Live-query deferred acquisition | [deferred acquisition oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/live-query-deferred-acquisition-oracle.test.ts), [React Suspense warm-cache witness](https://github.com/TanStack/db/blob/main/packages/react-db/tests/suspense-warm-cache.test.tsx) | An independent model of the law that a live-query Collection starts no provider work before a subscriber or a preload in its current sync run crosses seven source states (eager or on-demand, idle, already started or render-deferred, and an on-demand source that already holds the rows), an unordered query and an ordered window, one or two live-query levels, and eleven histories: start and read, subscribe or preload after a start, subscribe or preload without one, deferred subscription and resumption with or without a start, cleanup before a subscriber, peer start, source truncate, and restart after a run that had one. After every command it checks source sync-run starts, acquisition attempts, readiness when the call returns (the synchronous observation cut), settled readiness, and rows. A pooled block checks that building a pooled view starts no idle eager source until a view subscribes or preloads, and eight DbClient histories check that a preload answered by a pending or settled stream still resumes acquisition across eager/on-demand idle and render-deferred sources. The React streaming witness receives the on-demand render-deferral premise, starts local acquisition before the stream settles, and retains server rows until browser acquisition completes. Mutants that never defer, keep the subscriber-or-preload fact across cleanup, ignore preload, never resume a builder's subscriptions, let a deferring subscriber start a base source, build a pooled partition without deferral, never resume it, mark a preload one microtask late, or drop the stream's preload mark each fail it. The React witness checks that `useLiveSuspenseQuery` over a source that already holds the rows renders without the fallback, for eager and on-demand sources and both shapes; creating the Suspense collection started, or not preloading it in render, fails it. A later review added `peer` and `truncate` commands: a peer that starts an idle on-demand source, or a truncate on a running one, made the live query acquire before any subscriber, and both guards are killed separately. Pinned blocks check that a subscriber surviving cleanup is served after a restart while a surviving deferring subscriber is not, and that `toArrayWhenReady()` and `stateWhenReady()` count as a preload. A pinned block checks that a deferring live query is not counted in its source's `subscriberCount` or `subscribers:change` until its first subscriber, while still keeping the source from garbage collection; a Query Collection test checks that it causes no refetch. Another pinned block checks that a preload or subscriber requested by an inner live query's status handler, while the outer query creates its subscription to it, still resumes acquisition. Solid's Suspense receiving witnesses check that a resource whose preload settles first retains its live-query Collection while its sibling remains pending past the unsubscribed floor, then releases that temporary owner on both commit and abandonment, and that a preload settling after its owner unmounts cannot publish an error through the abandoned result. Open: source failure and asynchronous loads before resumption, which the subscription lifecycle oracle owns once acquisition resumes; a resume that throws, which leaves later sources unstarted only for a live query that is already in error; asynchronous sources; and arbitrary Suspense schedules with a `DbClient` beyond the bounded abandoned-render and streaming handoffs recorded above. | +| Live-query notification algebra | [real-query and checker calibration](https://github.com/TanStack/db/blob/main/packages/db/tests/notification-laws-oracle.test.ts), [independent Map checker](https://github.com/TanStack/db/blob/main/packages/db-collection-e2e/src/utils/notification-laws-oracle.ts) | One-key synchronous source histories check exact live-query callback reconstruction and unsubscribe silence. Synthetic multi-key histories calibrate the checker; direct source Collection events remain with the change-event history owner. | +| Flat-row change tracking | [flat change tracking oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/flat-change-tracking-oracle.property.test.ts) | Generated callbacks over flat rows, frozen or not and with or without a non-enumerable field (assignments of `0`, `-0`, `NaN`, `undefined`, functions, and fresh objects; reverts; deletions; added fields; data and accessor `defineProperty`; stored drafts; throws), run through the flat tracker, the draft proxy, and an independent model, which must report the same change sets with `0` and `-0` equal, or throw the same error and leave the rows unchanged. Defining a field acts as assigning it, and a non-enumerable field is row data only once written; the proxy broke three of those laws on `main` and the flat tracker one. Diffs using `!==`, `Object.is` alone, or no deletions fail it. It found that the proxy dropped a field added as `undefined` when another field reverted; the pinned history and both campaigns fail without that fix. Non-flat rows must fall back. The [2026-10-01 review](oracle-reviews/2026-10-01-pooled-live-queries.md) records each ORC outcome. | +| Virtual props cache | [cache laws](https://github.com/TanStack/db/blob/main/packages/db/tests/virtual-props-cache.test.ts) | Focused laws for the per-key cache of rows read with virtual props: a change's published value is the row later `get` and `toArray` reads return, for every subscriber, after sync and local writes, and a key that is deleted or rolled back leaves the cache. Enriching the value before the previous value fails the first two; skipping the drop on delete events fails the third. Rows read with virtual props through other paths, such as joins, are outside these laws. | +| LocalStorage Collection | [mutation order](../../packages/db/tests/local-storage-order-oracle.test.ts), [peer snapshots and ownership](../../packages/db/tests/local-storage-peer-history.test.ts) (fixed tests; no oracle owner (tracked in #2264)) | Finite two-write histories compare authored same/disjoint-key updates, both handler completion orders, and all four application-decision pairs against public rows, durable rows, caller settlement, and fresh restore. A nested fire-and-forget admission checks that the outer write reaches storage first; awaiting the nested receipt inside its earlier handler is an excluded dependency cycle. Handler-free direct insert, update, and delete are checked at the method-return boundary for synchronous Storage effects. A later handler failure is checked while its earlier handler remains held: its receipt rejects and its optimistic row rolls back promptly, while the durable predecessor remains pending. Fixed tests in `packages/db/tests/local-storage-order.test.ts` (no oracle owner (tracked in #2264)) cover the next two histories. An automatic insert followed by manual acceptance crosses same-key update versus disjoint insert and both earlier-handler decisions; manual settlement waits behind that handler, and public, durable, and fresh-restore rows match the authored fold. Un-awaited manual acceptance crosses direct and held-predecessor paths with success or Storage failure: both commit and persistence receipts wait for every registered write, report the exact fault on failure, and agree with durable and public rows at settlement. A two-Collection witness checks that one manual receipt waits for both un-awaited acceptances. Peer witnesses check serialized disjoint typed-key writes before delayed event delivery, equal-ID manual Collection ownership, construction-time rejection of direct or shallow-copy reuse before or after first preload with the first Collection still writable, a peer insert during pending local insert, listener release across cleanup/restart, malformed startup snapshots, transient startup read failures and restart, the null-versus-empty storage sentinel, local and peer clear publication with subsequent public/durable agreement and a pending optimistic mutation across clear, and storage/parser read failures during writes and event delivery. Reload witnesses let another tab (a second Storage object over the same bytes) update or delete a kept row and insert a row, or clear storage, while no event reaches the first tab, with and without a prior local edit of the kept row: `reload()` must resolve with the Collection `ready` and its public rows equal to the durable rows and a fresh restore, and the reloaded run must confirm a later write. The adapter that re-inserted stored rows in each run crashed on the update histories and kept the deleted row; a snapshot only for non-empty storage fails the clear histories. The [same-tab audit](oracle-reviews/2026-10-08-localstorage-same-tab.md) records witnesses that withhold events through each persistence receipt: disjoint typed keys, a manually accepted same-key update followed by automatic update and delete, clear, a rejected Storage write, a pending same-key optimistic insert, cleanup/restart, and a write started by a ready-status listener compare public snapshots with the authored durable fold. A default jsdom Storage witness receives a same-tab write. The old event-only adapter fails seven no-event checkpoints. The original adapter also fails the second-handler-first, delayed-peer, equal-ID, listener-release, read-failure, synchronous direct-write, prompt-failure, mixed manual/automatic order, un-awaited manual receipt, reused-options admission, malformed-startup, and clear-publication witnesses. A failed write read rejects without replacing durable rows; a failed event read retains the public snapshot until a later valid event. These checks do not establish simultaneous cross-tab read-modify-write atomicity, atomic acceptance across distinct storage keys, manual acceptance without a mutation Collection owner, arbitrary long histories, native browser scheduling, or synchronization across distinct custom Storage wrapper objects. Existing LocalStorage failure and CRUD tests remain supporting examples. | +| LocalStorage value fidelity | [JSON value model](../../packages/db/tests/local-storage-value-fidelity-oracle.property.test.ts) | With the default JSON parser, an insert, update, manual insert, or manual update is accepted only if a fresh restore can return the row. A recursive value grammar labels each generated value, while it is built, with its expected JSON form or as lost. Lost: functions, symbols, enumerable symbol keys, `NaN` and infinities (also boxed), `undefined` and missing array entries, arrays with extra or symbol properties, a `toJSON` that returns `undefined` in an array, and Map, Set, WeakMap, Promise, RegExp, Uint8Array, and SharedArrayBuffer values. Normalized: `toJSON` values (Date, a plain object, and a function with its own `toJSON`), `undefined` properties and `toJSON` results, `-0`, boxed finite numbers, and non-enumerable symbol keys. Histories cross the four entry paths with and without an automatic handler. At `when('settled')` settlement, an accepted row must keep the authored writer value, store the model's form durably, and restore it in a fresh Collection; a lost row must reject with `SerializationError`, leave the writer's predecessor and storage bytes unchanged, and never call the handler. Pinned witnesses cover every lost kind and normalization on each path, with and without a handler; a fixed-seed and a random campaign of 300 histories run under `local-storage.value-fidelity`. A pinned witness per entry path uses a `toJSON` that changes after its first call: storage must hold the JSON the check accepted, and serializing the row again during the key check or the snapshot write each fails it. The stringify-only check on `main` fails 162 of 230 cells. The first walker in this PR failed 66 cells of the expanded grammar. Mutants that reject `undefined` properties, accept `undefined` array entries, skip boxed-number unwrapping, treat non-enumerable symbol keys as lost, skip the array-property check, or shrink the opaque-type list each fail at least six cells. Open: custom parsers, including a wrapper around `JSON` (accepted by design, documented); class instances (accepted by design; the guide states they restore as plain objects); cross-realm natives; getters; non-enumerable string data; update payloads whose shape the draft copy changes (array extra properties and symbol keys are dropped, and a hidden symbol key becomes enumerable, so an update of an object with a hidden symbol key rejects although its insert succeeds), which the proxy detachment owner must decide; peer or event delivery of a rejected write, which never reaches storage; and peer delivery of normalized values: the peer owner checks only `Date` at that boundary, so a peer receiving witness for the generated value grammar remains with the peer owner. IndexedDB clone rejection is owned by the IndexedDB settlement and values oracles, which require `SerializationError`; local-only rows keep values by reference, pinned in `local-only.test.ts`. | +| Local-only direct writes | [direct write witnesses](https://github.com/TanStack/db/blob/main/packages/db/tests/local-only-direct-write-oracle.test.ts) | Focused witnesses pin when a local-only direct write skips the optimistic stage: a completed transaction and one publication per write, and the fallbacks for a pending, persisting, or ambient transaction and a user handler, for insert, update, and delete. Each history also runs on a local-only Collection whose handlers resolve, which must match its rows, change batches, errors, and transaction states, across mixed multi-key batches, a batch that fails midway, schema rejection, and handler rollback. Mutants that drop each guard, write only the first mutation, or never complete the transaction fail them. The change-event history oracle drives the direct path through generated histories (about two thousand writes per run); removing the guard, or guarding only persisting transactions, fails these witnesses. | +| Reused sync row objects | [reused row witness](https://github.com/TanStack/db/blob/main/packages/db/tests/sync-reused-row.test.ts) | Focused witnesses for a sync source that changes a stored row object in place: in development, writing it again as an update without `previousValue` throws, while the same write with `previousValue`, or with a new object, moves the row out of an `eq` live query. Production skips the check. The check compares a shallow snapshot taken when the object was written, so it misses changes to nested fields; a write that names `previousValue` and a row whose fields throw on read are not checked. | +| Index suggestions | [index suggestion oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/index-suggestion-oracle.test.ts) | A public filtered live-query Collection checks manual/default, eager, threshold, matching-index, and unrelated-index cases against the documented collection-size suggestion policy. A bounded ordered-query decision table separately classifies no field index, a null-placement mismatch, matching but order-ineligible indexes, and usable candidates in either registration order. It compares the development warning after preload; the matching-index selection owner below checks the chosen reader and ordered request. Two cyclic locale-options cells require the diagnostic to preserve valid query construction with or without a field index, and a custom-collation cell requires remediation to preserve comparator reference identity. The original manual-silence/eager-noise implementation failed two expected observations. The issue #2190 baseline failed four diagnostic cells, including a first-unusable/second-usable candidate order; the first formatter draft failed all three non-JSON-safe remediation cells. The owner does not judge repeated-query warning volume, slow-query timing, production-build suppression, every comparison-option mismatch, or every query shape. It runs in `@tanstack/db`'s `test:oracles` campaign. | +| Minified DB public API | [consumer bundle check](https://github.com/TanStack/db/blob/main/scripts/test-minified-db.mjs) | CI builds `@tanstack/db`, then bundles its built `dist` entry with db-ivm inlined through esbuild `minify: true`. The build renames TypeScript-private members from `packages/db/mangle-cache.json`, so this lane runs against the renamed output that consumers install. It rejects a `dist` that is older than `src`, the cache, or the package's build configuration. `pnpm --filter @tanstack/db test:dist` also runs five db test files against the built `dist`, chosen for coverage of the modules with the most renamed members. `pnpm check:mangle` fails when a cached name is used other than as a private member in db src, is read by another package, appears as a string, or has a short name that collides with a source identifier ([private-member mangling review](oracle-reviews/code-weight-private-member-mangling.md)). It checks every exported error class's current public `name`, the built-in `BasicIndex` resolver name in index metadata and its event, complete query rows (after removing the four documented virtual fields) against an independent array filter/sort/projection, and one live update. The `new.target.name`, public-member mangle, and extra-output-field calibration modes fail at the intended observations. This fixed slice does not replace the unminified generated oracles, exercise framework adapters, or establish stable names for custom index resolvers. | +| Boundary refinements | [cleanup/restart](https://github.com/TanStack/db/blob/main/packages/db/tests/collection-cleanup-restart-oracle.test.ts), [idle cleanup](https://github.com/TanStack/db/blob/main/packages/db/tests/collection-idle-cleanup.test.ts), [issue #1891 async-cleanup review](oracle-reviews/issue-1891-async-cleanup.md), [issue #1891 cleanup-start follow-up](oracle-reviews/issue-1891-cleanup-start-followup.md), [metadata publication](https://github.com/TanStack/db/blob/main/packages/db/tests/collection-metadata-publication-oracle.property.test.ts), [row metadata composition](https://github.com/TanStack/db/blob/main/packages/db/tests/collection-row-metadata-composition-oracle.test.ts), [nested-begin metadata rebuild review](oracle-reviews/2026-10-06-nested-begin-metadata-rebuild.md), [state mutation round 4](oracle-reviews/2026-10-06-state-mutation-round-4.md), [state retention](https://github.com/TanStack/db/blob/main/packages/db/tests/collection-state-retention-oracle.property.test.ts), [acquisition cells](https://github.com/TanStack/db/blob/main/packages/db/tests/collection-subscription-lifecycle-cells.test.ts) (fixed tests; no oracle owner (tracked in #2264)), [D2 source reconciliation](https://github.com/TanStack/db/blob/main/packages/db/tests/d2-source-reconciliation-oracle.property.test.ts), [top-K support windows](https://github.com/TanStack/db/blob/main/packages/db-ivm/tests/operators/topk-support-window-oracle.test.ts), [nested Query work](https://github.com/TanStack/db/blob/main/packages/query-db-collection/tests/includes-work-counter-oracle.test.ts) | Explicit lifecycle products, independent source maps and weighted relations, exact publication cuts, support/multiplicity, and value-plus-work observations. These refine the larger subsystem models; they do not replace them. The cleanup/restart owner's failing-dependents product crosses every ordered pair of resolving, throwing, rejecting, held, and pooled-throwing dependent ends with synchronous, held, and rejecting adapter cleanup and both release orders, with declared reach; it rejects a cleanup that stops at a throwing dependent, settles with `Promise.all`, or waits only for its dependents. Effects and nested live-query chains with failing ends are not generated. | +| Small structures and test mechanics | [SortedMap](https://github.com/TanStack/db/blob/main/packages/db/tests/SortedMap-oracle.test.ts), [cleanup queue](https://github.com/TanStack/db/blob/main/packages/db/tests/cleanup-queue-oracle.property.test.ts), [live-query GC clock](https://github.com/TanStack/db/blob/main/packages/db/tests/collection-gc-clock.test.ts), [guarded replay](https://github.com/TanStack/db/blob/main/packages/db/tests/oracle-replay.test.ts) | Map/full-sort and elapsed appointment-list models with executed target/seed/path checks. SortedMap promises what Collection state uses, not full Map parity (maintainer decision). Keys are strings or numbers: `sync.write` and mutations throw `InvalidKeyError` or `UndefinedKeyError` for any other key, and grouped live queries key nullish, boolean, and bigint group values with strings (maintainer decision, 2026-10-27). The model is a Map of numbers plus a full sort; production stores row objects so the driver can change one in place. Keys include `NaN`, which a grouped live query uses for a `NaN` group; it is one key that key order places after every other number. Generated histories mix deferred and immediate sets and deletes, clear, deferred bursts of 17 to 41 keys, prunes that delete entries with deferOrder while iterating, and in-place changes of a deferred or a placed row. A prune must yield the order it began with. A row changed before restore sorts by its current value; once a placed row changes in place, comparator order downgrades to membership (each key once, current value) for the rest of the history, and a later write or delete affects only its own key. At each ordered read with a comparator, restoring k deferred keys among n others makes at most k * (ceil(log2 k) + ceil(log2 (n + 1)) + 2) comparator calls, and none with nothing deferred. Declared reach (minimum 5, per campaign of 100): burst restorations, an entry yielded after a prune deleted one, a placed `NaN` key written without deferral (7 to 16), a restore whose bound is below n (19 to 30), a changed placed row later written (11 to 20), and a deferred row changed before restore (11 to 19). Mutants that re-sort every key (108 calls against a bound of 7), re-sort the kept keys with the dirty keys appended (34 against 7), re-sort clean scans, drop the scan fallback for a changed row, match slots by `===` (a placed `NaN` key then removes the last key), keep dirty slots, apply deferred deletes immediately, or place a deferred row by its value at the write each fail. Supplemental examples in `SortedMap.test.ts` pin splice movement at scale, a key iterator opened before a deferred restore, scan reuse, and public single-transaction and queued-transaction Collection sync paths. Not promised: other mutation during iteration (immediate writes, clear), the order of rows once a placed row changed in place, keys outside the key type, `-0` keys, a comparator that throws, and the work of key order without a comparator, which the public API cannot observe. `forEach` was removed; SortedMap is not exported. Wall-clock steps do not move due times, and a focused public witness checks source subscription release. Callback-reentrant scheduling, OS suspend/resume, environments without `performance.now()`, and fake/real Performance clock replacement while GC is pending remain outside this coverage. Tests must drain or reset pending GC before switching timer providers. | + +The index-suggestion oracle's PR #2196 extension checks a matching local +custom-collation index against the full-source request and retained warning, +an ordinary unindexed on-demand prefix against its bounded request, virtual +field advice, and direction-free comparison guidance. It observes first +acquisitions and warning text, not later window moves or real-provider cost. + +## Unit-test retirement transfers in PR #2116 + +The [direct index refinement oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/index-update-oracle.property.test.ts) +owns the seven behaviors moved from index unit tests: an absent lower bound +with an exclusive flag; exact membership through comparator-equal updates; +`null` and `undefined` exact and `IN` membership, including repeated owners; +undefined endpoint and cursor traversal; and explicit, omitted, and +double-undefined range bounds. A source Map predicts exact key sets and +ordered reads after bounded direct actions on BasicIndex and BTreeIndex. A +full-build witness catches native `Array.sort` moving raw `undefined` last. +The [round 1](oracle-reviews/index-unit-retirement-round-1.md), +[round 2](oracle-reviews/index-unit-retirement-round-2.md), +[round 3](oracle-reviews/index-unit-retirement-round-3.md), and +[round 4](oracle-reviews/index-unit-retirement-round-4.md) ledgers retain each +retired assertion. The [build review](oracle-reviews/basic-index-nullish-build.md) +records the direct RED/GREEN repair. A separate mixed number/string lane +checks that full direct traversals and one indexed, bounded public Collection +snapshot return each live source key once after deletion and after a direct +delete/update/re-add history. Two more direct histories keep an ordinary string +beside ordered ZonedDateTime or Duration keys and verify key conservation after +one Temporal deletion. An en-US locale and a custom string-collation cell compare +the first bounded public row through a scan and each index when an ordinary +string begins the internal encoding prefix. An `origin/main` probe failed the +default-locale public prefix case. The pre-classifier PR comparator failed all +four public prefix cases and both BTree Temporal-deletion histories. This owner +also compares complete forward and reverse reads through `IndexInterface`, +without prescribing index buckets or a BTree representative. A fixed churn +witness holds one live row while 16, 64, or 256 earlier distinct values are +added and removed; its public `takeFromStart(1)` result and comparator-call +growth reject retained empty buckets that add comparator work. This is a bounded +read-work check, not a memory bound or a general complexity proof. A deliberate +empty-bucket mutant failed at the intended comparator-count comparison. +The invalid-comparator owner checks rejection exactly when a comparison +returns a non-number or `NaN` and checks rejected removal atomicity; it does +not require a comparison merely because one comparator-equal row is removed. +The binary lane checks fresh equal-content removal, re-addition, and +replacement beside another byte value. It compares complete forward and +reverse key reads to a source Map. Separate short and 8193-byte pairs check +exact membership and complete forward and reverse key conservation when only +the last byte differs. An intermediate PR BTree rewrite lost a surviving key +after one equal-content owner left; the expanded lane rejected that result. +A separate two-value Date, Temporal.Instant, and Uint8Array callback lane +checks legacy scalar input kinds on direct add only. Arbitrary callbacks and +update, build, and range paths are outside that lane. +This owner does not prescribe mixed-kind order or cover binary range ordering, +all JavaScript value kinds, or general Collection publication histories. + +The [comparison oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/comparison-oracle.property.test.ts) +owns the transferred same-realm NaN and invalid-Date default-order cases, +plus ordinary PlainDate calendar and ZonedDateTime instant order. Its +independent supplied values predict direct comparator signs and ties. The +[round 5](oracle-reviews/comparison-unit-retirement-round-5.md) and +[round 6](oracle-reviews/comparison-unit-retirement-round-6.md) ledgers name +the retired examples. Mixed-kind order, cross-realm values, modified built-ins, +and public indexed reads are outside this transfer. + +The [deep-equality oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/utils-oracle.property.test.ts) +owns the transferred same-realm Date and RegExp equality boundaries: equal +copies, changed values, and inequality with timestamp, ISO, or source text. +The [round 7 ledger](oracle-reviews/utils-unit-retirement-round-7.md) retains +the retired examples. Caller-modified built-ins and Collection publication +are outside this direct-return owner. + +The [Duration query equality oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/duration-query-equality-oracle.test.ts) +uses ten public fields to predict direct BasicIndex/BTreeIndex exact lookups and +public `eq` snapshots before and after indexing. The `in` evaluator owner also +checks same-display Duration forms through constant and row-supplied lists. +The deep-equality and Collection change-event history owners check a Duration +field replacement and its subscriber mirror. These fixed histories do not +establish arbitrary multirow Duration updates or live-query publication. + +The [Temporal index order tests](https://github.com/TanStack/db/blob/main/packages/db/tests/temporal-index-order.test.ts) +(fixed tests; no oracle owner (tracked in #2264)) covers ZonedDateTime instant ordering and time-only Duration ordering for direct +BasicIndex/BTreeIndex reads and bounded public first-row reads. It separates +exact Duration fields from order ties, including exclusive cursor behavior. +An extended-year PlainDate lane checks calendar order under lexical string +collation through ready scan and direct indexed snapshots, with eager live-query +preload as parity evidence. The old indexed direct path chose the wrong year. +An exception lane requires ready scan, BasicIndex, and BTreeIndex direct reads +and eager live-query preloads to reject mixed PlainDate/PlainDateTime values and +distinct PlainMonthDays, as changelog #1519 specifies. The old indexed direct +path returned a row instead. Calendar-unit Duration order, other extended-year +histories, on-demand exception publication, arbitrary updates, and custom index +callbacks remain outside this owner. + +The [custom-index selection oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/query/custom-index-order-selection-oracle.test.ts) +checks that a reverse manual index cannot change an ascending limited public +snapshot or live-query result. Eager and prepopulated on-demand sources with +three distinct numeric ranks and a two-row window compare public IDs after +preload. The reviewer withdrew a claimed wrong live-query result after fixing +its scratch test's cross-checkout `@tanstack/db-ivm` resolution. The witness +makes no claim about per-row custom callback order inside a comparator tie. A +second on-demand witness registers an order-ineligible custom index before an +eligible index on the same field and comparison options. It requires a bounded +ordered request, a read through the eligible index, and no read through the +ineligible index. The first-matching-index mutant fails at that reader check. +Provider paging from an incomplete on-demand source and later updates remain +outside this witness. + +The PR #2196 follow-up adds a direct `optimizedOnly` snapshot to the +first-ineligible/second-eligible history. An eager-auto history starts with +only an ineligible custom index and requires an eligible ordered reader after +query compilation. These checks cover selection and creation at the initial +read checkpoint; later index registration, mixed value domains, and provider +paging from an incomplete source remain outside them. +The existing mixed-kind public-order oracle checks the neighboring dynamic +domain: a structurally sound index can temporarily lose ordered-read +eligibility as row kinds change, without warranting a duplicate auto-index. + +The [PR #2116 confirmed-fixes review](oracle-reviews/pr-2116-confirmed-fixes.md) +records baseline failures, hostile controls, and the bounded scope of these +repairs. The [deferred findings record](oracle-reviews/pr-2116-deferred-findings.md) +tracks remaining design and coverage questions, including valid Temporal kinds +without public order and mixed-kind query order. Earlier PR-branch +counterexamples do not by themselves establish an `origin/main` failure. + +The Query DB ownership oracle also owns the explicit mutation-handler refresh +contract ([evidence](oracle-reviews/pr-2171-explicit-query-refresh.md)). Its 18-history table crosses insert, update, and delete with no +confirmation, explicit refetch, and direct writes, with and without discarded +legacy return values. Each history requires optimistic and settled reach. At transaction settlement it +checks exact Query fetch counts and public rows: no confirmation exposes the +initial synced rows; either confirmation path exposes the server result. The +old automatic-refetch implementation fails all nine cases at the fetch-count +comparison (including a duplicate request after explicit refetch). This table +uses a real QueryClient and Collection with controlled backend rows; polling, +focus, reconnect, and external invalidation are outside its grammar. + +The [RC review coverage matrix](oracle-reviews/2026-10-28-pr-2171-review-coverage-matrix.md) +records a missing documentation receiving witness for this owner at +`858b174a6`. Executing the actual successful POST handler from +`docs/guides/error-handling.md` leaves one server row and no Collection row at +transaction settlement: the snippet omits explicit confirmation. The adapter +correctly follows the law above; the authored guidance is wrong. O21/O17 in the +challenge report assign extraction of real snippets and their confirmation +histories to this owner. The guide and durable receiving witness remain open; +the temporary probe is not a new oracle extension. + +The Query DB ownership oracle owns the direct-write no-automatic-refetch law +([review](oracle-reviews/issue-1935-direct-write-refetch.md)). +The Query DB `optimistic-ordered-refetch-oracle.test.ts` receiver runs a real +QueryClient and on-demand Query Collection. A held optimistic inbox-to-archive +update must publish installed `[2]` immediately; its mutation handler changes +the provider row and calls `utils.refetch()`, after which queued application +publishes `[2,1]` to both rows and subscribers. It observes a useful bounded +ordered request while held and rejects the old loader classification at the +immediate cut. The separate +`invalidateLoadedSubsets()` trigger belongs to the Query DB ordered-prefix +owner introduced by open PR #2154 and still needs a during-hold observation. +The load-lifecycle owner checks the bounded static-key full-source reuse law +through live-query subscriptions: a settled full request serves a later +filtered demand with no second provider call; after the full view releases, +invalidation still updates the filtered view; an overlapping exact refetch +keeps its own rows and owner; and final release removes them. A pending full +request does not establish filtered coverage; the filtered demand may wait for +its application or request its exact scope. The settled-full witness +fails on the original implementation at the provider-call checkpoint. +It checks that the initial full acquisition signal may abort while a later +shared full fetch receives a live signal. +Another held-fetch cut releases the final filtered owner during full Query +revalidation, then checks transport abort and no late row publication. +Query-cache success reentry checks that initial and replacement full results +cannot settle a filtered acquisition before Collection application; the +replacement cut rejects a gate based only on previous full-row ownership. An +applied direct cache patch permits reuse or one exact filtered revalidation +for a new demand, but cannot trigger a redundant full request. The bounded +history has no retry, invalidation, or explicit refetch; a duplicate filtered +provider-call mutant fails its full request-sequence assertion. Signal +witnesses cover providers that ignore +their signal and may finish into Query cache after final unload for full-source +and equal filtered sharing; equal full-source and filtered peers after the +first releases; and two Collections sharing a Query whose later fetch runs the +released Collection's queryFn. Held initial and refetch histories acquire an +equal filtered peer before or after fetch start, then release the first owner. +They distinguish exact and custom abort reasons, a fresh provider AbortError, +no remaining peer, and an unrelated ordinary error at provider-call, +acquisition-settlement, public-row, cache-row, and error checkpoints. A +recovery-specific ignored-signal witness rejects a guard that consumes Query +Core's lazy signal before the replacement provider runs. The foreign closure +witnesses verify the installed Query Core's option-selection path both before +and during a shared fetch. A synchronous provider throw caused by reentrant +peer acquisition has no separate witness. +The ownership owner seeds a persisted `until-revalidated` subset owner, then +applies a complete full-source result and acquires that subset. An omitted, +unchanged, or moved source row must match public and durable rows at settlement, +and the subset's retention marker must clear. A moved row must retain its full +Query owner after its old subset owner retires. Reusing the full Query while +leaving the subset owner unvalidated fails the row and marker observations; +unconditional row deletion fails the moved-row control. Arbitrary predicate +containment, function-built Query keys, TTL retention, native SQLite replay, +and other persisted startup histories with a shared full-source lease still +need distinct witnesses. +These tests do not prove every provider returns a complete full source, +arbitrary observer-option selection, or the policy for a new equal demand after +failed revalidation. A fresh AbortError caused by the request signal and an +unrelated fresh AbortError after that signal abort are indistinguishable to the +adapter; the one-retry policy favors a surviving peer and may retry the latter. +Real network providers and cross-Collection option selection beyond the +installed Query Core version need receiving witnesses. The load-lifecycle owner +also needs an external `queryClient.cancelQueries` history with a pending +on-demand acquisition and a surviving peer; compare the public acquisition +promise and status at cancellation and after release. A late-peer release +history also needs a public live-query preload checkpoint for the retired +creator, separate from the surviving peer's load. The raw internal +`_sync.loadSubset` creator promise can fulfill in the current fixture; its +settlement policy is unresolved at that boundary. Neither observation alone +establishes a public cancellation bug. The load-lifecycle owner also needs a +cleanup-fault calibration that preserves a primary row mismatch and reports +secondary cleanup failure separately. +Its real QueryClient witness loads ten disjoint on-demand subsets, then checks +`writeUpsert`, `writeUpdate`, `writeDelete`, `writeInsert`, and a mixed +`writeBatch`. After each accepted write it compares public live-query rows with +an independent category filter and checks that no provider request started. +The pre-repair implementation failed this request check with ten requests after +one upsert. A held `deferDataRefresh` barrier adds a request checkpoint after +release; the pre-review implementation made an unwanted request there. A +derived-`select` witness checks that a direct write cannot replace an `edges` +array with node objects. It permits a stale cache row while requiring a valid +response envelope and the accepted public row. A direct-insert/unload witness +keeps a manually written row after its queried peer retires; assigning every +insert to the active subset fails that public-row checkpoint. Existing sibling +cache witnesses reject retaining inactive entries under the Collection's base +query-key prefix, including entries seeded before sync starts. The load-lifecycle oracle +separately owns explicit refetch settlement; the ownership oracle checks that +an explicit refetch after a direct write starts a post-write request when the +initial fetch is still pending. A direct write alone starts no request. These bounded +witnesses do not establish exact Query cache membership, ordering, or window +replacement after a direct write. The ownership oracle owns future scoped-cache +witnesses; the cursor-pagination oracle owns windowed receiving cases. The +offline-transactions owner still needs a full reconnect-replay witness for its +real `deferDataRefresh` premise. The ownership oracle also owns a later-remount +witness after an inactive prefetched entry is evicted, and direct-insert +retention across persisted restore remains with its persisted-owner histories. + +The ownership oracle also checks per-key precedence when a held on-demand +refetch meets a later direct update, error and invalidation/freshness metadata +across cache patches, one patch per physical Query under a colliding hash, and +owner-bounded key extraction for ten disjoint 1,000-row subsets. An eager +derived-`select` remount witness rejects replaying its stale envelope over a +direct write; a foreign-observer witness permits an old inactive cache value +while requiring the public row to survive remount and release of another owner. +These controlled Query Core histories do not establish arbitrary `select` +inverses, exact scoped membership or window replacement, every custom hash +collision, or unbounded performance. The Query DB ownership oracle owns +further direct-write cache histories; cursor pagination owns windowed receiving +behavior. +A caller using direct writes must accept the +scoped-cache limits or request an explicit refetch. + +The ownership oracle also checks five later boundaries from PR #2065 review: +a derived `select` still applies a held fetch's rows and settles eager +readiness after a direct insert; overlapping explicit refetch callers follow +the replacement request after a silent cancellation; a fetched result queued +behind a persisted-baseline scan cannot overwrite a later accepted write; a +zero-lease subset observer retained by a listener receives a direct cache +patch before reacquisition; and eight retired on-demand subsets leave no +fetch-start positions (removing the retirement call fails at eight retained +entries). The Query error, invalidation, and staleness checks now cover both +eager and on-demand modes. These are controlled Query Core and persistence +seams. They do not establish every derived response shape, arbitrary +overlapping refetch schedule, native SQLite timing for the queued-result cut, +or unbounded cache-memory growth. This owner retains those boundaries; the +persisted direct-write oracle owns native durable-order receiving cases. +The retained-Query witness holds an empty-cache fetch across subset unload and +reacquisition. A direct delete then requires an explicit refetch to start a +post-write request and preserve the deleted public row at settlement; without +a later write, the same refetch may reuse the in-flight request. A separate +checkpoint keeps the detached fetch-start position until that request settles +and retires it afterward. Two further shared-Query histories add A's sync +restart after a direct insert/delete while B's pre-write request remains in +flight. Without an explicit refetch, the old result must preserve the delete; +with one, A must start a post-write request. Both were RED before physical +fetch-start positions survived the restart and are GREEN afterward. These +controlled histories do not establish every persisted adapter or Query +cache-removal schedule. +The [intermediate review at 74e399150](oracle-reviews/pr-2065-74e399-review.md) +adds ordinary-unload and unobserved-fetch histories to that request-order law. +The [final direct-write authority review](oracle-reviews/2026-10-12-query-direct-write-authority.md) +records the later law decisions, RED/GREEN evidence, and ten-item audit. +The [four-review follow-up](oracle-reviews/2026-10-12-query-authority-oracle-followup.md) +audits legal subset predicates, cache-projection boundaries, scoped omissions, +and the generated cursor manual-write campaign against its repaired commit. +The ownership oracle also checks derived-select reacquisition and eager restart, +explicit subset refetch, a derived result held behind `deferDataRefresh`, a +provisional cache row awaiting persisted application, aliased physical Queries, +and the boundary between an in-flight pending Query and an idle empty cache +entry. The cursor-pagination integration oracle supplies the idle-entry control. +Its shared-Query reset history holds B's acquisition after A's direct cache +patch, resets the physical Query so the next fetch repeats the patch's numeric +update count, and requires B's load to settle on the new provider row. It runs +with a new response array and with the patch's exact array reference. This +checks the authority boundary at fetch settlement, but does not model every +Query Core reset schedule or custom-hash collision. +An outside-prefix custom-hash collision remains in Query Core's cache after a +direct write. The disabled reacquisition witness requires the load to wait for +an explicit provider result, then checks that result and ownerless-row +retirement. It covers one physical alias and an unbounded scope, not arbitrary +hash functions or windowed scopes. +The ordinary-unload, provisional-row, and alias tests reject their respective +hostile classifiers at public-row or cache checkpoints. These finite histories +do not establish every persistence schedule, every Query hash collision, or a +full offline replay; those remain owned by the Query DB ownership and offline +receiving oracles. Query Core 5.90.15 is the lowest version on which the full +Query DB suite passed; 5.90.14 failed error-recovery witnesses, and 5.85.2 +failed the post-write refetch witness. Older versions are excluded by the peer +minimum. + +The ownership oracle now checks a direct row's ownership lifetime across one +and two subset claims, with and without Collection row metadata. A direct +update preserves those claims; a direct delete ends them; a later insert or +upsert of the same key starts unowned; a later Query result can claim the new +row. Its 20 bounded histories compare accepted public rows and exact persisted +owner identities after each action and subset release. Removing the delete-retirement repair +fails nine histories at those checkpoints. One real SQLite receiving history +releases both former subsets, restarts the Collection from the same storage, +and keeps the reinserted unowned row. A controlled persisted snapshot restart +also retires two overlapping claims one at a time and compares durable owner +identities and public rows after each empty result. This does not establish every persisted +adapter, overlapping result schedule, or ownership of an inferred window +replacement; the persisted direct-write owner retains the wider durable +receiving boundary. Shared-Query cached-result authority +across an unobserved Collection is now an accepted law: a newer physical fetch +may replace and claim a direct row on later acquisition, while an older cached +result or another Collection's local cache edit may not. The 24-history matrix +crosses fetch/write order, a later same-key or unrelated write, a local cache +edit, and another real refetch. It compares public rows at acquisition and +subset release and checks that acquisition did not start an extra request. The +pre-repair implementation failed the post-write and unrelated-write orders; +removing fetched-data provenance fails two local-cache-edit histories. Two real +SQLite receiving histories restart A as a fresh Collection instance while B +retains the shared Query, with and without a later local cache edit. An +ownerless stored row colliding with the cached result has unknown order, so +acquisition refetches once and the new server row wins. Without that guarded +revalidation the original receiving history kept the direct value. These +finite histories do not establish every persisted adapter, a cached result +without a colliding key, or a cross-process Query cache hydration order. + +Further controlled histories compare an accepted direct insert with a cached +server row across cleanup and restart, so a discarded run's write position +cannot suppress that row. The insert/delete neighbor distinguishes a stale +position from intentional row deletion. An equivalent-predicate alias history +coalesces two logical demands into one physical Query: a held pre-write fetch +must preserve the accepted direct row, while a later fetch can replace it. A +shared-Query history also checks A's direct insert while only B has loaded the +Query; B's public row and cache stay on the server value until a newer fetch. +The latter history rejects a cache patch that depends on A already owning a +subset. A stable empty persisted result avoids repeated full-store scans on +retained reacquisition, while a cold run still scans and retires a newly +seeded durable owner. A nonempty provisional result still takes the baseline +scan before applying. These checks use real Query Core observers and controlled +persistence; they do not cover every custom hash collision or external durable +writer. The overlap matrix below covers two requests and one shared row. + +The shared-Query authority histories now also vary peer acquisition timing: +both Collections loaded at the write, B loaded while A is not, and B acquiring +after A patched the cache. The latter rejects a local edit leaking into a +late peer at a held-request public-row checkpoint, then admits the later +server result. A real SQLite restart matrix distinguishes a persisted +ownerless insert from an owned direct update against a shared pre-write cache +row. It compares durable storage after cleanup and the fresh Collection's row +after acquisition; an owned row cannot silently revert to the cached older +value. Two alias schedules separately reject omitting the observer-attach +fetch position and omitting propagation of a later physical fetch position to +all logical observers. A directly selected response property checks the same +late-peer rule as a raw array. A failed replacement fetch reaches the peer's +public error state without publishing the other Collection's local edit. An +outside Query observer retains A's own cache patch through cleanup; A's next +sync run must request a new result rather than replay that old patch. A +separate owned-row history checks that `queryClient.setQueryData` can update +B but cannot replace A's later accepted direct value; a real post-write fetch +may replace it. A held-fetch history checks the local edit before that provider +result, and a sole-owner unload/reacquire history requires a new result before +the manually edited cache can establish rows again. Eager shared-Query histories +require compatible Collections to project the same representable cache patch. +They cross active co-owners, a new direct key, a later or disabled peer, a cold +cache seed, an older held fetch, and a later complete result. They compare A +and B's public rows and the shared cache at write and fetch-settlement cuts. A +held B application checks that disjoint writes alter only their own cache +keys while preserving the peer's edit, for raw rows and a directly selected +`{ items }` envelope whose unrelated metadata must survive. An eager writer's +representable edit also reaches an already-acquired on-demand peer sharing its +exact Query, while an unacquired peer waits for a server result. An eager peer +with a derived visibility filter projects a direct-property writer's cache edit. +A disabled eager peer projects a cached edit without a request; a disabled +on-demand peer waits for an explicit refetch. A held persisted direct commit +crossed with a refetch start compares public and durable rows before the provider +responds, then after a newer response; eager and on-demand modes both reject +replay of the old cached success. + +A complete matching on-demand refetch removes an omitted ownerless direct row, +whether the result is empty or nonempty. A real SQLite restart witness confirms +that the complete filtered fetch ran, retired the matching durable row, and +preserved a disjoint durable sibling. Unload alone, disjoint, limited, offset, +cursor, or failed results leave the row intact. Empty disjoint, offset, and +cursor results and matching/disjoint compound predicates check the completeness +and membership boundary at public-row settlement. + +A seven-history overlap model crosses prior ownership, both request settlement +orders, equal-value responses, and direct writes between or after request +starts. Its provider rows satisfy real `IR.PropRef` predicates; a fixture guard +rejects literal-field comparisons that would make these histories illegal. It +compares the public row after each result and after releasing both owners in +either order. The older response may add an owner without replacing the +later-started fetch's value while row ownership remains continuous. After full +release, a receiving witness reacquires each retained Query with infinite +`staleTime` and `gcTime`: each cache entry projects its own value without a new +request. A complete response that omits an ownerless direct row settles that +row for its matching scope; it does not tombstone older responses from other +Query entries. A cross-scope omission-ordering guarantee would need a separate +law and model. The original implementation fails three reverse-settlement +histories. A separate real Query Core history writes inside an observer's +success notification; a late on-demand peer must not mistake that cache edit +for the fetch action's payload. The original implementation fails at the peer's +held-request checkpoint. These finite histories leave same-run external durable +writers, larger overlapping-fetch schedules, and direct-patch provenance after +Query cache dehydration outside their proven scope. + +A fixed accepted-ownership history acquires three exact subset claims for one +row, then accepts two complete empty results while an unrelated optimistic +mutation delays publication. Its independent model stores the latest complete +result per active Query and exposes their union: the third result keeps the row +public until its own release, after which the union is empty. The ordered +refetch owner's held-delete replay is the killing witness for trusting stale +published owners over accepted ownership. A release/reacquisition history also +holds publication, reacquires the same cached Query, then proves the older +release cannot erase the new claim by releasing it and observing an empty +public row set. That history uses infinite `staleTime`, so reacquisition starts +no fetch. A sibling table crosses a stale reacquisition whose refetch fails, +returns the same rows, or returns no rows; the refetch settles while publication +is held or after the older release publishes; zero to two peer leases own the +first row until after the refetch; and the reacquired result has one or two +rows. Its independent model keeps each active observer lease's latest +successful result and compares public rows while held, after publication, +after the refetch, after every peer's release, and after the reacquired +release. On `main` at `31dad2dd3` 20 of its 42 cells fail: the older release's +empty ownership entry made the cached result look already applied, so the +reacquired lease owned nothing. A row only it owned disappeared at +publication; a row a peer also owned disappeared when the last peer left. The repair marks a released lease's entry and +lets only an applied entry satisfy the already-applied guard and the on-demand +direct-write skip. The empty-result cells reject keeping the row for every +reacquired demand. Removing the guard fails the persisted direct-write +histories that keep an accepted write through a refetch start, and their +sibling with an empty current result and an inserted row rejects treating +every empty entry as unapplied: in on-demand mode the insert disappears at its +receipt, and in eager mode Manual survives the Newer result. A second table +rejects deleting the entry at release: lease `b` claims the row while `a`'s +release is held or after it publishes, with or without a peer `c`, and a +server DbClient's dehydrated owners are hydrated into a fresh client. Deleting +the entry copies `a` back from unpublished metadata, which adds an owner edge +and, without a peer, leaks the row after the hydrated client releases `b`. Its `reacquired` histories reacquire `a` without +cached data and fail its fetch before `b` claims the row; deleting the entry +at reacquisition copies `a` back the same way. A +reacquisition behind a refresh barrier (`deferDataRefresh`) followed by a +direct upsert requires the reacquired Query's cache to show the upsert; reading +the released entry as applied leaves the cache stale, and only the Query cache +shows it. A release whose durable commit fails leaves the row public and +stored, puts the Collection in `error`, rejects a later acquisition with the +persistence error, and a `cleanup()` and `preload()` restart starts from clean +ownership; a host that swallows the failure fails it. A sibling holds the release's +durable commit, reacquires, and fails the reacquired application's commit: the +row leaves with the durable release, the Collection enters `error`, and a +restart serves and releases the row. Eager mode cannot reach +the reacquisition premise: it has no subset release, and ending an eager sync +run removes its Query from the cache. A cleanup that skips entries without an +observer passes this owner but fails the explicit-cleanup retention test in +`tests/query.test.ts`. A receiving probe on the Node SQLite adapter does not +match the controlled failed-release premise: when a SQLite trigger aborts +the release's delete, the Collection enters `error` and storage keeps the row, +but the public Collection drops it. The SQLite persistence owner has no +witness for that divergence ([#2210](https://github.com/TanStack/db/issues/2210)). Persisted retention (`persistedGcTime`) is +outside these histories. The +[review record](oracle-reviews/2026-10-20-stale-reacquired-ownership.md) +lists the mutant outcomes and open cells. Larger overlap schedules, window ownership outside the ordered +replay, and durable-adapter ordering remain open under the owners named above. + +A controlled cold persisted scan gives one Query two rows in both scan orders, +then returns a current result that omits one row. Its independent replacement +model requires the survivor to remain public with its owner and the controlled +persistence boundary to receive a deletion for the omission. A row-by-row +reconstruction that treats the first persisted edge as a resolved Query fails +one order. A sibling DbClient hydration history supplies the same two rows in +both orders, then compares the public Collection with the same complete current +result and releases its demand. A row-by-row reconstruction fails both orders +at the public-row checkpoint. These finite histories do not cover larger cold +results or hydration interleaved with another active Query result. + +The alias-scope owner also has a compiler invariant witness for a malformed +include plan: removing a captured ancestor's parent projection after the +builder creates it must fail before child rows are read. Without that check, +the plan compiled and a shadowed child could read its local row. The original +Query-builder paths remain covered by the owner's public-row histories; this +invariant witness does not claim arbitrary manually constructed IR is valid. + +The following related oracles own distinct production paths or observation cuts: + +- The [Collection lifecycle publication oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/collection-subscription-lifecycle-publication-oracle.property.test.ts) + owns exact public rows and change batches. The + [replay oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/collection-subscription-replay-oracle.property.test.ts) + applies that law to competing replay schedules and same-key writes. Both + models publish a replay's rows when its last owner is released and its + current attempt has not failed, and keep a failed one private; the + publication oracle's named histories cover a value written, rewritten or + deleted during the replay, a row no demand names, a failed replay, a + later request that updates a row still shown at an older value, and a row + retained by a failed replay that the source deletes before a later replay + retires. The replay oracle's named witnesses fail each synchronous failure + site (a replay load, an old lease's unload, a new request) and then release + the last owner; removing that site's sticky failure fails its witness. + Limit: the generated grammars reach a failed retirement followed by another + retired replay only rarely (the replay grammar releases one demand, on the + last attempt), so the named histories own that sequence. + Bounded three-row histories now compare a subscription's selected keys with + callback key/type batches across full, explicit, filtered, and empty snapshots, + two later ordered selections, both initial-state modes, and callback reentry. + A delete and reinsert checks that selection can reset. A 12-case callback + matrix also crosses new insertion and delete/reinsert with filtered, + full-state, and skip-filtering subscriptions, with and without a cursor. It + checks callback batches after each source commit and checks retained-key + count after deletion. Source-callback reentry also selects an unseen ordered + row after publishing another key. A controlled three-row offset provider + checks sparse callback and ordered callback-reentry histories in both + initial-state modes: local selection does not establish a provider prefix. + A second offset-only adapter checks cursors near the end of three- and + seven-row sources: without an explicit offset, the request must be unbounded + because no finite prefix derived from local keys can reach an arbitrary + cursor value. A four-cell optimistic-overlay matrix submits a new ordered + request after one or two pending deletes or predicate-removing updates. A + plain remote array checks that the provider page reaches the first still + visible remote rank. The source commit can remain queued behind the mutation, + so this is a request-bound check, not an immediate-publication claim. + The replay owner checks a filtered snapshot followed + by an ordered selection during a pending direct truncate replay, including + callback reentry before the target receives the source transaction. It also + settles a repeated ordered replay and compares public batches before and + after settlement. A sparse successful replacement checks that an adapter + honoring a last-key cursor can still acquire lower-ranked rows. The + [snapshot-selection review](oracle-reviews/2026-10-12-snapshot-selection-review.md) + records the baseline and reservation-removal mutant kills at public-row, + request-bound, and replay checkpoints. The controlled providers cover three + and seven distinct ranks and the tested request schedules; arbitrary rankings, + provider-specific pagination contracts, and compiled live-query windows + remain outside these owners. +- The [pagination oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/query/pagination-oracle.property.test.ts) + owns general ordered-window rows and publication. Its optimistic overlay + matrix crosses one and two pending source deletes or rank moves. It checks + that a submitted provider page reaches the independently modeled window, then + accepts the mutations through source sync and compares final public rows. A + second matrix holds one or two optimistic inserts or rank moves while an + indexed window widens. It crosses cursor-expression and offset-only providers, + including windows opened from zero. The plain remote array checks the needed + provider rank when a request can start before settlement; after source + acceptance the public window must equal independent recomputation. The + original six failing cells and narrow overlay, padded-boundary, and delayed + continuation mutants reached these request or public-row comparisons. + Settled ten-page traversals bound returned provider row volume and use only + cursor-expression and offset transports. CollectionSubscription cannot emit + `lastKey`; the [TrailBase adapter test](https://github.com/TanStack/db/blob/main/packages/trailbase-db-collection/tests/trailbase.test.ts) + covers an external last-key caller and shows that core requests do not retain + unused opaque cursors. Direct ordered subscription requests without an + explicit offset may repeatedly load a growing prefix; with a cursor and no + offset they may load the full source, because the adapter is permitted to + ignore cursor expressions. The + [ordered-work oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/query/ordered-work-oracle.property.test.ts) + rechecks rows while bounding eager indexed and joined work. + These finite matrices do not establish arbitrary overlapping optimistic + histories, ties, or provider-specific cursor behavior. +- The [IR identity oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/query/ir-stable-identity-oracle.test.ts) + owns demand-key encoding in only two generated tests. The fixed demand-key cases live in [ir-stable-identity.test.ts](https://github.com/TanStack/db/blob/main/packages/db/tests/query/ir-stable-identity.test.ts), not in this owner. The + [load-subset oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/query/load-subset-oracle.property.test.ts) + owns runtime sharing and retry. The + [transaction refinement oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/query/load-subset-transaction-refinement-oracle.test.ts) + owns abort timing around sync acceptance and publication. + +The LocalStorage owners above also cover the [peer and materialization +review](oracle-reviews/2026-10-08-localstorage-peer-review.md): a failing +same-tab receiver cannot revoke a durable writer receipt or block later peers; +a nested peer write remains authoritative through a later update; a DbClient +Collection and its module-level manual utility share one write order, including +before explicit preload; unrelated equal-ID Collections cannot borrow that +utility even when they share the Storage object and key; startup +validation fails before `begin`; and clear reads once per active receiver. +The default JSON writer avoids a second full parse only when its saved bytes +remain current. Custom parsers still read back. These finite witnesses do not +establish recovery of an invalid writer key extractor or manual acceptance +without a mutation Collection owner. + +The [follow-up oracle review](oracle-reviews/2026-10-08-localstorage-final-review.md) +adds direct-owner idle, ready, and cleaned-up histories for module-level +acceptance into a DbClient materialization. It checks the manual receipt, +durable snapshot, active public snapshots, and fresh restore. A hostile subject +that drops transaction commit work fails the held-receipt checkpoint. The peer +owner checks a peer write admitted by a restore subscriber, custom-parser +normalization of an authored insert and update without a version-token change, +invalid events followed by same-token repair, and malformed stored row shape +or key identity before startup opens a sync transaction. The order owner checks +a handler-free direct write at method return immediately after a synchronous +handler failure. These finite witnesses do not prove arbitrary reentrant +subscriber histories or cross-tab atomicity. Default JSON intentionally keeps +the writer's authored rich value until restore; a Date witness records the +public/durable representation difference. + +The [second LocalStorage review](oracle-reviews/2026-10-08-localstorage-round-two.md) +extends the peer owner across unchanged versus changed version tokens for an +untouched parser-normalized row. At a fulfilled receipt it compares writer, +peer, durable bytes, and fresh restore. It also checks duplicate decoded keys, +serializable NaN-derived keys, nonfinite default-JSON row IDs, and prewrite +versus postwrite read faults. The order owner holds an earlier handler while +manual acceptance is followed by a `mutationFn` failure, then compares both +receipt cuts and the eventual public, durable, and restored rows. A direct +nested peer write replaces the unsupported manual-acceptance fixture. These +finite histories do not establish arbitrary parser side effects, concurrent +cross-tab writes, or every possible row serialization shape. + +The [boundary audit](oracle-reviews/2026-10-08-localstorage-boundary-audit.md) +crosses default-JSON native-value retention with a disjoint same-tab peer write: +the writer keeps its authored Date while receiving the peer's row, and a later +same-key peer edit replaces that Date. Writer, peer, durable bytes, and fresh +restore are observed at the peer receipt. A throwing writer subscriber after +Storage acceptance does not revoke the receipt or prevent peer publication. +The controlled host does not establish arbitrary native values or `toJSON` +side effects, many-operation histories, or native browser scheduling. Those +need a widened peer-owner value/history grammar and a receiving browser witness. + +The LocalStorage peer owner checks descriptor admission after three failed +constructor boundaries: invalid index configuration before sync binding, +controlled browser-listener registration failure during sync startup, and a +missing on-demand `loadSubset` handler after a browser listener was acquired. +A corrected retry must persist; a third construction and reuse after successful +owner cleanup must reject. The original owner can restart and persist, while +controlled browser-listener counts return to zero after failure and cleanup. +Paired hosts compare the Storage reads caused by one later same-tab write; the +failed-construction host has one extra read on `8b3debb3a`, exposing an +abandoned same-tab callback. These checks cannot prove that a failed Collection +is no longer retained by a private adapter closure or an inert callback in +the same-tab registry; those resource-retention claims need a receiving +lifecycle/GC witness in +`packages/db/tests/local-storage-peer-history.test.ts` (fixed tests; no oracle owner (tracked in #2264)). +Native browser listener failure and host scheduling remain outside the +controlled history. + +The [React source ID reuse oracle](https://github.com/TanStack/db/blob/main/packages/react-db/tests/source-id-reuse-oracle.test.tsx) ([review](oracle-reviews/issue-1991-react-source-id.md)) +checks that a mounted derived-identity hook rejects a different same-ID +source Collection directly, after an intervening ID and predicate, within one +query, and after source cleanup. Stable sources, new IDs, a fresh hook after +cleanup, and independent client scopes remain usable. Returning to a prior +client scope retains that scope's binding, including through `DbProvider`. +A suspended render that starts a source query also claims the ID until unmount. +An infinite-hook driver runs direct reuse, reuse after another ID, the same +object and a new ID, reuse after a suspended render, and deferral release on +rejection through `useLiveInfiniteQuery`, whose render-time collection cache is +keyed by the same derived identity. +Separate client-scoped Suspense hooks and an initial suspended retry keep their +same-ID sources distinct. A rejected collision render releases sync-start +deferral for a different shared client descriptor; a later direct reader reaches +`ready` and observes its row. The public rerender error, status, or row is the +observation cut. A second controlled history makes two deferred sync starts +throw and checks that a later shared source still starts and publishes its row. +This distinguishes releasing every pending source from stopping at the first +startup error. The preparation rejection still reaches React's error boundary. +The expanded release grammar has 344 histories plus four checker controls. It +crosses applicable rejection causes, ordinary/infinite/Suspense hooks with +derived or changed explicit identities, initial mounts or rerenders, and four +readers. Rejection classes or authored error identities and after-hook execution +are observed before declared `prepared` reach. A subscription reader now compares +its callback replica, rather than Collection storage, after readiness and the +next source publication. A mutant dropping update deliveries survived all 86 +subscription cells before this recorder repair, and fails all 86 at the updated-row +comparison afterward. Abandoned-render histories +establish eventual release on the schedules encountered; they do not claim both +orders of reclamation and independent demand. + +The shared [descriptor startup-release model](https://github.com/TanStack/db/blob/main/packages/db/tests/descriptor-start-release-oracle.ts) +also has React and Svelte receiving drivers. Eighty-five finite prefix/mask/policy +histories fail a descriptor factory before, or after one through three acquired +sources, followed by an unvisited tail. Every acquired source's synchronous +start-failure mask crosses every independent eager/lazy policy mask. React's +three hook paths receive 255 histories; Svelte receives 85 at its native root boundary. Each observes the +exact factory error, factory counts, independent starts, complete ordinary rows, +Collection status and acquired healthy-resource cleanup counts at rejection, +independent demand and client cleanup. Failed starts acquire no returned adapter +resource. These controlled synchronous sources do not prove provider resource +cleanup after a provider throws before returning its cleanup handle, arbitrary +scheduling, or the outer Suspense catch path described above. + +The inline-options grammar additionally crosses live and Suspense config +hooks, getKey/schema/both, and query/queryKey replacement (12 host histories). +Repeated renders and an authoritative source update retain the Collection and +configured option references; identity replacement applies the new references. +Each history declares and reaches rerender, source-update and identity-change +cuts. Schema is exercised at the JavaScript configuration boundary: the current +public hook schema declaration is `never`; this test makes no typing claim. +Infinite hooks accept no inline Collection options and remain covered by their +source-identity and window owners. The old reference-based identity fails at +Collection identity during rerender. Other framework receiving paths are not +claimed by this React driver. +This owner's source-ID law does not constrain explicit `queryKey` or direct +Collection input, and it does not install a process-wide ID registry. React +dependency arrays are removed; Svelte's supported dependency API is covered by +its own receiving tests, not this React owner. Real persisted SQLite sources and DbClient SSR preload/streaming +remain outside this controlled React driver. A space policy for a mounted hook +that visits unbounded distinct source IDs remains open; this owner needs a +bounded-navigation counter witness once that policy is chosen. + +The SQLite core binding-capacity oracle in +`packages/db-sqlite-persistence-core/tests/sqlite-core-adapter-oracle.test.ts` is a +bounded owner for issue #1993. The +[review record](oracle-reviews/issue-1993-sqlite-binding-capacity.md) gives the +exact executable revision and guide audit. It failed on the original 900-item +chunking and passes with one JSON table binding per runtime `IN` list and a +statement-total fallback. It compares independently filtered rows and typed +values with +`loadSubset` on real Node prepared SQLite statements. Node versions that expose +the configured 999-parameter limit use that native cap. Older versions use a +controlled driver that rejects over-cap bindings before SQLite preparation; +the oracle tests that guard separately. Fixed and generated cases cover empty lists, 998/999/1000 +single lists, 499+500 and 500+500 statement totals, mixed scalar/list and +nested predicates, root and distinct transaction-driver routes (including an +omitted transaction cap at the 100/101 driver boundary), signed-64-bit BigInt edges, +escaped strings, an ordered two-hit list across the old 900-item chunk, both +cursor SELECTs, and a 1,000-scalar fallback. Both index-definition `IN` +contexts must remain literal SQL. The existing CLI +large-list test substitutes values into SQL and cannot witness parameter +capacity. This new owner does not establish behavior for null, nonfinite, +container, or structured values; 50,582-ID stress; JSON-function availability; +the cost of a full-table fallback; concurrent writers; or browser, mobile, +Tauri, and native-host execution. The core owner needs typed equivalence and +row-read work witnesses before claiming class closure. Each host driver owns a +receiving capacity witness for its real SQLite engine. + +The Query ownership oracle checks that a direct-write promise stays pending +while a controlled persisted adapter holds its sync commit and settles after +the adapter stores the row. It also checks that all five direct-write methods +reject after cleanup starts and that a restarted sync run can persist a new +write. Focused Query tests check a synchronous batch callback returning another +Collection's write without losing either write, while an async callback still +rejects. They also check two serialized updates: after each awaited response +write and mutation settlement, the public row shows the server revision. These +witnesses cover an eager Query Collection after initial load with a controlled +adapter, not a direct write already in flight when cleanup starts, native +SQLite, or overlapping Query revalidation. The Query ownership and +optimistic-history owners still need a joint generated witness +for competing source writes around mutation settlement. + +The hash-identity owner also runs every law in a second module copy whose +initialization draws are all equal, so every type marker has the same hash +number in any declaration order. There it checks equality verdicts, equal +hashes for equal identities, cycle rejection, and a `topKBatch` Map-to-Set +replacement. No distinct-digest check is needed: a legal 32-bit collision +cannot weaken the equality verdict. This closes the equality type-separation +witness for every generated carrier. It does not prove hash collision freedom. Generated Map keys include numbers, symbols, +references, and containers, and the model merges keys under SameValueZero. + +The [NaN ranked-window review](oracle-reviews/2026-09-30-nan-ranked-window.md) +records an eager direct-source refinement in the pagination owner. It covers +initial ascending and descending windows, two NaN tie values, an offset move, +and equal, changed, and deleted source ranks. On-demand NaN rank acquisition, +joined queries, and null/NaN order combinations still need public witnesses in +the pagination owner before they support a broader claim. + +The [Angular conformance driver](https://github.com/TanStack/db/blob/main/packages/angular-db/tests/conformance.test.ts) +checks a required signal input on a real TestBed component. Angular assigns +the input after field initialization, then the driver checks rendered data and +exact selected IDs after initial change detection and one input change. The +driver supplies the AOT-equivalent signal-input flag because this suite uses +Angular's JIT test environment. It does not exercise Angular's AOT compiler, +older Angular runtimes, or every scheduler cut. The same driver checks that an +explicitly disabled query reports `disabled` and ready before effects run. + +The query identity owner generates both equal and unequal primitive string +pairs. Its fixed pairs also check public `groupBy` rows after initial +publication. End-to-end subset-demand reuse is still unproven: the demand-key +check observes internal identity only. The identity owner needs a subscription +witness that compares public rows and acquisition counts after equivalent and +distinct predicate demands settle. + +The subscription callback-reentry companion kills eleven guard-deletion mutants +at the named request, resource, status, replay, or retention checkpoint on the +reviewed production head. It also checks that releasing a direct snapshot +during an unoptimized callback or snapshot evaluation prevents publication. +A combined-predicate loader case checks release by the original request +predicate before the loader returns. Three +guards remain for a separate code-weight review (SUBS-09): M1's generation +check after `loadSubset` is followed by demand-membership and generation checks +after cleanup removes a starting demand; M6's replaced-state check before +restart setup is followed by detached-demand and replay-state checks; M8's +`releaseAttempted` true branch was unreached in 538 focused tests and its callers +remove or detach ownership before invoking adapter unload. These are bounded +equivalence arguments, not permission to delete the guards without a separate +review. The companion does not establish arbitrary provider callback behavior. + +Row canonicalization before the result boundary now runs only for DISTINCT, +which tracks visibility by selected value; removing it fails the functional +projection oracle. Ordering already consolidates each key's batch before top-K +state changes, and materialized relations reduce by public key. Removing the +former parent-route, non-Collection source, join, grouping, ordering, and +includes conditions passed the whole `@tanstack/db` suite and a +`TANSTACK_DB_ORACLE_RUNS_MULTIPLIER=10` oracle campaign. Without the keyed +reduction, `live-query-result-multiplicity.test.ts` witnesses the output +boundary rejecting a flush that changes one key by more than one row. + +The Collection lifecycle publication owner checks 32 bounded histories in which +cleanup interrupts nested publication deferrals and a restarted sync run later +publishes or discards one or two source rows. It compares the next subscriber's +exact batch when the new deferral closes, including an old handle closed before +or during that deferral. The includes publication owner still needs a +compiled includes witness for cleanup during a discarded source deferral, +followed by a parent or child publication at its callback boundary. + +`packages/db/tests/query/group-by-work.test.ts` owns the work law for grouped +aggregates: a change costs the same number of iterator steps at every group +size, for a `groupBy` count and for an include count. `group-by.test.ts` +checks which member supplies the value of a group whose members are equal +under query equality but differ exactly, in both arrival orders. It also +checks that `min` and `max` equal a remaining member's value after each delete, +for signed zero and Dates and for stored and rebuilt arguments +([review](oracle-reviews/group-representative-exact-value.md)). Open: no law +observes which parent supplies an include route's parent context, because a +single-group include aggregate drops non-aggregate select fields; parents equal +under query equality but different exactly share one route. + +The [Temporal group-key oracle](https://github.com/TanStack/db/blob/main/packages/db-ivm/tests/temporal-group-key-oracle.test.ts) +owns the db-ivm `groupBy` value boundary for the eight Temporal kinds recognized +by structural hashing. It compares public group counts with fixture-defined +identity classes after one input batch, including distinct and matching fresh +values and a plain object with its own spoofed Temporal tag. Nested +serialization controls cover records, arrays, Maps, and Sets. Incremental +retractions and unsupported symbols, cycles, and mutable RegExp state are outside +this owner's bounded domain. The owner uses db-ivm's established kind-and-string +Temporal key domain. Alternate representations remain separate: Durations such +as `PT1H` and `PT60M` form different groups even when they compare equal. The +supported inputs are ordinary native Temporal values or values from one +polyfill copy. A custom prototype that impersonates Temporal can still merge a +plain object with a genuine value. A genuine value with a replaced prototype +can split from an equal value. Both altered-prototype cases and separately +loaded polyfill copies are outside this contract. The oracle still protects a +plain object's own spoofed tag without a custom prototype. + +The [includes publication oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/query/includes-publication-oracle.test.ts) +now checks one held ordered repair with two public parent rows and one inline +child. Its bounded history crosses a single-step row move, a private value +change that returns to the prior value, and a final value change. It compares +root Collection reads and subscriber callbacks with an independent sorted +source Map, and checks Effect callbacks against the last callback-visible row. +Its optimistic rollback histories also require the callback to be reached and +compare the restored public rows and callback trace after rejection. This +rejects a final-row-only check that misses a torn callback publication. +The [DEC-02 review record](oracle-reviews/2026-09-28-retracted-value-retention.md) +retains the RED and mutant evidence. The receiving Collection-valued owner +(`packages/db/tests/query/includes-collection-oracle.property.test.ts`) now +checks a held finite ordered child repair request with A→A, A→B→A, and B→C histories, +facade identity, silence during the hold, final child order, and subscriber +delivery. The optimistic owner +(`packages/db/tests/query/includes-optimistic-oracle.property.test.ts`) checks +a same-root-row optimistic update during held ordered repair, comparing exact +public snapshots and change events before release, after release, and after +rollback. Both use controlled provider timing; real-provider ordering remains +a separate receiving handoff. + +The ordinary committed-transaction companion in the SQLite resume-snapshot +owner checks durable rows, row and collection metadata, expected-key evidence, +tombstones, applied position, idempotent reapply, and replay threshold behavior +for bounded insert, partial-update, delete, metadata-only, and repeated-key +histories. Its fixed and random generated campaigns cover a two-row seed, +ordered action prefixes, seven required action/metadata suffixes, optional +dependent updates, and late-bookkeeping rollback, comparing durable state after +each settlement. The generated grammar is bounded to two writable string keys, +one absent metadata key, up to 60 prefix mutations sampled in short, middle, +and long bands, three row-metadata and +two collection-metadata prefix actions, and up to three committed positions; +it does not claim arbitrary histories or concurrent owners. Every generated +seed, candidate, dependent update, and late rollback must enter exactly one +SQLite transaction; counters are captured before durable snapshot reads. A +separate fixed/random generated work grammar covers 0–60 distinct keys, six +insert/update/delete/row and collection metadata shapes, and 100/999 parameter +caps. It checks +durable values before applying the same proposed chunk-bound work law; all +repeated-key histories now check a distinct-key work bound after both committed +and rolled-back candidates. A fixed 205-update single-key case drops from 824 +to 8 query/run calls against a bound of 18. Truncate cases require overwritten +invalid row and metadata values, including unbindable `undefined` collection +metadata, to reject and roll back. Fixed 25/26 and +10,000-row cases separately witness the independent-key work boundary. Both +fixed and generated independent-key work laws were +RED on `ef1e6a4` and are GREEN with ordinary batching: the 10,000-row insert +plus metadata takes 505 query/run calls instead of 50,005. A later +125-row-ceiling follow-up checks 124/125/126 rows under a 500-parameter host +cap; the 10,000-row case now takes 405 calls. A temporary +repeated-key classifier mutation fails the generated oracle at the candidate +durable checkpoint, seed 1992, path `2:0:0:2:2:2:2`. A temporary chunk-limit +mutation passes the Cloudflare 25-row case but fails at 26 rows under the +100-parameter guard. A collection-metadata classifier mutation that ignores +repeated keys fails at the durable checkpoint for a set/delete history. All +mutations were restored. The Cloudflare receiving driver also persists ordinary +writes with row and collection metadata at 25, 26, and 205 rows within that +guard. Its Node SQLite storage seam does not establish Cloudflare +Worker execution. The synthetic node:sqlite seam does not establish OPFS +worker scheduling, browser latency, or native-device performance. Tagged-value +serialization, unavailable key evidence after raw loss, concurrent schema +migration, WAL across processes, and arbitrary longer transaction histories +remain with their existing owners or need explicit future witnesses. + +The same owner checks the optional source-progress bridge with an independent +path-of-edges model. A discontinuity stays incompatible across matching and +empty commits; a repeated endpoint is idempotent; a full replacement repairs +continuity. Physical key integrity is checked separately after every transaction. +The fixed/random `sqlite-resume.source-progress` campaigns use 80 runs each, +seed 2281 and direct seed/path replay. Histories have 3–16 actions plus an initial +replacement, opaque positions, stale predecessors, duplicates and optional old +schema migration. Declared reaches require gaps, later matching commits, +replacement after a gap, duplicate endpoints and migration. The primary Browser +owner and real OPFS receiver supply source-head convergence evidence; the storage +model alone does not establish provider framing or cross-host scheduling. + +The SQLite resume-snapshot owner also checks a cold, unique-key, non-delete +full replacement with 205 rows. It compares exact durable rows, row metadata, +key-set evidence, resume metadata, and applied position at transaction return; +the driver query/run count is bounded, and a later-batch fault rolls back the +whole transaction. Duplicate-key and delete histories keep their sequential +semantics. This in-memory driver-call law does not measure Chromium OPFS +latency, worker scheduling, or an end-to-end Electric snapshot. + +The Cloudflare Durable Object test applies 25-, 26-, and 205-row full +replacements and ordinary writes with row and collection metadata through the +actual Cloudflare driver. A storage seam enforces Cloudflare's documented +100-bound-parameter query limit and checks durable keys and metadata after each +transaction. Its subset receiving witness compares exact rows for 100 and 101 +equality or one-item `IN` clauses through both savepoint and native transaction +drivers; the same seam rejects a statement above 100 bindings. The original +transaction driver exceeded the cap at 101. This Node SQLite seam does not +execute in Workers or prove native JSON-function support. + +The [worker diagnostics oracle](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/tests/opfs-worker-diagnostics-oracle.test.ts) +drives the real worker module and also classifies a refused OPFS storage +root, as in a private window or a browser without OPFS storage: the worker +answers `PERSISTENCE_UNAVAILABLE` with the browser's error name and message +before SQLite opens, and a `PersistenceUnavailableError` response carries only +its details, which the opener prefixes once. + +The browser OPFS lifecycle owner also checks that a silent initialization +rejects at the default or overridden open deadline, terminates its worker, +ignores a late response, and releases the deadline and abort listener after a +successful open. It also checks an abort during synchronous option evaluation, +after the initial signal check but before listener registration. A Chromium +fixture holds the VFS database Web Lock while a second tab times out. It checks +that the queued request disappears, then reopens the database after releasing +the lock. This controlled lock holder does not +reproduce a frozen tab or establish behavior in other browsers. +The [issue #1883 review record](oracle-reviews/issue-1883-opfs-open-deadline.md) +preserves the exact-head unit and Chromium evidence and the queued-lock mutant. +The jsdom open cases start after asset resolution. The +[OPFS open outcome oracle](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/tests/opfs-open-outcome-oracle.ts) +owns how an open settles across its four steps: the Vite asset lookup, worker +start, WASM load, and initialization. One module holds the law, the history +grammar, the model, and the comparison; neither driver states expected +results. A history fixes each step (the lookup resolves, is held, or fails; +the worker starts or is refused; the WASM loads or fails; initialization +answers or stays silent; the worker script can also fail to load) and pairs +every pending step with a deadline or an abort. The model predicts the +settlement, started and terminated workers, the steps that production reached, +and a later open after an interruption. The +[jsdom driver](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/tests/opfs-open-outcome-oracle.test.ts) +reaches worker start, initialization, and the worker's WASM failure response. +The [Chromium driver](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/e2e/opfs-open-outcome-oracle.e2e.test.ts) +reaches the lookup, the real worker's WASM fetch, and worker start against a +Vite dev server that pre-bundles the built package. It holds back only the +opener's deadline timer and fires it explicitly, interrupts only after the +server holds the lookup, and reads the settlement and worker counts one task +after each event, so no observation depends on elapsed time. Each driver checks that its +history product covers the outcomes it declares; the `reached` observation is +the production reach witness. Mutants that leave the lookup outside +the deadline and abort, start a worker after a late lookup, report a WASM +failure as an internal error, or let a refused Worker constructor's error +escape each fail the driver that reaches that step, and a mutant that rejects +before making the lookup fails only on `reached`. The grammar does not +interrupt an open in the same turn as a failing step, because no law orders +those causes yet. + +The +[install-layout oracle](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/e2e/install-layout-oracle.e2e.test.ts) +models which installs open in Vite development and production: direct, +hoisted, pnpm, transitive, npm-alias, and two-copy installs, three cache +directories, and exclusion by import name or package name. It reads the +importer's specifier and the Node resolution of the package name from each +installed tree rather than from a table per layout. A pre-bundled copy +opens when the app root resolves the package name and otherwise reports +`PersistenceUnavailableError`; with two copies it loads the copy that the root +resolves, and when that copy is another version its worker refuses the opener +and the open reports `PersistenceUnavailableError` after one worker starts. The +two-version layout rewrites the nested copy's built version marker, so the +opener and the root copy's worker really come from different builds. Excluding the specifier that the importer uses serves the copy in +place with its own worker. The README states these rules. A mutant that always +runs the lookup fails the three excluded transitive, alias, and two-copy +cases. Its dev servers do not watch files, because a late watcher event +reloaded pages during an open. + +The packaged-consumer +[checks](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/e2e/wasm-package-consumer.e2e.test.ts) +cover the build output and export map, a classic worker in development and +production, an `about:srcdoc` frame, and a page on another origin than the +Vite server, where the Worker constructor throws `SecurityError` and the open +reports `PersistenceUnavailableError`. The +[bundler consumer checks](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/e2e/bundler-consumers.e2e.test.ts) +install the built workspace packages as published packages in a webpack 5 app +(production and development builds) and a Next.js app (Turbopack and webpack, +each under `next build` with `next start` and under `next dev`). Each opens and +queries SQLite in Chromium, starts one classic worker, loads one +`200 application/wasm` response, and makes no Vite lookup request. Each +production build, and the Vite consumer's production build, ships none of the +lookup's code. A module +worker fails all six; a lookup that runs without a Vite client fails the four +Next.js cases and is equivalent under webpack, which emits the worker and WASM +in one directory. These owners do not cover webpack-dev-server, Rspack, Parcel, +esbuild, an open from inside a worker, a Vite client injected without a script +`src`, or browsers other than Chromium. The +[PR #2103 review record](oracle-reviews/pr-2103-separate-wasm.md) classifies +each calibration control and records every applicable requirement. + +The [browser OPFS lifecycle owner](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/tests/opfs-page-lifecycle-oracle.test.ts) +also generates explicit close histories: zero to three pending executes, how +many answer while close is in flight and in which order, a successful close and +every worker error classification, a concurrent close, an execute submitted +during close, and pagehide or a Worker terminal event before the close +response, which then rejects close and pending siblings with its own failure. At close settlement, answered executes keep their rows and the +rest reject with `AbortError`. Every close call shares the first close's +outcome. The worker receives exactly one close request, and work submitted +after close starts never reaches it. Rejecting at close start, posting a second +close, admitting work during close, deferring rejection past close settlement, +and the pre-fix hang each fail this lane, as does ignoring pagehide or a Worker +error while close is in flight. The controlled worker +answers in any order, so these histories also cover a close response that +overtakes pending work. + +The [OPFS request-order oracle](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/tests/opfs-request-order-oracle.ts) +states that a connection settles requests in acceptance order with sequential +results, settles close after them, refuses work after close starts, and keeps +answered writes across a reopen. One module holds the law, the sequential +replay model, the fixed examples, the public-connection driver loop, and the +comparison. Two drivers run it. The +[Node driver](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/tests/opfs-request-order-oracle.test.ts) +runs the real worker module and wa-sqlite WASM build behind a bridge Worker, +over an in-memory VFS with OPFSCoopSyncVFS's lock protocol, with fixed, +random, and SEED/PATH campaigns. The +[Chromium driver](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/e2e/opfs-request-order-oracle.opfs.spec.ts) +runs every fixed example against real OPFS and takes the access handle through +the VFS's own BroadcastChannel and Web Lock handoff. Histories vary one to +three inserts and selects, which execute meets a taken handle, whether the +handle returns before or after close reaches the worker, and an execute after +close. The previous worker handled messages concurrently, so close ran while an +execute waited for the handle and SQLite refused to close. That worker and a +worker that queues executes but not close fail both drivers. Neither driver +proves other browsers, frozen tabs, a crash between a write and close, or more +than one other handle holder. The review record is +`docs/contributing/oracle-reviews/pr-2213-opfs-close.md`. + +The ordered-work owner also checks that an Effect with `skipInitial` waits for +joined demand before ending initial callback suppression. A deterministic work +counter rejects an extra joined-filter graph schedule on root readiness while +preserving the joined-source wake-up. Nonterminal per-demand failure recovery +is outside this owner: `failDemand` enters terminal live-query error while its +plan record remains unsettled until sync teardown. A future recovery contract +must reconcile plan and subscription pending state before reopening publication. +Its startup witness covers one synchronous rank update after an indexed page +load and before the last source subscription returns. It does not cover every +source ordering, multiple buffered mutations, or asynchronous startup changes. +The [ordered Effect startup review](oracle-reviews/gap-06-ordered-effect-startup.md) +records its baseline and hostile-mutant observations. + +The Collection lifecycle change-event history owner also checks eager auto-index +equality buckets and fresh indexed `eq` live queries against its independent +row Map. Its required same-turn delete/reinsert prefix reaches an already-built +index for both BasicIndex and BTreeIndex. In the batched lane, public rows and +equality buckets are checked after each mutation returns, before any transaction +is awaited; the batched final state and each sequential settled prefix are also +checked. Fresh indexed queries are checked at the final checkpoint. +Fixed and random tails check nearby legal histories. One temporary mutant that +skipped index updates failed the fixed batched final count: one indexed key +versus two model rows. Another fault that preserved the final keys failed the +new returned-mutation check. The direct index refinement owner does not prove +this Collection mutation path. Callback-time index agreement, +the reported insert-seeded string-key setup, adapter cancellation, and +non-local-only persistence remain outside this lane. +The [issue #1912 review record](oracle-reviews/issue-1912-eager-index-history.md) +records the grammar controls, exact reviewed commit, and ORC audit. + +The [live Electric immediate-reload witness](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/e2e/electric-immediate-reload.opfs.spec.ts) +checks one Chromium/OPFS history: an `onInsert` handler receives PostgreSQL +transaction evidence, `awaitTxId` and the optimistic transaction settle while +the source row's adapter write is held, and a hard page close is followed by +an empty raw OPFS snapshot before the recovering Collection or Electric starts. +After release, the witness checks the exact public and durable row from live +Electric and records that the reopened page sent no user-insert request. Public +rows are polled before durable snapshots to avoid a full OPFS read on every +pre-publication tick. This bounded history does not prove Windows/Edge behavior, +offline recovery, arbitrary later re-inserts, or a durable-acknowledgment +contract for `awaitTxId`. + +The [optimistic-history oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/optimistic-history-oracle.ts) +now owns the held-row law for sync transactions deferred behind a wrapping +sync's durable step, and checks that every optimistic mutation is visible in +the tick that makes it. Its +[persisted lane](https://github.com/TanStack/db/blob/main/packages/db-sqlite-persistence-core/tests/persisted-optimistic-history.test.ts) +runs the shared grammar through `persistedCollectionOptions` with +`hold-store` and `release-store` steps and a handler that commits its own +server row. It was RED before the wrapper reported deferred sync keys to +core: when an earlier source transaction's store was held, a settled row +dropped to an older synced row before its deferred server row applied +(insert-then-update and insert-then-delete histories). It is green now, and +mutants that drop deferred keys from the hold, drop the held row at a drain, +or skip the held overlay after a drain each fail it. The grammar also +aborts a deferred transaction before its turn, and ends every history with a +source transaction on every key, so a stale hold or leaked local attribution +shows. That step was RED on the first fix: the wrapper marked an aborted +transaction as handed to the Collection, so its hold never ended, and a +non-optimistic write kept local attribution for a dropped transaction. A +campaign check requires each run to reach a hold across a drain and a hold +released by an abort. Open: wrapper cleanup or source restart while a +transaction is deferred has no grammar step, and a held-row release filtered +as a recent sync echo was not reproduced. The lane uses an in-memory +adapter. +Several tabs on one durable store each run their own source and store +every source transaction they receive. DB promises only eventual +consistency between them: while their streams interleave, a lagging tab's +copy of an older transaction can be stored and announced after a newer one, +so a tab can briefly show an older server state. Every tab delivers the +same log in order, so durable state and every tab converge once the streams +catch up. No lane checks this yet. +A counterexample bounds that convergence claim +([#2209](https://github.com/TanStack/db/issues/2209)). Two Browser tabs +share one SQLite file through the real coordinator, with a controlled +Electric stream per tab. Tab A's stream applies v1 then v2 at `30_0`. Tab B's +lagging stream applies v1 at `20_0`, and tab B closes before it reaches v2. +Tab A then shows v1, and the durable `electric:resume` offset is back at +`20_0`. Nothing redelivers v2 to tab A while it stays open, because its +stream is already past it. A tab that opens later resumes from `20_0` and +repairs the cache in that history. Restart formerly failed when a later commit advanced the resume position +over stale rows (#2281). The eager SQLite bridge now records source +continuity atomically and requires a fresh snapshot on reopen after a gap; +see the [repair evidence](oracle-reviews/issue-2281-investigation.md). +This preserves temporary arrival-order regressions and leaves #2209 open. The +multi-tab Electric oracle in `browser-db-sqlite-persistence` pins this +history with `it.fails` beside a passing control in which the lagging tab +catches up. Ordering the copies would need a generic +source position on sync commits; Electric already stores `{ handle, offset }` +with each commit, PowerSync does not use this wrapper, and persisted Query +collections have no log position. + +The [PR #1914 follow-up review record](oracle-reviews/pr-1914-immediate-reload-follow-up.md) +preserves the held-checkpoint and extra-POST mutant results at the exact +reviewed executable commit. + +The [PR #1902 review record](oracle-reviews/pr-1902-change-event-history.md) +also covers one-pass queued-sync cancellation, partial-update dependencies on +queued source rows, and focused late-hydration cases in +`packages/db/tests/db-client.test.ts`. Those cases cross queued adapter insert +and delete with a held local mutation. The +[DbClient hydration authority oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/db-client-hydration-authority-oracle.test.ts) +crosses applied adapter insert, update, delete, and truncate with hydration +before and after the applied receipt. It includes delete/reinsert and +truncate/reinsert controls. These owners do not establish arbitrary hydration +stream order, a later authoritative hydration epoch, or unbounded retention. + +The [two-tab OPFS remote-subset oracle](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/e2e/remote-subset-two-tab.opfs.spec.ts) +now exercises a real Chromium follower with a QueryClient-backed, filtered +on-demand live query. It checks the function-bearing local request, clone-safe +follower post, public row, and rejection of an invalid nested wire value before +transport. A raw-options mutant fails with browser `DataCloneError`. The +[review record](oracle-reviews/2026-09-27-remote-subset-two-tab.md) names the +exact reviewed commit. The test does not run React, Firefox/Zen, or a live +backend, and it does not settle exclusive OPFS ownership during leadership +transfer. + +The [two-tab live Electric OPFS oracle](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/e2e/electric-resume-two-tab.opfs.spec.ts) +crosses real Chromium tabs, Web Locks, OPFS workers, PostgreSQL, and the +installed Electric SDK. It checks distinct per-Collection schema versions, +unchanged public and durable rows when a follower opens, leadership transfer +with later source rows, and recovery of partial-row loss or a fully empty +pre-key-ledger baseline with a stale resume marker. A wrapped source-commit +test in `packages/browser-db-sqlite-persistence/tests/browser-coordinator.test.ts` +checks scheduler entry before the writer-lock request; the old bypass fails at +that order assertion. The live recovery histories also guard the resulting +progress. These bounded histories do not prove Firefox/Zen, +React rendering, React Native, arbitrary large datasets, or an exclusive-OPFS +ownership topology. Their +[review record](oracle-reviews/issue-1589-live-electric-opfs.md) preserves the +RED and GREEN host observations. + +The [issue #2085 review record](oracle-reviews/issue-2085-leader-close-red.md) +preserves the baseline RED result and the subsequent repair audit. The source +Collection law keeps an applied receipt pending after an indeterminate RPC. The +replacement leader checks its exact transaction ID under the writer lock; an +existing ID acknowledges the commit, while an absent ID permits application +only against an unchanged durable anchor. The Collection and a dependent live +query stay in one sync run. Direct mutating RPCs retain the #1845 +`IndeterminateCommitError` contract. An absent ID after an intervening write, +reset, or pruning remains indeterminate because applying it could regress a +newer row or opaque source cursor. + +The persisted wrapper owner in +`packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts` checks +before/after durability, pending receipts, mounted live-query rows, queued +hydration, later work, known durability failures, and an unchanged +reconciliation reload. Its orphan row-metadata case fails on a shortcut that +skips needed cleanup. Sparse-array, extra-array-property, and nested-prototype +histories require a changed durable value to reload even when ordinary deep +equality would skip it. + +The PR #2096 four-oracle review adds a mixed-demand reset route to the +targeted/full-reload history. A previously public row outside the current +filter is retained, refreshed, or deleted according to complete durable +evidence; an unconfirmed DbClient seed remains source-owned. The controlled +reset must publish no intermediate empty snapshot. Eighteen crossed histories +compare every callback snapshot and final rows, including absent or older +durable seed values. The original reset failed at the callback checkpoint. +Targeted peer metadata set/delete now crosses active complete demand, an active +filter, and retired demand with row update/delete/insert: an unconfirmed seed +keeps both its row and its row metadata. The old receiver failed all eighteen +seed-metadata histories. A protected-seed no-op reconciliation control reaches +the same public event cut and emits none; its suspected redundant public event +did not reproduce. Accepted same-key source transactions racing a targeted +notice, paginated mixed demand, a peer write between complete and filtered +reads, and atomic multi-subset reads remain unproved by these controlled +histories. The [four-oracle review](oracle-reviews/2026-10-12-pr-2096-four-oracle-review.md) +records the exact laws, witnesses, and dispositions. + +Direct persisted options and their spread copies can create only one Collection, +both with and without a wrapped upstream sync source. The second creation +rejects before it redirects the first owner's local or source writes. +Selecting only the exposed wrapped `sync` config omits the descriptor claim; +the runtime guard rejects that second Collection before changing control +callbacks. A local-only write and an in-flight upstream source transaction +remain with the first owner after the rejection. +The ownership history now starts a selected-sync copy before the original +lazy wrapped-source Collection as well as after it. A copy cannot bind first +and steal the runtime from the successfully constructed options owner. The +LocalStorage peer owner crosses those start orders for a selected `sync` copy +and checks the owner's receipt, public rows, durable bytes, and listener count. +Controlled listener and row observations do not establish that retained module +options stop strongly referencing a cleaned-up restartable Collection. Whether +cleanup promises collectability remains a separate resource-contract question. +Cleanup does not release that claim. A selected `sync` config also cannot bind +a second Collection after the first cleans up; the original Collection can +restart and write through the same runtime in local-only and upstream-sync +modes. Two DbClients materializing one descriptor receive independent runtimes. +A no-demand on-demand full-reload notification reads collection metadata +without fetching durable rows when no row is public. If a retired demand left +public rows, the notification reads one durable snapshot and corrects only +those rows. It retracts a peer-deleted row, updates a changed row, and removes +obsolete row metadata without exposing unrelated stored rows. A later +constrained demand does not need another validation read. An empty bounded +page does not retract a row outside its scope. Eighteen source-precedence histories +for that route preserve accepted source rows across insert/update/delete, +truncate replay, and queued or serial reads. The original unbounded-read path +and direct options reuse both failed these new witnesses before repair. +The notice corrects already public rows even during the idle interval after +demand retirement. With an active unpaginated filter, six further histories +retain, update, or delete a previously public out-of-filter row across targeted +and full-reload notices while another in-filter row changes. The original +mixed-demand implementation failed three of six exact public comparisons; +the repaired path validates previously visible out-of-filter keys from a +complete durable snapshot. Paginated mixed demand, arbitrary overlapping +retired subsets, a peer write between the full and filtered reads, and host +scheduling remain outside these bounded controlled histories. +A failed direct Collection constructor releases its options claim if the runtime +never bound an owner; a corrected retry can then create the Collection. A +successful owner still retains its claim through cleanup. The persisted owner +also checks DbClient hydration seeds against absent, older, and equal durable +rows through complete demand, zero-demand peer validation, and a bounded peer +notice. The active-notice history combines a protected seed with ordinary row +replacement, deletion, and insertion, then repeats the notice with an older +durable seed. These controlled paths assert public rows and change batches; +they do not prove every host-delivered peer schedule. Zero-demand validation +clears orphan row metadata with zero or one public row, while an equal +metadata-bearing row emits no change batch. The full durable snapshot used to +validate visible keys remains O(table) per notice, including when a current +filter leaves previously public keys outside its scope. There is no O(visible) +read budget. A keyed atomic snapshot would need an adapter contract and +receiving witness before that work bound can be claimed. + +The persisted wrapper owner models complete durable rows as exact values: +field absence, including an own `undefined` field in the generic adapter, +matters as much as scalar content. Eighteen bounded histories cross complete +demand, retired-demand full-reload notice, and retired-demand reset with field +removal, addition, and scalar change. They compare full public rows, event +values, deleted and newly admitted keys, and a protected DbClient hydration +seed at settled publication. Nine removal histories fail on the reviewed +`8b3debb3a` implementation, while nine neighboring controls pass. The +`undefined` arms are generic-adapter evidence; SQLite object encoding omits +those fields. A captured-read source history checks submitted same-key and +new-key writes after the held read releases, while eighteen complete-demand +source-precedence histories extend the existing bounded matrix. These checks +do not assert that a source write was accepted before the held read releases. + +Targeted `tx:committed` notices have separate bounded demand-scope and +partial-patch histories. Six cases cross active `title = Keep` demand and +retired demand with a row that stays in, enters, or leaves that predicate; +five fail at exact public-row comparison and the leaving-active control passes. +Nine more cases protect a DbClient hydration seed against durable replacement, +partial update, or deletion under active complete, active filtered, or retired +demand while an ordinary row changes; all nine fail. Two active-demand union +controls keep a row that leaves `title = Keep` but enters `title = Other`, in +both demand acquisition orders; both pass on the reviewed head. Temporary +first-only and last-only evaluator mutants each fail one control at the public +snapshot comparison. +Ten source-confirmation histories apply a source insert, update, or delete to a +DbClient hydration seed, await its applied receipt, then deliver a targeted or +full-reload peer update, deletion, or insertion. A changing ordinary sibling ensures a +perpetual-seed-protection mutant fails at exact public-row comparison rather +than only timing out. Six same-key operation-order cases cross active complete and retired demand +with delete-then-update, update-then-delete, and two partial updates; three +fail and three pass. The independent models select authored complete durable +rows, while notices carry legal source patches. Delete-then-update and +update-then-delete produce the same separated `changedRows` and `deletedKeys` +but opposite durable outcomes, so the receiver cannot infer the row from that +envelope alone. The baseline failures and neighboring controls are recorded in +the [PR #2096 law review](oracle-reviews/2026-10-12-pr-2096-oracle-laws.md); +the repaired controlled paths pass. Further in-scope source transactions +concurrent with targeted delivery need a same-key acceptance-order witness in +this owner. +Real SQLite delta creation and browser/Electron notification delivery need +matching receiving witnesses in their existing adapter and host owners before +this controlled result can be claimed across those boundaries. +The Electric recovery owner has separate real SQLite restart histories. Its +generated peer-publication history instead uses a Map-backed adapter and a +manually delivered notice. It compares exact public rows, durable rows, and a +committed metadata marker. A full-reload notice requires an authoritative +durable row read in that active-demand history, through either adapter row-read +route. A targeted notice for a previously unseen key may also read durable +rows because its value has no completeness certificate. The oracle does not +treat `requiresFullReload: false` as a no-read guarantee. +The metadata of an unseen targeted row is not asserted to be demand-scoped: +core permits explicit row metadata for an absent row, so this needs a separate +product decision before changing that contract. + +The user chose demand-scoped schema-reset publication for on-demand +Collections. With no active subset, reset validates only already public keys +against durable rows; an empty public Collection remains empty, and a changed +or deleted visible row is corrected. Eager reset still restores its full +baseline. Eighteen source-precedence histories cross on-demand reset with +insert/update/delete, source truncate replay, and queued or serial reads; +three focused reset histories distinguish empty, changed, and deleted visible +scopes. They do not establish host notification delivery or a keyed SQLite +read budget. The visible-key validation may read the full durable table. + +A controlled term-jump history recovers a missed row; a position-only +certification history shows that observing a durable version +does not publish its rows. Reset continuations rebase the publicly applied row +version, reject a delayed pre-reset notification in both orders relative to a +newer election, and recover a row committed between the reset reload and the +later position-only read. A controlled same-term reset continuation checks two +new-epoch commits separated by a delayed old-epoch notice. The commit envelope +has no reset epoch, so the wrapper rereads durable rows for all three ambiguous +notices; a no-fence mutant publishes the old row. This establishes public-row +safety and its three-reload work cost, not a bounded-work promise. The built-in +coordinator currently has no `collection:reset` sender; a real-host same-term +reset needs a Browser/OPFS receiving witness. A future epoch-aware fast path +needs the persisted wrapper owner to distinguish old and new same-term notices +at each public cut before that real-host receiving witness. The controlled +wrapper compares public rows after each held cut; removing the old-term fence +or treating the later position read +as published rows fails those assertions. The SQLite adapter owner checks exact +IDs, row versions, reset epochs, pruning, +unchanged anchors, same-key peer writes, cursor metadata, durable no-write term +reservation, and the exact-ID index plan against real SQLite transactions. It +also checks that a live claim on a retired physical generation can proceed, +that live claims cannot cross physical generations, that expiry fences +reconciliation and term reservation before a durable write, +and that a new claim remains usable. The Browser coordinator owner checks +writer-lock stream advancement, changed +routes, and invalid wire anchors. It delays a former owner's captured heartbeat +past a no-write Web Lock takeover and checks both the new owner and a passive +follower retain the newer route after the delayed heartbeat. Distinct durable +terms protect the later routed source write. Its election witness checks that +the registered physical storage ID and claim reach term reservation. The +Electron IPC bridge runs the +SQLite adapter contract through its renderer and main-process handlers, +including exact-ID reconciliation and protocol-v5 claim-scoped term reservation. Its +resolved-adapter witness checks that reconciliation carries each Collection's +mode and schema version, and its absent-capability witness checks that an +unknown result leaves durable state unchanged. The real-process witness runs +the renderer adapter in Vitest and sends its envelope to an Electron main +process. It does not execute the adapter inside an Electron renderer or check +concurrent requests from multiple renderers. A reconciliation IPC reply delayed +until after main applies is an untested transport history owned by the Electron +receiving contract. The Chromium/OPFS host owner runs idle, before-durable, +after-durable, peer-before-reconciliation, notification-before-lost-answer, +and cold-successor histories across real tabs, Web Locks, BroadcastChannel, +workers, and SQLite. +It observes durable rows, cursor, and exact applied transaction IDs, public +source/live-query rows, receipt settlement, one sync run, and passive-tab event +counts. The peer history holds +reconciliation before the writer lock until the peer write is durable. Removing +the reconciliation reload marker leaves the passive source and live query +without the original row at that public checkpoint. The real Electric receiver +in `packages/browser-db-sqlite-persistence/e2e/electric-leader-close.opfs.spec.ts` +streams a PostgreSQL row through the installed SDK, closes the first writer +after SQLite applies that source transaction, and checks the surviving +Collection, mounted live query, durable rows, exact applied ID, resume marker, +and later PostgreSQL row. The original Electric applied receipt fulfills in +one sync run. The crossing row's transaction and first durable snapshot agree +on resume offset, handle, and shape ID. It supplies the SDK handoff for this one +after-durable history. + +The wrapper's controlled term jump and the real OPFS cold-successor election +are separate evidence cells. The persisted wrapper now has a controlled +same-key peer-write continuation: +X is durable at version 1, Y advances storage to version 2, exact-ID +reconciliation acknowledges X's original position, the source receipt fulfills, +and the wrapper's owned resume evidence fails closed against version 2. A +temporary latest-position response makes that evidence assertion fail. The +real SQLite exact-ID decision and this wrapper receiving history still have +separate drivers; one real-adapter wrapper join remains with the persisted +SQLite receiving owner. A single real-host history combining a missed +original notice, an empty reserved term, and a passive follower's later public +rows remains with the Chromium/OPFS owner. The controlled nested-prototype +replacement reaches source and mounted live-query cuts; real SQLite +serialization of that nested class in the same wrapper history remains with +the Node SQLite receiving owner. The reset continuation uses a controlled +snapshot with the same version-zero premise that the real SQLite adapter's +schema-reset oracle proves. A combined real-adapter wrapper reset and later +notification remains with the persisted SQLite receiving owner. + +The persisted wrapper owner also holds an on-demand resume certification across +a newer, sequence-zero term while the row version and reset epoch stay fixed. +It rejects the old exact-generation guard at key-set evidence. The Electric +held-snapshot owner joins the real SQLite term reservation with the persisted +wrapper and mocked Electric stream: the source retains its cursor and reaches +ready with the original durable rows. Neighboring committed-write and schema- +reset histories reject inheritance of the old baseline. The real Chromium/OPFS +reopen history still owns the host election and restart schedule; this local +join does not prove that schedule passes in CI. + +A bounded TLA+ design grammar and exact TLC receipts live in +`review-evidence/issue-2085-tla/`. It explores one X, one same-key peer Y, +one reset, delayed/lost notices, position-only observation, and ledger pruning; +it checks the reference laws and kills wrong-design mutants but does not +execute a production path. A separate challenge shows that an old commit +notice can publish an old row after durable reset _before_ a passive peer +receives any reset signal. The built-in coordinator has no reset sender, so +this is an unresolved proposed cross-tab reset law, not a confirmed current +receiving failure. A future sender needs a Browser/OPFS witness for that +pre-signal order. + +This bounded owner assumes tabs run the same protocol version and one `txId` +identifies one immutable payload. The Electric receiver does not cover +before-durable loss, queued hydration, two Electric providers contending in +both tabs, seven concurrent Collections, or a closing leader's active SQLite +call; those need receiving schedules owned by +the Browser Electric, hydration, and OPFS page-lifecycle suites. Arbitrary +concurrent writes, physical crash durability, other browsers, and +cross-version tabs are outside the established host evidence. These limits do +not authorize a late apply after an intervening durable write. + +The persisted wrapper owner now also checks the no-`pullSince` row-version-gap +fallback at every Collection change callback: a durable baseline, a missed +commit, and a later notice may expose only the baseline or the complete durable +replacement. The old truncate fallback exposed an empty intermediate snapshot. +The Electric recovery owner drives ordinary peer notifications only after its +adapter commits matching rows, metadata, and a contiguous row version; its +bounded peer/stream histories retain exact public-row and publication-marker +checks. The previous fixture announced version 101 while its durable position +remained zero, so the new gap detector treated an ordinary notification as a +missed history. These controlled owners do not prove real Electric delivery of +a lost-notice gap; the Browser Electric/OPFS receiver owns that schedule. +The Electric recovery fixture now distinguishes same-key insert replacement +from partial update merge. Its generated peer-publication grammar varies that +operation and a removable field, and requires a subscriber event carrying the +authored callback-time snapshot when public rows change. A missing-event mutant +fails at that event assertion. The generated same-key insert history found a +receiver violation: targeted notices omit insert/update identity, so a public +row must be checked against durable state before interpreting an omitted field. +The primary persisted owner crosses insert replacement with two-update and +delete/update controls under active and retired demand; removing the durable +check fails both insert histories at exact public-row comparison. These +Map-backed histories do not establish real SQLite notice construction or +browser delivery; the relevant real-provider receiver remains needed. + +The [live Electric hydration-straddle oracle](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle.opfs.spec.ts) +owns one #1754 host history: an on-demand Collection has an initial durable row; +an OPFS subset hydration is held after its local read; a PostgreSQL update +begins an Electric source transaction during that hold; and the row-bearing +commit is released only after the hydration scope exits. It checks the old row +before release and the exact new public and durable row afterward, then after +follower takeover and reopen. The fixture observes the on-demand run's claimed +storage ID and claim at coordinator registration; PostgreSQL supplies the +expected row independently. A late-buffer mutant leaves the old row in both +observations. The [core persistence tests](https://github.com/TanStack/db/blob/main/packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts) +own the adjacent during/straddling/after settlement, FIFO, abort, and failure +histories. The live oracle does not prove eager-startup overlap, other mutation +shapes, arbitrary Electric fetch schedules, Firefox/Zen, React rendering, or +exclusive OPFS ownership. The +[review record](oracle-reviews/issue-1754-live-electric-hydration-straddle.md) +preserves the live-host and mutant evidence. + +### Source durability inside a shared hydration scope + +The `hydration source durability routing` owner in +`packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts` now runs +144 bounded histories: startup/subscription/after-ready × scoped/unscheduled × +empty/shared-key baseline × twelve transaction templates. Its independent edit +log covers full upsert, same-key order/delete, truncate/replacement, row and +collection metadata set/delete, truly empty work, atomic multi-row batches, +independent pre-abort, and first/second durability failure. It checks each +acceptance and applied receipt separately, exact durable admission and prefixes, +sequence allocation, current scoped-adapter identity/lifetime, ordinary next +use, a second on-demand scope, and durable reopen. Four cleanup/restart histories +and one early-receipt checker calibration supplement the matrix. Existing +startup-read failure, request-local failed-read/retry, and optimistic causal +replay witnesses preserve no-scope, same-adapter and distinct-adapter paths. +Three callback-lifetime calibrations distinguish nested scopes and both overlap +exit orders. Forwarding-wrapper and explicit-adapter controls cover coordinator +composition and the ordinary registered fallback. + +The receiving fixed tests in +`packages/browser-db-sqlite-persistence/e2e/hydration-commit.opfs.spec.ts` +run 24 real Chromium OPFS histories plus one cleanup-failure calibration. +They have no oracle owner: their expected rows come from a written table keyed +by baseline and source history, not from a model. An oracle owner is tracked in +#2264. +It crosses the original phases/coordinators/baselines with single-insert and +rich source histories, and checks three distinct Collection schemas, a cold +peer, metadata, durable rows, and reopen. Sixteen startup/subscription histories +require exact owning-scope exit; eight after-ready controls have no held scope +and report that fact explicitly. The held cut waits for real peer hydration and +ordinary-write scheduling admission. Peer hydrate/regular callback entry and +C's commit SQL remain forbidden until the owning callback exits. Election +stream-position reads are deliberately outside that regular-work law. The tail +joins any truncate-triggered hydration, then must use the default coordinator's +public apply or the browser coordinator's regular scope exactly once. Cycle detection ties the same transaction +to the supplied live scope and its erroneous scheduled call; temporal overlap +alone is insufficient. Captured primary evidence survives cleanup failures. + +The default coordinator now uses the supplied scoped adapter for that call, +without caching it. The initial repair receipt on main at `a37e69ab` records +48 core routing assertion failures and eight real scheduler-cycle assertion +failures for the original production code. The latest review follow-up passes +640 persisted-oracle checks (with one existing todo) and 25 Chromium OPFS checks. +The original eight hostile repairs cover premature receipts, cached loans, +omitted data, reverse FIFO and suppressed/misclassified failure. New calibration +shows the old browser checks accepting cached loans and ordinary scheduling +bypass; the strengthened receiver rejects them at tail routing and the complete +scope boundary. A no-scope error-identity mutant is rejected by the restored +failure paths. The +[loss audit and exact content bindings](oracle-reviews/issue-2046-hydration-loss-audit.md) +preserve all source recoveries, decisions, commands, calibration and limits; +the [initial review](oracle-reviews/issue-2046-hydration-commit-deadlock.md) +retains the original issue ledger. + +- [ ] **Scoped lifecycle compositions.** The persisted-history owner needs + distinct-scope witnesses for dependent and post-publication aborts, + buffered partial-update recovery, row metadata from different owning + reads, arbitrary queued reloads, and late already-issued storage results + after replacement. Existing semantic owners remain authoritative. The + new queued-cleanup witness does not promise rollback of issued writes. +- [ ] **Nested work under fairness backlog.** The shared-driver fairness owner + (`tests/shared-driver-fairness-oracle.ts` in the browser package, with its + OPFS receiver) needs a nested source commit inside a hydrate while both + lanes retain a backlog that distinguishes K=1 from K=2. Finite peer + progress here does not establish that composition. +- [ ] **Elected-owner/host handoffs.** The per-Collection coordinator owner + retains transfer/cross-tab RPC while nested durability is pending. The + OPFS receiver owns provider-supplied physical failure and abort/cleanup + witnesses; other browsers and native hosts need their own receivers. + Distinct-schema isolation on this host is now checked. +- [ ] **Historical attribution.** A package bisect is still needed for the + earliest affected release; this work checks the stated `origin/main`. + +### SQLite boolean predicate arity and row work + +`packages/db-sqlite-persistence-core/tests/sqlite-boolean-arity-oracle.test.ts` +is the primary executable owner for bounded `and`/`or` predicate arity at +`SQLiteCorePersistenceAdapter.loadSubset` return. Its independent three-valued +model compares public keys while a `node:sqlite` driver records SQL and raw row +work. The 960-case matrix and paired fixed and unseeded campaigns cover root +arity 0–3, one nested operator, boolean and nonboolean operands, safe selective +predicates, and sound candidate supersets for unsafe predicates. Eight direct cases challenge Unicode `ilike` +and `like` patterns, tagged `NaN` comparison, lone-surrogate equality, and +null-vs-missing under `not`. Paired- and lone-surrogate equality controls +retain SQL row-work reduction. +An empty-IN control and two-SELECT cursor composition check zero-row work. + +The original compiler was RED for unary and empty boolean work and multioperand +under-selection. The first repair was RED for unsafe SQL prefilters: Unicode +case folding, an emoji spanning two JavaScript code units, tagged `NaN`, and +null-vs-missing under negation. A later lone-surrogate equality witness was RED +for public rows and SQL work. The compiler now applies SQLite JSON extraction +to the matching binding; paired and lone surrogates both keep indexed equality. +The current compiler passes the bounded oracle. +A removed-WHERE mutant fails the raw-row work law and a blind-unary mutant +fails public rows. For this grammar, SQL pushdown admits every row that the +in-memory evaluator could retain; a statically false AND may read zero rows +even with an unsafe sibling. +Mixed-AND witnesses retain a certified equality while an unsafe range, NOT, +or OR sibling stays with the row evaluator. They cover child order and one +nested AND, measure one raw row instead of twenty, and reject a removed-WHERE +mutant. The unsafe OR mutant loses a public row. +A fixed 80-level nested AND witness bounds compiler argument reads to linear +work. It was RED on the prior quadratic compiler and is GREEN after removing +the repeated subtree compilation; elapsed latency and arbitrary depth remain +outside this owner. +TimFL's 50,582-string-ID `AND(OR(IN, IN))` scope was RED for SQL row work: +the adapter returned the right public row but read all 20 stored rows. A fixed +real-SQLite witness now checks the public result, exact candidate count, two +bound JSON arrays, and use of both named expression indexes. It includes +lone-surrogate, NUL, and paired-surrogate string members. Mixed-type lists, +other SQLite hosts, and elapsed-time gains remain outside this witness. + +The Node expression-index oracle checks the receiving SQLite query, raw +candidate keys, public keys, and named-index plan. Its generated indexed +grammar uses string equality, BigInt membership, numeric range, and string +conjunction. Focused Date-epoch witnesses keep numeric equality and numeric +membership on the JavaScript fallback path: a stored `Date(1)` matches numeric +`1` in the row evaluator but is ISO text in the SQLite expression index. Fixed +lower-wrapped BigInt membership and lone-surrogate coalesce witnesses also +retain fallback: SQLite converts the former to text and encodes the latter's +inline fallback differently from a JSON-extracted binding. Integer-bound +range cases include adjacent fractional values and tagged or mixed scalars; +this is bounded evidence, not a proof over every persisted representation. +Large Number versus BigInt range cases check both operand directions, four +inequalities, and unary boolean wrappers. Unsafe BigInt bounds admit a wider +candidate range over the indexed expression for potentially rounded Numbers; JavaScript still +decides the public result. Numeric-key object and array cases check public +rows and named-index use through three digit segments, including mixed +carriers. Deeper numeric paths use an unbounded candidate read. Unicode +lowercase cases place NUL before and after a fold and keep the matching row +through either equality direction and unary wrapper. +The receiving Node plan uses the named index for the selective BigInt range +orientation in both operand orders. The opposite orientation can scan because +its candidate union also admits text and rounded Numbers; this oracle makes no +constant-index-use claim for that branch. +Fixed `strftime`, `add`, and Date-range witnesses also use full reads for mixed +stored types; direct SQL would exclude a matching numeric timestamp or string +concatenation. A typed-field contract or a separate indexed representation is +needed before those forms can gain a generally sound selective candidate. + +The owner does not establish arbitrary depth or arity, every equality or LIKE +pattern, limit/offset after filtering, all cursor shapes, order or value/metadata +semantics, index-expression compilation, native hosts, OPFS, or multi-process +execution. The existing SQLite owners above retain their separate reset/resume, +order, driver, and host contracts. Broader class closure needs witnesses at those +remaining paths. + +The optimistic history owner includes an external completion action. +`optimistic-history-outcomes-oracle.test.ts` runs264 bounded completion histories: +pending/persisting/completed/failed states,1–3 attempts, absent or independently +pending/persisting same/other-key peers and target/unowned/peer/truncate source +work. It preserves state, request outcome, public rows and waiter-time snapshots; +a later source write checks continued use. Full owner287 passes on858b174a6. +Deferred publication and missing admission guards fail at their intended checks. + +The real Offline settlement owner receives this law through60 serialized-outbox +restoration histories:2/3 transactions, every outcome mask, shared/disjoint +width1/2 and partial-overlap width2. A stateless prefix fold checks whole rows, +pending metadata, eager waiter snapshots, core restoration states, per-ID outcome, +provider calls and remaining outbox. It exposes14 unrepaired cleanup exceptions: +a successful durable replay attempts to complete a snapshot a sibling rollback +already failed. Known trace:shared or chain FT, FTF, TFT, FTT; shared also FFT. +The independent outbox result is successful; the core snapshot must remain failed. +214/220 declared cuts reach; six later cuts remain blocked by earlier RED. +Removing publication or deferring it fails public pending/waiter metadata even +on all-success histories. See the RC chunk10 record for exact logs and limits. +This is an open production class; repairs await completion of all oracle chunks. +The receiving scope excludes deletes, retries, terminal outbox phases, multiple +Collections, native durability and multi-owner timing. + +## Acceptance map + +The post-merge review added three missing domains to existing owners: + +- [Top-K batch contracts](https://github.com/TanStack/db/blob/main/packages/db-ivm/tests/operators/topk-batch-contract-oracle.test.ts) + cross sparse-array length/holes and RegExp source/flags/position with equal + controls, replacement order, hash consolidation, and actual retained graph + output. Ordinary replacements also run without the global `File` constructor. +- [Leadership replay](https://github.com/TanStack/db/blob/main/packages/offline-transactions/tests/leadership-replay-oracle.property.test.ts) + holds real storage-read delivery across successful and permanently rejected + durable removals, with bounded scans, concurrent loads, and unfinished peers. + This is distinct from exactly-once execution across independent owners. +- Settlement-time drop of optimistic state is owned by the + [optimistic history model](https://github.com/TanStack/db/blob/main/packages/db/tests/optimistic-history-oracle.ts) + and [state retention](https://github.com/TanStack/db/blob/main/packages/db/tests/collection-state-retention-oracle.property.test.ts). + A transaction's optimistic state drops when its mutation function settles. + Sync transactions committed while it persisted are held and publish with + that drop; `when('settled')` settles after that publication. Handler sync writes + are generated at every cut, with and without awaiting their acceptance. + Receipts stay pending until visible. With one persisting local mutation and no + truncate, the first queued same-key source transaction consumes local + attribution at settlement if that mutation succeeds. An absent-key delete + consumes it without leaving a row. Repeated same-key writes in that first + atomic source transaction keep its attribution. Later source transactions + are `'remote'` without another local owner. A truncate can publish while the + mutation remains active and label its same-key row `'local'` even if that + mutation later fails. Attribution uses key and timing; `SyncConfig.write` + has no causal client identity. A pending manual mutation does not hold source + writes; a same-key source row can retain local attribution after rollback. + A subscriber can commit a truncate and a later same-key transaction during + another drain. The suffix truncate can use the still-active mutation's + attribution. The later same-key transaction in that suffix drain is remote. + An active mutation on a key the + truncate does not write still attributes that key's first later transaction + in the same drain. Bounded histories also cross two + persisting same-key mutations, one or two source batches, both settlement + orders, and success/failure combinations. The first source batch is local + when at least one mutation succeeds; two failures leave it remote. A hostile + failed-sibling mutant fails the one-success cases at settlement. The pending + lane rejects a mutant that ignores pending mutations at its source or + settlement publication. Bounded manual histories now cross success, direct + rollback, and handler rejection with both optimistic visibility settings. + A later disjoint edit cannot supply the earlier manual request's handler + outcome. The original harness left rejected manual requests pending; the + new settlement check rejects that harness and a shared-handler variant. + Six pending-peer histories distinguish same-key rollback cascades from + secondary rollback and different-key controls, through public settlement + and publication. Longer manual interleavings and larger same-key cascades + remain outside these fixed histories. The original truncate implementation + failed the reentrant history at failed-mutation settlement. The earlier + accepted-snapshot retention law + and its truncate-capture ownership refinement were retired with this + contract. The + [PR #1907 review record](oracle-reviews/pr-1907-accepted-delete-ownership.md) + is historical. The [settlement-drop review record](oracle-reviews/2026-10-03-settlement-drop.md) + holds the RED/GREEN and mutant evidence. A handler that awaits a visibility receipt held by its own + transaction, such as an on-demand load of its own Collection, waits for + itself; that history is outside the generated grammar. + +Issue #2071 adds two focused checks to this acceptance map: + +- The [issue #2071 review](oracle-reviews/issue-2071-refused-insert.md) + checks a rejecting `onInsert` through the persisted wrapper. Four controlled + histories cross source insert/delete with acceptance before/after rejection. + They compare live rows, exposed base, durable rows, origin, and a held source + receipt at mutation settlement and after source application. The + [real SQLite receiver](https://github.com/TanStack/db/blob/main/packages/db-sqlite-persistence-core/tests/persisted-real-adapter-lifecycle.test.ts) + checks two same-key continuations, including a real source `update` after + reinsert. Each reopens the same file with a new + Collection, adapter, and SQLite driver. Both the reported release and this + review tree pass these bounded histories. The reporter's private bridge + sequence is unavailable; these witnesses do not establish its behavior, + other mutation kinds, on-demand sync, other SQLite hosts, or an OS process + restart. The persisted wrapper owner needs the bridge callback and write + sequence to reach that path. + +- The [optimistic publication owner](https://github.com/TanStack/db/blob/main/packages/db/tests/optimistic-history-publication-oracle.test.ts) + distinguishes one and two queued same-key source transactions during a + successful local mutation, two same-key writes inside one atomic transaction, + an absent-key delete before a source insert, a delete and reinsert in one + atomic source transaction, a truncate published before + a mutation fails, pending manual mutations, two overlapping same-key + mutations, a truncate followed by another same-key transaction, and a + truncate followed by a different-key transaction with an active mutation in + one reentrant drain. The persisted wrapper receives the one/two transaction + pair. The source grammar can now preserve mixed write order inside one + transaction; its atomic delete/reinsert witness is distinguished from a + later same-key transaction. These histories establish bounded key-and-timing + attribution; they do not establish every mixed operation sequence, causal + authorship, or every overlapping mutation schedule. A + causal `$origin` guarantee would require an explicit source signal and a new + core-owner model and provider receiving witness. The + [PR #2078 review](oracle-reviews/pr-2078-origin-attribution.md) records the + original same-drain RED, the repair, and hostile controls. The optimistic + history owner still needs controlled pending-plus-persisting same-key + histories, truncate with two active owners, and longer reentrant or mixed + source suffixes if those broader schedules are claimed. Such a witness must + compare origin and complete publication cuts, not only the final row. The + persisted wrapper owner still needs the private bridge callback and write + sequence before claiming that reported path. + +| Issue obligation | Implemented evidence | Limit | +| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | +| Metamorphic laws | Includes cross-formulation/partition, D2 independent-key commutation, DBSP incremental/full recomputation, pagination provider/UI boundaries, optimistic snapshot stability | Equivalence premises are explicit; not arbitrary query rewrites. | +| Public observations | Reads, exact event payloads and reconstructed state, observer eligibility, downstream includes, lifecycle and ownership checks | Count/work budgets are separate from row truth and only pin established promises. | +| Meaningful async histories | Applied receipts, truncate/replay, cleanup/restart, pending optimistic work, held provider completion, leadership loss | Control real causes; unsupported SDK traces receive no coverage credit. | +| Checker sensitivity | Faulty output controls, missing/duplicate events, stale completion/ownership controls, forced collisions and pre-fix runtime witnesses | Setup failures and timeouts are recorded separately from assertion kills. | +| Executed reach and replay | Named manifest with guarded replay, finite boundary products, pinned examples, fixed/random lanes and explicit stress runs | Root `test:oracles` is a selected core/Query DB campaign, not all repository oracles. | +| Contract and scope records | Guide, companion case notes, this map, suite-local law/domain comments and architecture | This is an executable testing method, not a completeness proof or new product specification. | + +## Running and replaying + +Build workspace dependencies before testing consumers of package exports. Use +the package's checked-in config; source aliases and native shims must be named +when they replace that path. + +```sh +pnpm --filter @tanstack/db-ivm build +pnpm --filter @tanstack/db build +pnpm --filter @tanstack/db test:oracles +pnpm --filter @tanstack/db-sqlite-persistence-core build +pnpm --filter @tanstack/node-db-sqlite-persistence test:oracles +# Service-dependent: requires Electric and PostgreSQL to be running. +pnpm --filter @tanstack/electric-db-collection test:e2e +pnpm run typecheck:tests +pnpm exec tsc --noEmit -p packages/db/tsconfig.json +pnpm --dir packages/db exec vitest run --coverage.enabled=false --maxWorkers=2 +pnpm --dir packages/db-ivm exec vitest run --coverage.enabled=false --maxWorkers=2 +pnpm --dir packages/offline-transactions exec vitest run --maxWorkers=2 +``` + +Core guarded replay is run from `packages/db`, for example: + +```sh +TANSTACK_DB_ORACLE_SEED=1813 TANSTACK_DB_ORACLE_PATH=0 \ +TANSTACK_DB_ORACLE_PROPERTY=live-query-observer.granular-history \ +node --import tsx tests/oracle-replay.ts \ + tests/live-query-observer-history-oracle.property.test.ts \ + -t 'granular listeners follow the eligibility ledger with a random or replayed seed' \ + --coverage.enabled=false --typecheck.enabled=false +``` + +Use the failure's actual seed/path for a reproduction. The example establishes +target execution, not reproduction of a particular bug. Local IVM and offline +properties have separate environment variables; inspect their test headers. +Do not assume the core multiplier reaches them. + +The Node expression-index oracle runs matching fixed-seed and seedless-random +campaigns by default. A path replay of its property selects only the +requested seed and shrink path: + +```sh +TANSTACK_DB_ORACLE_SEED=1659005 TANSTACK_DB_ORACLE_PATH=0 \ +TANSTACK_DB_ORACLE_PROPERTY=sqlite-persistence.expression-index \ +pnpm --filter @tanstack/node-db-sqlite-persistence test:oracles +``` + +Stress runs need an explicit file list, run budget, seed policy, runtime, exit +status and cost. For long synchronous campaigns, yield **between complete +histories**, never between an action and its synchronous observation. In this +project, worker progress RPC starvation produced passing assertions with a +nonzero process exit. Such a run is not green. Raising a test timeout alone +does not let the worker process its progress messages. + +## Action enforcement example + +`examples/react/action-enforcement/eslint-rules/no-direct-collection-mutations.test.js` +(fixed tests; no oracle owner (tracked in #2264)) covers the example's +file-local lint boundary. Its finite crossing covers 12 binding +forms, six methods, six call forms, and two callback definition orders, plus +lexical shadowing, sibling scopes, configurable imports/methods, and alias cycles. +Each expected count is written: a table gives one diagnostic for each write +method and none for each read, so the crossing is fixed tests, not a model +(maintainer decision 2026-10-27: a table of written expected values selected by +a test's parameters is not a model). The checkpoint is the +complete diagnostic list from the real ESLint `Linter.verify` call. A receiving +check runs the actual ESLint configuration on feature writes, feature reads, +and action writes. The import-source dimension checks eight alias/relative/barrel +spellings against four nearby unrelated paths, including the real configuration. +Three TypeScript import-equals forms must preserve other diagnostics without +crashing; static and escaped template members are paired with dynamic names. +On `7d3a3e85b`, the expanded suite fails 21 of 32 groups: the parser-node witness +throws while linting; the other failures are missing diagnostics. The repair +passes all 32 groups. A path-prefix mutant that includes `collections-extra` +fails three groups. A template-prefix mutant that treats interpolated names as +static fails the dynamic-name assertion. Both failures reach diagnostic +comparisons; neither is a setup failure. + +The original implementation (`3da274d8a`) failed 11 of the 18 test groups at +that checkpoint: +name-based scope pollution, declaration-order dependence, and missed destructuring. +The repair passes all groups. The example README states the conservative rule +for reassigned aliases. This oracle does not establish inter-module or type-based +tracking, function argument/return propagation, new object/array containers, +extracted mutation functions, or dynamic method names. Those are outside this +example's declared static scope; expanding it requires new syntax witnesses here. + +Guide evidence: ORC-001–005 and ORC-009 are stated beside the finite grammar, +model and driver. ORC-006 uses the original production rule as the rejected +wrong design; failures are diagnostic assertion mismatches, not setup failures. +ORC-013 pairs writes with reads and imported bindings with shadowing local values. +ORC-007 does not apply to this bounded enumeration; ORC-008 has no stateful model, +ORC-010 has no shrinking or resource cleanup, ORC-011 has no named shared semantic +fault requiring a second formulation, and ORC-014 is witnessed at the real ESLint configuration boundary; no browser +or backend-provider claim is made. +These outcomes supply ORC-012 evidence; the executable suite contains the law +and observations, rather than relying on this record for expected behavior. + +`examples/react/action-enforcement/tests/TodoApp.test.tsx` owns the example's +application input and pending-write controls. Its focused component witnesses +use real actions and the fake API with a virtual clock. They check unchanged, +newer, and empty drafts after successful adds; unchanged and newer drafts after +controlled create rejection; native form submission; and disabled toggles at +0, 40, and 119 ms during creation. A settled row remains enabled while another +row is pending, and a newly settled row can toggle and persist its change. +The original component fails the newer-draft, form, and three pending-row checks. +The fixed component passes these nine cases. The lint oracle above cannot observe +these application behaviors, and the shared framework owners do not own form +state. + +The component suite also owns add admission: one add may be pending per mounted +form, the input stays editable, and settlement permits the next submission. +Four controlled-create histories cross fulfillment/rejection with repeated +submits before/after a React render. They assert one action and provider call, +reject submissions of a newer draft while pending, then admit that draft after +settlement and check exact persisted counts. A fifth witness checks admission +after synchronous validation rejection. The original handler fails all four +pending-add histories. A guard based only on React state fails the same-render +histories; a mutant that omits guard cleanup fails subsequent admission. + +A controlled-toggle history also checks failure and retry in both completion +directions. It observes pending disablement, optimistic state, rollback, visible +error, retry error clearing, and final provider state. The original component +rolls back but fails the error-message assertion. This provider rejection is +injected at the component boundary; it does not claim a real backend emits it. +The same-path rejection probe found no unhandled rejection: Collection cleanup +already catches the persistence promise. + +Toggle tests select buttons by accessible role. A hidden-toggle mutant passes +the original tag selector but fails the scoped role query before interaction. +This checks exposure in the accessibility tree; it does not establish keyboard +focus or screen-reader behavior. + +These 15 focused cases do not establish arbitrary-length form histories, +remount behavior during pending writes, or browser-native Enter dispatch. +This component suite owns the first two extensions; a browser form witness +is needed for native keyboard dispatch. + +## Oracle tooling + +`scripts/run-budget-oracle.property.test.js` owns run-budget tracking +(`scripts.run-budget`): every lowered budget, and every budget whose key is in +no tracked oracle file at head, is reported. A budget belongs to the oracle +file that uses it, including campaigns and constants in the test modules that +it imports. The removal rule is the maintainer's decision of 2026-10-27; a move +into an imported support module is not a removal. Its grammar varies where the +count, key, and campaign are written at base and head, a base oracle file above +1 MiB, `.ts` and `.js` files, an untracked second oracle file, and a fixed seed +shared with an unrelated campaign. Its driver fails on a crashed script. It +does not establish that a lowered budget moved between two oracle files is +compared when its fixed seed was already in the destination at base: a fixed +seed is not unique, so the script cannot attribute it, and neither removal nor +lowering is reported there. A budget moved into an ordinary test file whose +seed another oracle file shares is therefore not reported. + +A budget that a helper computes at run time is not tracked either: the script +reads the counts and keys written at a campaign's call, or in constants that the +oracle file declares or imports. The op-sqlite alias campaigns in + `packages/react-native-db-sqlite-persistence/tests/op-sqlite-driver-oracle.test.ts` +(`op-sqlite.write-marker-aliases` and `op-sqlite.columnar-sql-aliases`, 50 runs +each) register through `registerAliasOracle`, which builds its parameters from +`readAliasOracleRuns()` and `TANSTACK_DB_OP_SQLITE_ORACLE_RUNS`, so lowering +them is not reported. On main the script instead counted the literal +`oracleRandomParameters(50, …)` calls in a unit test of those parameters. That +unit test now lives in `op-sqlite-driver.test.ts`, so the script reports both +keys and the fixed seed 271828 as removed, although both campaigns still run in +the oracle file. Owner: `scripts.run-budget`. Needed witness: a campaign whose +budget a helper computes and passes in as parameters. + +`scripts/lint-scope-oracle.property.test.js` owns lint selection after an +edit (`scripts.lint-scope`): the changed scope, the named (pre-commit) scope, +and the Claude edit and stop hooks. Its model is a fixed-point closure over the +drawn import graph, independent of the script's forward search. It covers +oracle-to-oracle imports, untracked oracle files, and `.js` oracle files. It +also covers the checkout rule from #2280. A session works in the launch +checkout or in a worktree nested under its `.worktrees/`. The hooks run the +launch checkout's script with the session checkout as the input's `cwd`. They +must select that checkout's files, and they must report through that +checkout's own copy of the rule. A plain scratch directory under `.worktrees/` +must produce no report. The repository's `.gitignore` is part of the tested +configuration. It does not draw `.js` support modules or drivers, although the +rule now resolves a `.js` test module when no TypeScript source exists for it +(the part-reach oracle owns that resolution). It passes file names in their +real-path spelling: a name given through a symlinked path is not covered. It +does not generate a hook input without `cwd`, a worktree outside the launch +checkout, or a worktree branch that has no copy of the script. In that last +case the launch checkout's rule lints the worktree. + +`packages/db/tests/oracle-replay.test.ts` checks every replay manifest owner: +from its cwd, the command's files exist, its target is the owner, and a Vitest +config applies. + +## Reusable-law backlog + +RFC #1659 reviews found several green oracles whose stated laws remained valid +but whose fixtures, grammars, or observations did not cover a neighboring +boundary. Track the generalized repairs here instead of accumulating isolated +regressions. Completion requires an executable owner, a production-path witness, +a hostile wrong-answer control, and an explicit statement of remaining limits. + +- [ ] **Offset-only providers under a joined ordered window.** A provider that + honors `offset` and ignores `cursor` gets a wrong window at preload under a + LEFT-joined filter: the ordered loader repeats a continuation `offset` + capped at the window's `offset + limit` (`countAcquiredRows()`), so the + provider serves the same page again. Witness: ranks 2, 1, 0 with only rank + 0 marked and limit 1 leaves the window empty (an `it.fails` case in the + Query DB ordered refetch oracle). Owner: the ordered-work oracle + (`packages/db/tests/query/ordered-work-oracle.property.test.ts`) needs an + offset-honoring provider; the Query DB ordered refetch owner then adds the + offset encoding to its generated campaigns. +- [ ] **Bounded recovery after a failed ordered request.** The maintainer + decided that full-source reads should be avoided whenever a bounded read + is available. A failed page, tie, or bounded repair, and an invalidation + before any request has settled, still recover with a full-source read. + A first attempt to make failures recover with a bounded prefix repair + broke 48 pinned expectations and showed real regressions: a wrong window + (`[1, 4]`, expected `[1, 2]`) after a failed tie request, changed lease + release after a failed tie, and settlement mismatches in the pagination + pending-history campaigns. Owner: the ordered-source-loader and pagination + oracles; the change needs its own oracle work before production changes. +- [ ] **A page-capping provider during an ordered repair.** A provider may + return fewer rows than a request's `limit`. Under a joined, offset + window, a row whose order changes can then drop out of the window after + a refetch, through the loader's own repair; it reproduces on main. + Witness: the capped-provider `it.fails` case in the Query DB ordered + refetch oracle. Owner: the ordered-work oracle needs a page-capping + provider; the Query DB ordered refetch owner then adds page caps to its + generated campaigns. +- [ ] **Refill after a joined-side removal when a continuation adds no rows.** + With a current provider, unmarking a window row makes the loader request a + continuation that returns only rows it already holds, then stop with an + underfilled window instead of continuing from the new boundary. Witness: + the `it.fails` case in the Query DB ordered refetch oracle (window `[5]`, + expected `[5, 1]`); it reproduces on main. Owner: the ordered-work oracle; + the Query DB ordered refetch owner then checks the window after marker + flips while the provider is current. +- [ ] **Accepted sync transactions across persistence reads (partial coverage).** + The persistence-history owner retains the original 24 histories: + insert/partial-update, one/two/three queued reads, unreserved/reserved + commit turns, and an absent/present dependent successor. They check applied + receipts, public/durable rows, metadata, cursor, and durable source order. + Eight adjacent histories begin during an owning persistence read and commit + after it finishes. They vary original key membership, commit-turn reservation, + and one/two later reads. Captured row metadata ownership survives acceptance. + The maintainer specified that persisted rows are an older baseline for + newer sync-adapter changes; an internal read cannot revoke an accepted + sync transaction. Ninety additional histories compare queued and completed + reads against the same independent snapshot algebra. They cross + insert/update/delete, no/before/after truncate replay, on-demand subset + demand, and coordinator reset/full-reload notifications in eager and + on-demand Collections. Exact settled rows, row metadata, opaque cursor + metadata, status, applied receipts, loaded rows and durable completion order + distinguish source precedence from truncate replay's ordered replacement. + The old guard fails all 45 queued cells; serial controls pass. Omitting + public truncate fails 60 cells; omitting public delete fails 10. + Owner: `packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts`. + The [original review](oracle-reviews/issue-2036-queued-hydration-publication.md) + and [admission follow-up](oracle-reviews/issue-2036-admission-follow-up.md) + preserve revision receipts, review dispositions and oracle-guide audits. + Further witnesses must compose these schedules with cancellation, cleanup, + or persistence-read failure. Existing rejection of still-open transactions + across persistence reads remains a characterized admission restriction; + its independent authority argument is not established by these tests. + The notification fixtures assume a compatible persisted baseline. They do + not establish behavior after schema changes or invalidated resume points, + or each intermediate publication during a multi-transaction truncate replay. + The same owner needs those histories before claiming those stronger laws. + Reserved commits (#2226). The same driver and model also own the + reserved-commit law: a persisted hydration that would start while a + reserved sync transaction is open waits until it ends, and the commit is + accepted. Authority: `reserveCommitTurn` in `packages/db/src/types.ts`, + widened by maintainer decision on #2226 to every persisted hydration. + Further maintainer decisions fix its edges. Readers keep the rows from + before a hydration while it waits; a transaction opened after a + hydration began straddles it. A notice that waited may be retried as + gap recovery. A tab asks the leader only to fill a gap it has not + already filled, and sends no added upstream request. Each reservation + a notice waited for can open one gap, so the oracle checks at most one + more pullSince request per wait (maintainer decision on PR #2265). A source that stops for good abandons its + reservation. + A second registration crosses the routes with the reserving timings, + ending, and follow-up dimensions. The routes are subset, complete + subset, a follower's routed subset, the source's own baseline, + reset, full reload, sequence gap with or without pullSince, sequence + gap whose pullSince deltas reload partway through (a targeted delta, + then one past the targeted-invalidation limit), and a targeted change + that starts no hydration. Gap recovery is a hydration cycle only from + the point where it will change rows (maintainer decision on PR #2265): + after pullSince answers, or at its reload without pullSince. A + reservation opened after that point straddles it; one opened during + pullSince makes it wait, and a transaction that did not reserve may + commit during pullSince. A peer reset abandons a reserved transaction + that began before it (maintainer decision on PR #2265): the commit + rejects and the reset does not wait. The reserving timings are + before a subset load's admission, during gap recovery's pullSince + (with or without a reservation), across the mutex turn, across scope + entry, during the read, in the route's first publication, and across + startup. A load that waits for a reservation runs after it ends, + whether it commits or is abandoned (maintainer decision on PR #2265); + on `851e8d9da` the abandoned admission cells failed with a rejected + load. The ending is commits, + abandoned, cleanup, an aborted demand, or the source awaiting its own + baseline. The follow-up is a reset then a pre-reset commit, or a + reset, a new reservation, and then the pre-reset commit. A reservation + that a reset abandoned cannot reserve again; on `7a6f32fd7` 8 + re-reserve cells failed because a later hydration waited for the + abandoned transaction. An aborted demand still waiting for its turn is + no longer active demand. It receives the reset follow-up, or a peer's + targeted change for a row no live demand covers; nothing reads or asks + the leader for it. On `7a6f32fd7`, 12 of the 24 aborted-demand cells + fail: the reset's reload read for it, and the peer change sent a + pullSince past the bound. Filtering aborted demand only from reloads + still fails the 4 peer-change cells that wait at the mutex turn or + scope entry. + Observations: receipts, final rows, row metadata, cursor, status, + reads during the reservation, upstream requests, pullSince count, + rows while waiting, and every read against the fold of the durable + events before it. Every cell declares reach (ORC-015). + | Evidence | Result | + | --- | --- | + | Row-algebra cross and reservation cross | 677 cells pass. | + | Peer reset against an older reservation, on `2047f4281` | 53 cells fail: the reset waited and the stale commit landed in the new epoch. Abandoning only at the reset's turn fails 6 follow-up cells; abandoning only on arrival fails 40 route cells. | + | Gap recovery's cycle start, on `2047f4281` | 12 pullSince cells fail: an unreserved commit during pullSince rejected, and a reservation opened then straddled. Dropping the reservation check after pullSince fails 8. | + | Gap recovery that reloads partway through, on `851e8d9da` | 28 cells fail: recovery advanced the stream position and applied a targeted delta before it deferred, so its retry skipped the reload. A gate only at recovery start still fails 15 scope-entry cells; reloads that start their own cycle inside recovery fail 61. | + | `5649a07d4` | The open cells fail with `a persisted sync transaction cannot cross a hydration cycle`. Three `after read` reset follow-up cells also fail, because `main` applied a pre-reset commit before the reset set its term fence. Queueing resets with notices fixes this pre-existing defect. | + | Hostile mutants | Each mutant fails its cells, and every failure is an assertion. Deferring every notice fails the targeted change. Dropping the follower reply's wait fails the routed subset. Reporting hydration start before the check fails the aborted demand. A reset reload that starts its own cycle fails the first-publication reset. Applying a reset outside the notice queue fails the reset follow-ups. Retrying a waited notice while the reservation is open fails the follow-up and routed cells (repeated pullSince). Checking only at the mutex turn fails every scope-entry cell. Gating subset hydration only fails every reset, reload, and gap cell. Requeueing a waited notice behind later notices fails 12 reset follow-up cells: the pre-reset commit is published before its reset, and a reload is lost. | + A routed load also judges law 7 at its settlement: the owner's row is + public when the load settles. A reservation that ends between the + follower's flush and its wait check is a one-microtask window no + timing reaches; a temporary hook that ended the reservation there + showed the old wait loop settling early in 4 straddled cells and the + current loop keeping the owner's row public. A new reservation that + opens between the loop's wait and its flush defers the owner's + broadcast again. The loop checks for a reservation after each flush; a + temporary hook that opened one there showed the loop on `7a6f32fd7` + settling the routed load in 2 cells before the owner's row was public. + A third registration reaches it without a hook. The owner's reply is + held while its broadcast waits; the source commits 0 to 8 microtasks + after the reply and reserves an empty second transaction 0 to 12 + microtasks later. On `7a6f32fd7` the 12 cells that commit 3 microtasks + after the reply settle the routed load before the owner's row is + public; all 117 cells pass on the current loop. A held write after the + commit does not open the window, because a reservation lasts through + its own durable write. + A second reservation that opens 7 or more microtasks after the commit + holds the broadcast twice, and the notice asks the leader once per + wait, as the bound allows. A peer reset for a + storage generation this Collection no longer uses abandons nothing. + Known limit (maintainer decision on PR #2265): Electric publishes a + transaction's txids as stream evidence before persistence accepts its + commit, so `awaitTxId` can resolve for a commit that a peer reset or a + terminal failure later rejects; the source then fails, and the + Collection's health node records it. + Known limit (maintainer decision on PR #2265): no fairness rule. A + hydration released by a commit waits again for a reservation that + opens before its next mutex turn, so a source that reserves back to + back can delay it; Electric reserves across batches only during an + initial sync or refetch. + Not covered: `forceReloadSubset`, which only a sync-absent Collection + has, and which therefore never meets a reservation; startup hydration, + which source entry follows (an `open across startup` observation); + and row visibility for on-demand Collections without demand beyond the + source's own rows. Cleaned-up cells judge only cleanup. + Real SQLite adapter: + `packages/db-sqlite-persistence-core/tests/reserved-commit-sqlite-adapter-oracle.test.ts` + crosses three routes (peer full reload, peer reset, and a subset + admitted earlier) with two timings (a held write of this Collection, + or the shared scheduler holding the scope after its turn) and two + endings (commits, abandoned). It runs on `SQLiteCorePersistenceAdapter`. + It also judges that a subset load that waited settles successfully. + Its peer-reset cells judge that the reset abandons the reservation: + the commit rejects and row-1 keeps its durable value (both committing + peer-reset cells fail on `2047f4281`). + A nesting dimension adds a second reserved transaction inside the + first. A peer reset must abandon both, so both reject with the same + abort; on `5ac5a8711` the inner one rejected with the cross-cycle + error instead. Its 20 cells pass. + Every committing single-transaction cell fails on `5649a07d4`. A + mutant that rejects the waited load after the reservation fails the 4 + subset cells; before the load's outcome was judged, it passed. A peer + full reload behind this Collection's own held write cannot become + durable through the single SQLite writer, so it is excluded here and + left to the grammar's fixture leader. + Browser host: the multi-tab Electric oracle in + `browser-db-sqlite-persistence` crosses two or three tabs, eager or + on-demand Collections, which tab finishes first, a storm or staggered + arrival, the last tab's open reserved transaction (its refetch, or an + ordinary batch after it resumed from the persisted position), how its + source stops for good (an error that `onError` does not retry, the SDK + giving up, a signal abort, cleanup, or resume invalidation), and how + the application recovers it (restart in the page, or reload). It + judges every tab's status once every source has delivered or stopped, + and again after recovery. Eager cells also compare rows at both + checkpoints, except a cleaned-up tab before recovery. It also judges the + full-snapshot request after resume invalidation. A Collection in + `error` keeps showing durable rows (maintainer decision on PR #2265), + so the tab whose source resume invalidation failed must show its + peers' refetch; it stays `ready` with an error on its health node. Its 290 storm cells pass. Every ordinary storm + cell fails on `5649a07d4`. Removing Electric's abandon on the `onError`, + give-up, or signal-abort path fails 12 cells each, and on the resume + invalidation path 16 cells. Cleanup's abandon mutant survives here, + because the persisted wrapper ends its transactions on cleanup; the + Electric descriptor-isolation oracle kills it without persistence. + On-demand cells judge receipts and status only. + Generated histories in the same file interleave server changes, + deploys, partial and complete deliveries, every stop except resume + invalidation, restarts, and reloads across two or three eager tabs + (fixed seed 952051961 and a random seed, 80 runs each; the shared + `TANSTACK_DB_ORACLE_SEED`, `_PATH`, and `_PROPERTY= + browser.multi-tab-reserved-history` replay one). The + grammar constructs a 24-step prefix before its unchanged 8–24 arbitrary + steps. Every stop kind occurs in a generated order: a source opens a + transaction, stops, lets a distinct peer commit, then recovers and catches + up. Source tab, initially absent key, and recovery method vary. Four + membership toggles restore the original rows before the arbitrary tail. + This makes stop/peer reach structural; an earlier CI random campaign + missed the SDK-give-up boundary with the unconstrained shuffle. + The model folds committed batches into the expected durable rows. After + every step it judges the durable rows, every Collection status, and + the rows of each tab with no open transaction; after every tab catches + up it judges them again. A Collection status changes only through its + own source (maintainer decision on PR #2265): a peer's commit leaves a + `loading` or failed tab as it is. That law failed on `main` (a peer's + truncate reload marked such a tab ready); the fixed seed fails without + the repair. Removing the reservation gate, or the `onError`, give-up, + or signal-abort abandon, fails both campaigns. Each declared premise, + including a peer commit after each kind of stop mid-transaction, is + reached in every campaign. Known gap #2267 is left out of the + generated histories and pinned with `it.fails`: a resumed tab that lags + behind a peer can receive an update for a row the peer already + deleted, and Electric's resume check puts it in `error`. + The published Electric resume-safety contract requires rejecting an + unknown partial row. Whether composition must avoid that condition + without failing the source remains a separate contract question; + this repair does not remove the guard or resolve the pinned expectation. + The histories also post a peer `collection:reset` on the tabs' + coordinator channel; no shipped host sends one yet, so this premise is + controlled. The reset abandons every open reserved transaction, and + Electric's next commit then fails the source until the tab restarts + (maintainer decision on PR #2265): a ready tab keeps `ready` with an + error on its health node, which the histories judge, and a loading tab + goes to `error` (#2168). A reset that waits and + accepts the stale commit fails both campaigns. + Electric's updates carry only changed columns by default, so an update + for a row the tab does not hold applies nothing; the fold follows that + rule (a CI random campaign found the history where a lagging tab's + update follows a peer's delete). + A delivery may also commit only a prefix of what a stream owes, as a + server answering at an earlier moment does, so lagging commits are + generated. The final checkpoint requires the server head (Law 2). + Known gap #2209 is one history that breaks it: a lagging tab + commits an older row and closes for good. Generated histories never + close a tab for good, and an `it.fails` replay through the same driver + and checks pins that history. + The [#2281 repair record](oracle-reviews/issue-2281-investigation.md) + retains the row-loss and resurrection counterexamples on `82c5f999b`. + A lagging source changed rows; another source advanced the shared resume + position over the missing change. The eager Electric/SQLite bridge now + detects a discontinuous predecessor atomically with rows and metadata. + Source continuity remains incompatible until an authoritative replacement; + the physical key ledger stays independent, so cached rows remain visible. + Reopening a missing, foreign, or discontinuous source token requests a + fresh snapshot. The original row-loss replay and both six-step membership + replays now pass. The generated `browser.electric-source-prefix` property + runs 80 histories with seed 2281 and 80 without a seed, with direct + seed/path replay. It crosses initial membership, one to three toggle + pairs, swapped source roles, empty/disjoint advancement, drain/no-drain, + tab reload or source failure followed by same-Collection restart, and + current/missing/foreign progress. Caller positions vary between omitted, + initial `-1`, handle-only, numeric offset with handle, and `now`. + Declared reach + requires all four divergent membership/advancement cells, a draining + control, missing/foreign provenance, and divergent recovery through both + actual lifecycle paths. It also requires divergent recovery with explicit + numeric and `now` positions, plus initial-offset and handle-only controls. + Both Browser random registrations and the SQLite + source-progress registration stand aside during another property's path + replay. The SQLite owner separately + checks opaque predecessor paths, sticky gaps, duplicate endpoints, + replacement repair, and additive schema migration (80 fixed/random + histories each). Disabling gap retention fails both owners; rejecting + duplicate endpoints fails the SQLite law. Bypassing startup provenance + validation fails the Browser owner. + The real installed SDK/HTTP/PostgreSQL/Chromium OPFS receiving driver + `e2e/electric-source-prefix-oracle.opfs.spec.ts` shares the source-head + judgment. The original ten cells cover both membership directions, empty/disjoint + advancement and drain/no-drain, plus missing legacy provenance with + disjoint advancement. Each declares held earlier response, stale commit, + later advancement, and reopened source comparison. All ten pass; gap and + legacy-validation mutants fail the matching real disjoint cells at that + comparison. Ten more cells cross numeric and `now` caller positions with + both membership directions and current/legacy provenance, plus initial + offset and handle-only controls. Eight wrong-row comparisons were RED on + `883b565ab` before the eager-only repair; all ten explicit cells now pass. + Each explicit cell declares its caller-position reach. A mutant that + requests recovery but preserves the caller position fails the final Node + source-head comparison (seed 2281, path `1:0:0`). Explicit positions cannot + bypass eager source-progress recovery. Other sync modes and explicit + malformed/tag/shape regimes retain their earlier behavior and are + outside this added evidence. Real foreign-token provenance and arbitrary + changes of caller form still need separate receiving witnesses. + Each of the source-prefix property's 80 generated inputs now runs both + its original history and an interrupted-recovery neighbor (160 histories + per campaign). The latter starts recovery without a caller cursor, stops + before any source delivery, and reopens with the generated caller form. + Actual persisted reset markers before divergent numeric/`now` recovery + are required reaches: fixed/random counts are 10/6 and 7/8. Both campaigns + fail on `623c59955` before the eager-only reset fix, and pass afterward. + The real matrix adds ten interrupted histories across both membership + directions and all five caller forms. A held first SDK response prevents + replacement publication; the driver observes the durable reset before + closing that run. Numeric/`now` give four wrong-row RED comparisons; + six neighboring controls pass. All thirty real cells pass after the fix. + The reset marker now requires eager recovery and clears the effective + caller position. No expectation, model rule, or comparison is relaxed. + This is bounded same-version eager Browser evidence, not + arbitrary scheduling, mixed client versions, native-device, Electron, + on-demand, optimistic, or monotonic-live-write coverage. + The final checkpoint now observes a reset-abandoned commit's source + failure, restarts it, and only then judges catch-up. The original reset + trace (seed `-1115798760`, path `1:4:3:3:3:3:3:3:3:3:3`) and the later + no-deploy reset trace (seed `-1371495859`, path + `75:0:0:0:0:0:0:1:1:0:0`) pass on unchanged production with that repaired + checkpoint. Both remain fixed inputs to the shared driver. A generated + reach separately requires recovery first discovered during that final + delivery, so an earlier reset failure cannot satisfy this premise. + It reached 2 of 80 fixed-seed histories and 6 of 80 random histories. + Removing the final restart makes both reset traces fail the final row + comparison. The broader Node campaign can also log a leadership term + write against a closed test database during teardown (pilot `PILOT/S02`, + fixed seed `952051961`). This is distinct from injected stream errors; + its user-facing effect is unverified. Owners: broadcast coordination and + this host driver. Needed witness: hold the election term write across + coordinator disposal and database close, then observe its settlement + and resource release on controlled and real hosts. Passing convergence + comparisons do not establish clean teardown. + A generated cached-row regression also preserves intermediate observations during + source-continuity repair. #2209's permanent-close case and #2267's + resumed-update gap remain pinned expected failures under this owner. + A claimed leftover-replay/reset failure still needs a concrete schedule; + the bounded two-replay-then-reset probe drains replay before reset. + Live Electric/browser OPFS delivery needs the matching schedule in + `packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle.opfs.spec.ts`; + its current ordering does not establish this handoff. + These are bounded witnesses, not closure of arbitrary interleavings. + +- [ ] **Optimizer residual convergence beyond the checked paths.** The + optimizer-semantics owner now checks `sourceWhereClauses`, source-free + predicates, namespace-only predicates, later RIGHT/FULL join nullability, + and an ordered QueryRef refusal at structural checkpoints. It still needs + public-row witnesses for later RIGHT/FULL chains, residual checks for + aggregate, functional, and projection-remapped QueryRef refusals, a + separate UNION branch model, and incremental source/publication histories. + Owner: `packages/db/tests/query/optimizer-semantics-oracle.test.ts` and + its production compiler driver. Each extension needs a distinguishing + wrong-plan control at the relevant checkpoint. + +- [x] **Real-provider conformance fixtures.** Frozen 15.2.7 React Native and + Node receipts cover the supported peer version; 18.2.1 React Native, Node, + and browser receipts cover the known forward shapes. Exact-row checks and a + row-dropping hostile control prove the shim accepts those envelopes without + mutating them. Owner: + `packages/react-native-db-sqlite-persistence/tests/fixtures/op-sqlite-provider-results.ts` + and `packages/react-native-db-sqlite-persistence/tests/op-sqlite-driver-oracle.test.ts`. + Native device/host execution remains a separate runtime receipt. +- [x] **Minimal ambiguity and name invariance.** Generate one-field and + otherwise minimally distinguishable results. Renaming a selected column to a + structural-looking alias must not turn a data row into a write envelope. + Owner: `packages/react-native-db-sqlite-persistence/tests/op-sqlite-driver-oracle.test.ts`. +- [x] **Carrier coexistence and representation symmetry.** Cross `rows`, + `rawRows`, `columnNames`, and supported result containers, including legal + coexistence. Equivalent array and object forms must agree on rows or on the + documented rejection. Owner: + `packages/react-native-db-sqlite-persistence/tests/op-sqlite-driver-oracle.test.ts` + and the shared SQLite driver contract. +- [x] **Shared-handle transaction admission.** Concurrent driver wrappers for + one provider database handle serialize root transactions. Nested work must + use the transaction driver supplied to the callback on hosts without async + context propagation. Owners: + `packages/node-db-sqlite-persistence/tests/node-driver.test.ts` and + `packages/react-native-db-sqlite-persistence/tests/op-sqlite-driver-oracle.test.ts`. +- [x] **Transitions at every relevant await.** Hold each coordination boundary, + then change leadership, remote-subset ownership, abort state, cleanup, or + restart generation before release. Owners: browser/electron coordinator, + persisted-history, and collection cleanup/restart oracles. +- [x] **Local-versus-transport refinement.** Compare local-leader and transported + remote-subset behavior for immutable values. Separately prove that local + `signal` and `subscription` references survive delivery and reach matching + unload cleanup. Owners: browser/electron coordinator and persisted-history + suites. +- [x] **Partial-construction cleanup.** Fail database, driver, worker, and + subscription construction after each acquired resource. Preserve the primary + failure while proving all acquired resources are released exactly once. + Owners: OP-SQLite driver-contract construction and OPFS page/worker lifecycle + suites. +- [x] **On-demand persistence after evidence changes.** Cross baseline versus + on-demand hydration with consistent, unknown, and incompatible key-set + evidence. A baseline certification failure must not silently erase valid + on-demand rows. `loadSubset` checks baseline visibility and on-demand rows; + the sync-absent `forceReloadSubset` route crosses the same startup evidence + states but records baseline visibility as unobserved because that API does + not expose baseline hydration. Owner: + `packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts`. +- [x] **Deterministic value-and-work laws.** Pair row correctness with stable + statement, scan, trigger, or queue-cardinality observations where the + subsystem promises bounded work. Owners: SQLite resume snapshots, Electric + acquisition work, shared-driver scheduling suites, and the Node + same-database-handle admission law. +- [x] **Startup generation interleavings.** Hold persisted startup between its + metadata and hydration snapshots, then cross no write, a managed mutation, + and hostile raw loss. Compare public rows with the atomic durable snapshot, + and require mutation persistence to wait until the prior stream position is + known. Owners: SQLite resume snapshots and Electric resume snapshot races. +- [x] **Schema-generation fences on cached adapters.** Cross a newer-schema + reset with snapshot, row, metadata, delta, position, and index-lifecycle + operations from the cached older adapter. Every stale operation rejects; + the current adapter remains readable and its index registry remains intact. + Owners: SQLite resume snapshots plus browser/electron coordinator routing. +- [x] **Explicit omission records.** Add a short `Known omissions` section to + each primary executable owner touched above and keep this map synchronized as + laws land. An omission record narrows evidence; it does not waive a product + obligation. +- [ ] **Atomic active-subset full reload.** Load collection metadata and every + active subset from one adapter generation, including filtered and paginated + on-demand subsets. A sound implementation needs an atomic multi-subset API; + loading all rows or accepting a metadata/row torn pair is not equivalent. + RED evidence and the deferred executable placeholder live in + `packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts` under R5-007. + +- [x] **Joined aggregate and `singleResult` subquery histories.** + `packages/db/tests/query/subquery-user-value-oracle.test.ts` checks flat and + nested aggregate source changes and joined `findOne()` QueryRefs at initial + and later public checkpoints. The bounded histories and remaining forms are + recorded in the QueryRef owner row above. + +## Persisted readiness and network-first initial rendering + +The persisted-readiness oracle in +`packages/db-sqlite-persistence-core/tests/persisted-readiness-oracle.test.ts` +owns when a persisted Collection with network-first initial rendering opens +from restored rows. A small model enumerates every ordering of the restore +outcome (rows, empty, failed), the remote answer (ready, ready followed at once +by `reportError()` or by `fail()`, fail, none), a later `reportError()` then +`fail()` in one tick, and the deadline, each with quiet or throwing ready +effects, 390 histories. After every +event it compares Collection status, the node's `error` and `failureReason` +messages, the visible rows while `ready`, uncaught errors, and the status of a live query over +the Collection. The multi-call histories check that the wrapper delivers the +adapter's calls to core in the order it made them; a throwing `onFirstReady` callback or `status:ready` listener surfaces as uncaught and the Collection stays `ready`. The laws: restored rows open the Collection once the +deadline passes or the remote fails; an empty restore stays `loading`; a remote +answer before the restore completes waits for it; a failed restore crashes. +Fixed tests in +`packages/db-sqlite-persistence-core/tests/persisted-readiness.test.ts` +(fixed tests; no oracle owner (tracked in #2264)) check that a Collection +without `initialRender` waits for the +network, the three-second default deadline, configuration rejection, and that +`reportError()` before ready still crashes; `markReady()` after a ready +Collection's `fail()` is a no-op. Mutants that let an empty restore open, +skip the deadline, skip the remote-failure opening, let `fail()` bypass the +call queue, crash on a throwing ready effect, or report every `markReady()` +throw on a ready Collection as uncaught each fail the oracle. + +This owner uses a controlled persistence adapter, not native SQLite or a real +network provider. Restart, cross-tab coordination, a remote that stalls without +an answer, and a Collection that synced to zero rows before (it waits like an +empty restore) are outside it. Hooks read Collection status only, so the +framework conformance suite's status scenarios cover the binding side. + +The persisted demand coverage oracle in +`packages/db-sqlite-persistence-core/tests/persisted-demand-coverage-oracle.test.ts` +owns the on-demand law: an opted-in wrapper 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. Its bounded enumeration +crosses twelve session-1 histories (not requested, failed, overlapping +filter, same filter with another window, same filter with another order, +rejected durable row write, settled empty, settled, settled then truncated, +three in which a reset lands after the 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 one in which the source +deletes a row of D after D settles), two waits, and six +session-2 upstream schedules (pending, early and late failure, early and late +answer, late empty answer), over the real wrapper and +`SQLiteCorePersistenceAdapter` on `node:sqlite`. Session 1 creates coverage +only through real loads, and a positive witness checks whether it sent the +session-2 demand key. A pure trace model predicts status, `preload()` state, +and exact rows at five checkpoints. A fixed lifecycle history checks that each +load measures its own wait and settles on its own restored rows, that +release aborts the upstream load, and that coverage survives release. A joined history checks that a +query over an eager and an on-demand source is ready only when the on-demand +demand is covered, read directly and through an outer live query, with the +joined row visible when ready. A third shape reaches the on-demand source +through two live queries; covering only the inner query's demand must keep +the query loading. +Two fixed same-session histories check that a load of D after a cache +rotation, and a `refetch: true` load of D, both wait for the network. Model-level calibration shows that eighteen wrong designs (fifteen +single-demand, two lifecycle, one joined) each differ from the law at a named +case. Before the implementation, every +production case failed at setup because `initialRender` rejected +`syncMode: 'on-demand'`; without the opt-in, that code matched every +uncovered cell and diverged at the first checkpoint where the law applies for +every covered cell except an early network answer. With the implementation, +every case passes; the [implementation review](oracle-reviews/2026-10-21-persisted-demand-coverage-review.md) records RED, GREEN, and each mutant's outcome. Production mutants that ignore coverage, ignore or halve +the wait, error a covered demand on upstream failure, write coverage before +the load settles, measure the wait from the sync run, keep coverage across a +truncate, let an on-demand opt-in permit fallback alone, or read the root +query's demands instead of the owning inner query's each fail named cases. +A review then found three more defects, and the in-flight histories and the +two-reader shape reproduced each one on unchanged production: an obsolete load +recorded coverage after a truncate or rotation (22 cells), a coverage record +during pending recovery failed the session-1 load (12 cells), and the first +live query reached on a source stood for every reader (one cell). After the +repair, mutants that restore each defect, or that check only for a truncate, +fail those cases. A third review round added the row-deletion history, the +source-failure history, and the rotation and refetch histories; on the parent +production code they failed 14 cases by assertion, and the Query DB witness +failed its retention history. Mutants that drop the row count, require more +rows than recorded, ignore the cache generation, let a refetch settle on +coverage, or let a network failure bypass on-demand settlement each fail +those cases; a gate that ignores the +eager deadline fails the persisted-readiness oracle instead. The Electric +progressive witness in +`packages/electric-db-collection/tests/electric-progressive-offline-readiness-witness.test.ts` +receives the law as a production driver for the shared model: it records +Electric's own log at its `loadSubset` boundary, and session 2 reads either +the subset session 1 loaded or one no query read. A subset-only session 1 +settles the loaded subset offline and leaves the other waiting; after a +completed progressive full sync, the never-loaded subset is ready too, +because Electric settles every later load at once from its complete rows +(model rule `full sync completes`). A mutant that drops the complete resume's +full-snapshot resolution fails only that never-loaded history. The Query DB witness +in `packages/query-db-collection/tests/persisted-on-demand-offline-witness.test.ts` +receives it with a fresh QueryClient per session: a covered on-demand query +stays loading until the wait and then shows its restored rows, a rejected +`queryFn` settles it at once, and a never-loaded query keeps loading; a mutant +that ignores coverage fails both covered histories. A fourth history lets +Query DB retention delete the released rows after a 20 ms `gcTime`; session 2 +must then keep loading. React and Solid Suspense once received a joined +initial-render gate through capability fixtures. The source-health change +(#2168) removed that gate: Suspense now releases at query readiness, and the +React and Solid receiving witnesses below run the shared contract. A later round closed most open limits with enforced cases. 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, and +removing the wrapper's early rejection handler raises unhandled rejections. +Part 2b adds a schema reset over an empty covered demand, an optimistic write +that rolls back on a demand settled from SQLite, and two cache-claim expiry +histories: one before the coverage record and one before the coverage check. +Part 4 covers ordered windows: a row deleted, moved out, or moved in after the +window settled keeps it loading, a swap keeps it covered, and with an index 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 +(`useLiveSuspenseQuery-persisted-on-demand.test.tsx`) and Solid +(`useLiveQuery-persisted-on-demand.test.tsx`) Suspense render a covered demand +from a real persisted on-demand Collection on `node:sqlite`. Vue +(`useLiveQuery-persisted-on-demand.test.ts`), Svelte +(`useLiveQuery-persisted-on-demand.svelte.test.ts`), and Angular +(`injectLiveQuery-persisted-on-demand.test.ts`) witnesses observe `isReady`, +`isLoading`, and rows. All five run the shared receiving contract described below. Mutants that drop +the early rejection handler, keep collection metadata across a schema reset, +key coverage without cursor and offset, ignore coverage, or let a coverage +read under an expired claim fail the load each fail these cases. +A fifth round added Part 2c and five more histories. Part 2c checks that a +failed coverage read, after the load or during the check, keeps the load's +own outcome; that a synchronous answer's coverage counts its rows, so a +later deletion ends it; and that a release during the coverage check stops +the source Collection loading. Part 3 then added a source failure before a +covered demand's wait ends; after the source-health change (#2168) replaced +the initial-render gate with Collection status, those histories belong to +`packages/db/tests/source-health-oracle.test.ts`. Part 4 adds a row replaced below the window boundary and a row gained +by an under-full window, which keep the tie-boundary key. Each failed on the +parent production code. A window check that accepts a superset of the +recorded keys, and a builder that drops its error-state check without +notifying settlement, each fail named cases. +A sixth round (the 2026-10-28 review) closed three more holes, each RED on +the parent production code. The campaign adds a `snapshot` command (a +snapshot transaction ends coverage, as a truncate does) and a source that +stops and resolves an aborted load (a load that core aborted records +nothing); Part 2b adds a load after scoped recovery reloaded the demand (no +load settles or records during the rest of that sync run). The shared model +now judges demand identity from the request data itself instead of core's +demand key. The storage sweep also checks that a failed coverage write +crashes the Collection. + +Remaining limits: coverage relies on the source committing a load's rows +before it settles that load (law 7); an unordered demand that loses one row +and gains another keeps its row count and stays covered, so a row that +retention removed can be missing from a covered restore (maintainer +decision, 2026-10-26; a fixed history pins the rule); and multi-tab +coordinators, outside the law by maintainer decision (2026-10-26). Part 4 +now covers a tie at the window boundary, modelled as a limited demand plus +an unordered tie-boundary demand, and page moves with and without an index. + +The generated campaign in +`packages/db-sqlite-persistence-core/tests/persisted-demand-coverage-campaign-oracle.test.ts` +shares the fixed oracle's driver (`tests/helpers/demand-coverage-driver.ts`). +Its model replays the source's own log of row writes, deletes, truncates, +and settled loads, so it does not predict when the wrapper reacquires a +demand. A fixed and a random campaign of 40 histories each interleave two +unordered demands with load starts, answers, failures, releases, live +writes and deletes, truncates, snapshot transactions, and a source that +rejects, ignores, or stops and resolves an aborted load; declared reach +covers an obsolete load that settles, an aborted load that settles, a demand +settled only by an aborted load, coverage ended by a snapshot, coverage +recorded twice, a row moved between demands, and a covered demand short of +rows. Each campaign also runs two generated packs of nine required histories, +with varied row keys, unordered demands, and ranks. Every pack must reach all +15 declared states through the same real driver and shared judgment; an +unrelated random history cannot supply a missing pack witness. This preserves +all 40 unrestricted histories. The separate +`persisted-demand-coverage.required` seed/path coordinate replays a failing +pack directly; `persisted-demand-coverage.history` retains its original +history grammar and coordinates. A seed without a path still checks the full +campaign's reach, and failures report the actual seed. Part 2 fails each +adapter call of a covered history once, before or after its effect, in +either session: safety holds for every call, a failed coverage read keeps +the load ready, and a failed coverage check waits. Part 3 releases the +demand after each step of a load: the source Collection stops loading, and +every load left behind was aborted. 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. The campaign also reads the window `group = 'a' orderBy rank limit 2` +over generated ranks with ties: a query is ready offline exactly when every +load it sent in session 2 is covered, which judges the tie-boundary load +without predicting when core sends it. Mutants that ignore window keys, +compare them in order, or record keys for every demand each fail it. Its +limits: real timers in session 1, one process, `node:sqlite`, and no cursor +or offset loads. +A scripted mutation sweep (63 mutants, recorded in the review record) then +added five fixed histories: 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 during the +check, and a coverage read that outlasts the wait. A sixth history holds the +load's cache-claim renewal past the wait and kills the last survivor, a wait +measured from after that renewal. No survivor of the sweep remains open in +this owner. + +Every receiving witness is a production driver for the same judgment +(ORC-014). The shared contract, +`packages/db-sqlite-persistence-core/tests/contracts/persisted-demand-coverage-receiving-contract.ts`, +holds six histories as data (session-1 steps and session-2 premises only): +covered, never loaded, covered then a source delete, a covered window, a +window with a row replaced below the boundary, and a covered demand whose +upstream fails at once. Each host records the source log, the session-2 loads, +and two checkpoint observations, and the shared model in +`tests/persisted-demand-coverage-model-oracle.ts` judges them; no witness +states an expected result. The histories module imports nothing, so the +Playwright spec can load it. + +The hosts run all six through their own entry point: Node (better-sqlite3 +through `createNodeSQLitePersistence`), Chromium OPFS (wa-sqlite in two page +loads, in `playwright.opfs.config.ts`, which the browser E2E job runs), +Electron (a renderer over IPC to a `node:sqlite` main process, two app +launches, in the desktop E2E job), Cloudflare Durable Objects (`wrangler dev` +restarted over one persist directory, in the cloud-mobile job), React Native +(an op-sqlite-shaped driver, in the cloud-mobile job), Expo (an expo-sqlite +shim), and Tauri and Capacitor (their unit-suite SQLite stand-ins). The React, +Solid, Vue, Svelte, and Angular witnesses run the covered, never-loaded, and +failure histories through their own hook or primitive. With the contract's +session-2 source forced to a 60 s wait and an upstream that never answers, +every host and framework reports violations on its covered histories. The +OPFS witness also pins the guide's statement that a multi-tab coordinator +waits for the network; that run is recorded, not judged, because it pins the +guide rather than the law. + +Limits: no CI job runs the Expo, Capacitor, or Tauri e2e suites, and no host +runs on its native runtime here. The Capacitor device e2e needs an iOS +simulator or Android emulator, and `adb` is not installed here; its Node e2e +fails at file load (`SqliteError: cannot start a transaction within a +transaction` at `tests/helpers/capacitor-sqlite-test-db.ts:22`). The Tauri +native e2e needs `cargo`. React Suspense reveals a covered failure about 300 ms +late (its fallback throttle), so the contract's before-wait checkpoint sits at +450 ms. + +## Sync metadata session + +[Issue #2220](https://github.com/TanStack/db/issues/2220) reports that a +sync-side metadata read misses an earlier accepted transaction's write while a +persisting optimistic transaction holds it. The law is borrowed from standard +theory (maintainer decisions of 2026-10-22 and 2026-10-23, recorded in +[the review record](oracle-reviews/2026-10-22-sync-metadata-session.md)): one +sync run is a session with read-your-writes, own reads, and monotonic writes +in commit order. Every sync transaction is independent, including one begun +inside another; accepted transactions apply in commit order, and a hold +cannot change that order. An outer insert that lands on a different source +row in commit order makes its transaction invalid, and an insert written over +such a row is rejected as a duplicate, whatever holds or seeds there are. Consumers see whole +transactions. Hydration seeds and persisted cache rows are older than every +source write, and a later seed replaces either until the source writes the +row or its metadata; an equal re-insert keeps metadata without writing it. +In the persisted wrapper an insert replaces whatever row it lands on. A +peer's notice of a write in this run counts as a source write, and writes +during a storage read follow the same rules, the last write for a key +winning. When a stored row's author is unknown, a seed this tab holds stays +unless storage changed it, and other stored rows count as source writes +(decisions 13 and 15). Reloads keep metadata of a missing row (decision 16), +a write over a seed stores the row this tab shows (decision 17), and a +reload after a named notice keeps a seed's metadata (decision 18). + +| Owner | Production paths | Observations and checkpoint | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [sync metadata session oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/sync-metadata-session-oracle.ts) (`flat`, `nested`, `seeds`, `nested-seeds`, `seeds-full`, `nested-conflicts` lanes) | `DbClient` Collection, core hold, `DbClient.hydrate` seeds | `metadata.row.get`, `metadata.collection.get`, `list()`, `list(prefix)`, `collection.base`, each commit receipt's state, thrown errors; after every action and after settlement | +| [persisted driver](https://github.com/TanStack/db/blob/main/packages/db-sqlite-persistence-core/tests/sync-metadata-session-persisted.test.ts) (`persisted-flat`, `persisted-nested`, `persisted-seeds`, `persisted-cached`, `persisted-conflicts`, `persisted-nested-seeds`, `persisted-reads`, `persisted-reads-seeds`, `persisted-peers`, `persisted-peers-full`, `persisted-peers-on-demand`) | `persistedCollectionOptions` over an in-memory adapter, store hold, core hold, and a storage read held in flight; seeds through a `DbClient`, alone and crossed with nesting and a row cached in storage at startup; a peer's notices, eager, under full row updates, and on demand | the same source reads after every action; `collection.base` and receipts after settlement; in the peer lanes, the stored rows after settlement | +| paired properties and boundary witnesses in the core driver file | production against itself: holds removed, row 2's actions removed, or seeds performed first; stale run; write without a transaction; cleanup with a held seed | source reads (and row 1's view) at each shared checkpoint, or every observation after settlement for seed order; thrown errors; unhandled rejections | +| [row metadata composition oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/collection-row-metadata-composition-oracle.test.ts) held-window and nested lanes | Collection, core hold, a transaction begun inside an open one, `DbClient.hydrate` | implicit row-metadata rules during a hold, and for an outer transaction whose inserts are reclassified after an inner write applies first, with and without a hold | +| [hydration authority oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/db-client-hydration-authority-oracle.test.ts) | `DbClient` initialData, hydration chunks, and an adapter sync transaction (inserts, updates, deletes, truncates, and metadata-only writes) in full and partial row modes | public rows, the synced store, and row metadata through `client.dehydrate()` after each action; D5 per object | +| [Query ownership lifecycle](https://github.com/TanStack/db/blob/main/packages/query-db-collection/tests/ownership-lifecycle.oracle.test.ts) receiving witnesses | Query DB claims, releases, and retention cleanup over core and `persistedCollectionOptions` | published owners and non-owner row metadata | +| receiving expectations in the [persisted oracle](https://github.com/TanStack/db/blob/main/packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts), the Electric oracle, the Browser coordinator oracle, and the Electron IPC tests | persisted source inserts over a hydrated baseline; the durable mutation a source insert writes | public and durable rows; persisted mutation types | + +On `30175be48`, 51 of 186 tests fail in the session, composition, and +retention files: held reads, commit order and outer writes after a held inner +commit, every seed rule, and the paired properties. The persisted lanes fail +the held reads through core, reads of an enclosing open transaction, an +insert's implicit clear, equal re-inserts, inserts over seeds and cached rows +under partial row updates, and late seeds beside a store-held transaction. +Six Query witnesses fail, including two that only dropping Query's `??` +metadata fallback repairs. The review record's evidence block lists reach +counts, mutants, and GREEN runs. + +Two comparisons are narrowed on purpose, each recorded with its authority in +the review record: + +- Under partial row updates the seed-order property drops `reinsert` from + both runs: decision "b" lets a seed moved first change a partial update's + merge base, so a re-insert planned from the arrival-order row is no longer + equal. The named witnesses `partialUpdateThenLateSeed` and + `seedUnderOpenReinsert` cover that interaction. +- Key independence skips histories in which the model invalidates a + transaction: invalidation removes the whole transaction, row 1's writes + included (decision 7). + +The `persisted-peers` lane plays a second tab's source through the +persistence coordinator: named row, metadata-only, delete, full-reload, and +reset notices, crossed with seeds, the cached row, nesting, and holds. A +reload that a notice causes is a storage read: its truncate replaces rows +without truncating source authority or row metadata, an unchanged row keeps +its status, and a seed's metadata survives a reload after a named notice +(decisions 9, 11, and 15 to 18). A reset is a source truncate, and every +later notice in its term reloads storage; on demand the reset keeps the +seeds this tab holds (decisions 19 and 20). After settlement the stored rows +must equal the model's, which a restart would read. +`persisted-peers-full` repeats the lane under full row updates, where an +update replaces its row in storage too; `persisted-peers-on-demand` repeats +it on demand with a demand for every row. A reset over metadata of a missing +row is too rare in generated histories to declare; a replay pins it. A +random CI campaign (seed `654774679`) found a `persisted-peers-full` history +that reproduces on `main`: an outer transaction inserts over a bare seed, an +inner one re-inserts the row and commits, and a peer's full reload leaves the +row with the cached seed's metadata, where the model expects none (law: +the source reads its row metadata). The maintainer chose to leave it open, +tracked with #2281; an `it.fails` replay through the same model, driver, and +comparison pins its shrunk prefix, and the random campaign can still draw it. +Owner: this oracle. A reset +may rewrite its rows, which its reload then applies as source writes. +`persisted-reads-seeds` crosses read holds with seeds and the cached row and +aborts a buffered commit's signal, which abandons it at the read's end +(decision 21); a seed that arrived after it stays judged against it +(decision 22). Dependent suffixes of an abandoned transaction belong to the +abort-graph oracle. The peer is the driver, not a second +Collection, so the claim stops at the controlled coordinator; a second +production driver with two real coordinators is the open receiving +witness (issue #2272). Missed notices and notices over the targeted key limit are not +generated; the persisted oracle's grids cover them. Two held-cleanup +Query retention witnesses race on CI; the maintainer deferred the race until +a failure reports the timer trace these witnesses now print, and the review +record's "Open" section holds the evidence. Restart and resume reads belong to the +SQLite resume-snapshot and Electric resume owners; this oracle's claim ends +with one sync run. + +## Deferred contracts and evidence + +The maintainer assigned offline policy work to +[RFC #1659](https://github.com/TanStack/db/issues/1659). It is not a merge blocker +for this oracle repair. Keep these scenarios and decisions with that owner: + +- **A10 R7/R13:** the executor holds caller success until deletion is + acknowledged, and a persisted `deletion-pending` phase prevents provider + replay after a known deletion failure. Controlled restart and adapter-failure + witnesses cover that boundary. A crash before the phase write, old readers of + marked rows, physical power loss after adapter acknowledgement, and native + browser restart or multi-owner handoff remain open. Provider idempotency is + required to avoid a repeated effect in the first two cases. +- **A10 R4:** [Issue #1939](https://github.com/TanStack/db/issues/1939) + is owned by the leadership replay oracle. The action grammar crosses retained + leadership, loss before invocation, and loss during or just after `onMutate` + after offline selection; a manual transaction loses leadership after its + optimistic mutation and before `commit()`. The original action path fulfilled + with no outbox record or provider call. The repaired paths reject and roll + back; retained-leader and before-invocation controls still fulfill. Later + leadership changes and a held outbox write crossed with loss or regain remain + for this leadership oracle, using controlled write settlement and caller + checkpoints. The [IndexedDB write-settlement owner](https://github.com/TanStack/db/blob/main/packages/offline-transactions/tests/indexeddb-write-settlement.test.ts) + still needs a browser-host composition with leadership loss across a write. + These paths are outside this bounded repair. The [review record](oracle-reviews/issue-1939-offline-admission.md) + preserves the RED/GREEN and checker-calibration evidence. +- **A10 R5/R9:** manual removal/clear outside the active-provider witnesses and + broader restored optimistic lifetimes remain open. The settlement owner checks + the optional retry decision across AbortError, ordinary network, 400/401/403/ + 422/404 message, and permanent errors. Each kind crosses true, false, + undefined, and absent hook answers with 1–3 FIFO peers. The fixed examples + each receive a campaign run before generated cases, with a reach assertion. + A later failure checks the current retry count. Public `commit()` witnesses + cover thrown, null, zero, and fulfilled or rejected Promise hook results; + a rejected Promise must not escape as an unhandled process rejection. Primitive + and status-bearing object provider rejections check Error conversion before + the hook. A status-bearing Error from another JavaScript realm reaches the + hook by identity, keeps its first retry record, and later settles after a + successful attempt. A plain status-bearing object still takes the non-Error + conversion path; spoofed Error tags are outside these witnesses. The first + stored decision distinguishes terminal rejection from a retry record. A hook + fault records a terminal rejection for only the failed row. The executor + removes that row, + rejects its caller with the hook failure, and continues queued work + after acknowledged deletion. The public admission witness holds the terminal + marker read, write, or deletion after a thrown or invalid hook: a new commit + durably joins the queue at each cut but cannot run before head cleanup. + The started-write witness crosses write acknowledgement before, during, or + after the hook fault, including a row visible before acknowledgement and a + write rejected before storage changes. It checks both public promises, + outbox IDs, Collection rows, and same-executor peer progress. A visible but + unacknowledged peer cannot run even after the head is removed. The controlled + adapter does not cover a write that changes storage and then rejects its + acknowledgement; fresh executor startup before a held write acknowledges + remains outside this witness. + Failed terminal marker or deletion storage still stops the executor: the + head caller receives the hook error, the batch receives the storage error, + and queued peers stay durable and unrun. If deletion fails after the terminal + marker is written, restart removes the marked row without another named + mutation function call or optimistic restoration. The settlement owner also + checks terminal named mutation function rejection with failed deletion, a + halted executor with queued peers held, and offline executor restart without + a second named mutation function call or optimistic restore. The executor + does not automatically retry a failed phase write or deletion. A failed + terminal marker write followed by a crash remains an unmarked outbox replay + window. The [row-local review](oracle-reviews/pr-1592-row-local-retry-hook.md), + [retry-decision grammar review](oracle-reviews/pr-1592-retry-decision-grammar.md), + [rejected-Promise review](oracle-reviews/pr-1592-rejected-hook-promise.md), + and [error-boundary review](oracle-reviews/pr-1592-error-boundary-review.md) + record the checker controls. The Node-hosted Promise witnesses do not establish + browser `unhandledrejection`, third-party thenables, or rejection after executor + disposal; this settlement owner needs a receiving witness for any broader + claim. A held provider can reject after `removeFromOutbox` or `clearOutbox` + acknowledges deletion. The default network retry and a hook's 401 retry both + reject that caller with the provider Error, keep the outbox empty, and allow + later same-executor work. Manual removal overlapping the read/write inside + `outbox.update`, rather than finishing before provider settlement, still needs + a controlled witness and an atomicity decision in this owner. Broader offline + retry and recovery policy remains with RFC #1659. +- **A10 R11/R12 and earlier R8:** metadata/native-value domain, old readers of + new wire records, and unreadable/unknown-version outbox recovery. New readers + accepting old records does not prove reverse compatibility. Do not delete + unreadable work silently or invent exactly-once guarantees. + +Other explicit follow-up boundaries are owned by +[issue #1820](https://github.com/TanStack/db/issues/1820), not silently claimed +green: + +- **Effect disposal:** the existing external-disposal wait and synchronous + in-handler cancellation are tested. Waiting for independent work returned by + the initiating handler conflicts with supporting a handler that awaits its + own disposal. The candidate implementation was removed pending a contract. +- **Proxy native mutators and symbol writes:** retain supported standard + Map/Set behavior. Decide additional support versus a clear error before + generalizing expectations; do not silently drop native edits. +- **Progressive/native execution:** eight progressive registrations lack valid + phase/capability evidence. The progressive suite's expectation that a live + query preloads before the full sync now matches the core initial-readiness + law, but no run receipt shows that case executing against Electric. Electric's ready gate is after application, not a + pre-swap source gate; TrailBase does not supply that capability. The 24 move + cases and actual native hosts need separate execution receipts. A green + registration helper or package job does not prove these cells ran. +- **Optional mechanics/performance:** separating the expensive replay-process + campaign, sharing native runner code only with both host builds verified, + optional cancellation calibration and proxy construction-work budgets. + An earlier suggestion to merge unnamed cleanup helpers still lacks exact + targets; do not perform a blind consolidation. + +## SQLite Temporal value preservation + +The implemented [v2 grammar and evidence](oracle-reviews/issue-2034-design/v2.md) +cover native `Temporal.Instant` and `Temporal.PlainDate` through the shared +SQLite adapter and local persisted wrapper. Global constructors must be +registered; other Temporal kinds reject. Existing lifecycle, receipt and +publication-before-durability laws remain with their current owners. + +- The [typed-value owner](https://github.com/TanStack/db/blob/main/packages/db-sqlite-persistence-core/tests/sqlite-temporal-value-oracle.test.ts) + checks real file reopen, direct/nested rows, metadata, replay, equality/range, + Boolean/IN, large IN, field comparisons, cursor ties/continuation and ordered + windows. Its directly named rank/text companion supplies independent native + observations across precision, signed-year, endpoint and calendar families. + Constructor failures, invalid brands and marker-shaped records are included. +- The [Node expression-index owner](https://github.com/TanStack/db/blob/main/packages/node-db-sqlite-persistence/tests/expression-index-oracle.test.ts) + checks raw SQL before residual cleanup, final adapter keys and actual named + index use. Wrapper-created coalesce indexes reach Collection metadata/native + literals and distinguish adjacent-literal signatures. Small and large scalar + membership indexes protect compatibility with SQLite's DDL restrictions. + Numeric array/object native EQ and IN also inspect persisted expression values. + Mixed-domain polarity checks retain NaN, null/missing and scalar controls. + NUL strings cover both equality directions, membership and Boolean composition. + Negated coalesce and scalar eq(in(...), true) keep #1997's full-read fallback. + Exact replay excludes unrelated native cases; simultaneous semantic/cleanup + failure retains both errors and still closes SQLite. +- The [ordinary-work owner](https://github.com/TanStack/db/blob/main/packages/db-sqlite-persistence-core/tests/ordinary-transaction-work-oracle.ts) + adds twelve native repeated-action success/rollback histories and invalid + superseded actions. Its independent Map model retains immutable native values; + kind/text observations reject the old structuredClone false-green boundary. +- The [persisted wrapper owner](https://github.com/TanStack/db/blob/main/packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts) + rejects native remote-subset literals before dispatch/retry, including an + ownerless leader that becomes a follower while hydration waits or after local + success followed by sequence-gap recovery. The latter uses the existing + terminal error path before retry admission. String controls dispatch once; + existing transient retry, receipt and FIFO histories still run. The + [mixed-demand refinement](oracle-reviews/issue-2034-recovery-review.md) covers + both demand orders, retained/aborted/released candidates, both native kinds + and string controls. It observes valid-prefix dispatch, exact reentrant and + later error identity, post-terminal replay/retry fencing, and exact release + objects. Successful controls observe complete demand coverage after held + transport settles. First-demand-only and replacement-error mutants distinguish + these laws from the older singleton checks; successful pullSince recovery and + real-host elections remain separate receiving paths. + +The [initial RED receipt](oracle-reviews/issue-2034-temporal-persistence.md) +remains historical. The v2 receipt records failures against original production, +a lexical-order mutant that retains native reconstruction, and intermediate +paired-IN, nested-path, routing and membership-index counterexamples. Final +results are green for the reached paths; this is not a universal proof or a +latency guarantee. SQL cleanup alone cannot compensate for lost candidates. + +Remaining witnesses have explicit owners: engine-native/alternate constructors +and old-byte/marker-shaped compatibility belong to the typed-value owner; +native truncate/delete, crash and format-transition histories belong jointly +with ordinary-work/resume owners; real mobile/browser roundtrips belong to each +adapter's receiving contract; actual native multiprocess transport belongs to +browser/Electron coordinator receiving owners. The persisted owner retains +native release/abort/reset and wider ownership schedules. None of those receiving +claims follows from this Node/shared-core result. Same-kind ordering is the +supported domain; arbitrary Temporal arithmetic and other kinds are excluded. +Applications may rebuild old damaged caches through existing schema/reset paths; +no type can be recovered from an already-erased empty object. + +### Law enforcement audit + +The [law audit](oracle-reviews/issue-2034-law-enforcement.md) strengthens the +persisted and Browser owners after three demonstrated false greens. Captured +pre-validation expectations protect mutable builtin state on acceptance and +rejection. Rich output checks now enforce detachment and exact release-object +identity; later projections must reflect changed input without altering earlier +snapshots. Optional indexes cross both supported native kinds and missing-global +cases, plus independent local/coordinator synchronous and held failures. The +intermediate held cut checks that coordinator completion evidence cannot precede +local settlement; settled cuts check warnings, exact calls, healthy siblings, +readiness and ordinary reads. Mutants establish assertion sensitivity. Copy-work +bounds remain explicitly limited to the measured buffer/record copy seams and +full discarded projections; total allocations, latency, arbitrary third-party +coordinator work, and real-host scheduling are not established by these checks. + +### Remaining review investigations + +The [high-effort follow-up](oracle-reviews/issue-2034-high-effort-review.md) +records measured work and upgrade behavior. Remaining questions are assigned +to the following owners: + +- The boolean-arity compiler-work owner now counts discarded literal quoting + for bound string IN at sizes 0, 1, 32 and 1,025. The old compiler performed + two quotes per value; the repair performs none and retains exact rows and + binding counts (BUG-007, HE-006). This is not a latency bound. +- The [bounded follow-up](oracle-reviews/issue-2034-bounded-follow-up.md) closes + repeated native literal encoding (HE-007): the typed-value work witness admits + at most one encoding per literal occurrence for EQ and IN at sizes 1/3/1,025. + Both native kinds are calibrated against the previous implementation; the + surrounding expression-index and rank/text owners retain semantic coverage. +- Discarded wire projections (HE-008) are closed for the three persisted admission + guards by shared validation-only traversal. The [work follow-up](oracle-reviews/issue-2034-wire-validation-work.md) + records an 18-case persisted matrix: follower/role transition/retry/recovery and + abort/release during hydration at 0/1/33 bytes. Payload copies drop from two to + one per successful attempt, four to two across a retry, and one to zero when + demand retires during hydration. Independent Browser-owner rejection/path + fixtures cover validation-only admission; frozen rich-value receiving histories + retain cycles, aliases, detached snapshots, and local lifecycle identity. The + original guards and no-admission/no-detachment mutants fail at intended + assertions. Repeated validation traversal and runtime latency remain unmeasured; + real-host scheduling remains with the browser/Electron receiving owners. +- Optional index specification serialization now shares the existing best-effort + persistence boundary. The [index-admission follow-up](oracle-reviews/issue-2034-index-admission.md) + records startup/runtime histories with supported native controls, unsupported + kinds, missing global constructors, and healthy siblings. The persisted owner + asserts readiness, exact local/coordinator signatures, warning paths, rows, + and subsequent ordinary reads. Original source and a local-only repair fail + six cases each. This covers wrapper admission; real SQLite index execution and + browser scheduling retain their separate receiving owners. +- The resume owner now proves the selected explicit schema-reset route for + obsolete indexes (HE-010) and ambiguous legacy marker bytes (HISTORY-002). + It removes both registry entries and physical indexes before the first read, + preserves an unrelated collection, and retains newly escaped data and rebuilt + indexes on same-schema reopen. A registry-cleanup mutant fails this witness. + Automatic reclamation without reset and lossless old-byte migration remain + outside the selected policy; this test does not claim either. +- The [Collection index-value owner](https://github.com/TanStack/db/blob/main/packages/db/tests/collection-index-value-oracle.test.ts) + distinguishes native literals from ordinary tagged records, nested mixtures, + and the escape envelope while preserving equivalent key orders (BOUNDARY-005). + Plain and null-prototype own-tag records have isolated metadata snapshots + (BOUNDARY-004/HISTORY-004). The old implementation fails these public metadata + observations. Custom-prototype impersonators are outside this owner's native + contract; the typed-value owner separately checks rejection and rollback of an + invalid native brand at SQLite persistence. +- The Node expression-index owner now covers ordinary/native-mixed NUL lists, + four NUL/Unicode positions, positive and negated membership, exact stored index + values, final keys and candidate containment (VERIFY-NUL-NOTE). The former SQL + literal encoder fails at the public index-DDL boundary. Direct runtime mixed + membership already passed; the defect was persisted expression construction. + +## Final evidence record + +Verification receipts and follow-up issue links are recorded in the PR and +the issue closeout comment. Keep their exact revision/runtime boundaries; +historical counts in research notes do not certify later commits. Closing the +bounded repair means the acceptance map has evidence and each remaining +question has an owner—not that there can be no more bugs. + +## IndexedDB Collection persistence + +Owner: [IndexedDB oracle portfolio](https://github.com/TanStack/db/blob/main/packages/db/tests/indexed-db/ORACLE.md). +Settled histories compare independently authored scalar rows with public, +subscription, peer, export, raw durable and fresh-restored snapshots. Imports +check changed-value versions; detached driver inputs protect authored truth. +Handler decisions are followed by an ordinary successful insert before a +separate replacement suffix, so replacement cannot erase the observed leak. +Wrong-version, input-mutation and suffix-leak controls calibrate those boundaries. + +The transport owner adds twelve histories: delayed clear/import followed by a +later disjoint receiver insertion under FIFO/reverse/duplicate notifications; +idle/cleaned-up clear/import; real manual acceptance after cleanup; and whole- +database deletion with active sibling stores. Every affected public snapshot +must match authored durable operation order after controlled delivery. A raw +unseen row makes excluded-notification handling observable. These cases repair +specific convergence laws; they do not establish arbitrary concurrent receivers. + +The [compatibility tests](https://github.com/TanStack/db/blob/main/packages/db/tests/indexed-db/compatibility.test.ts) (fixed tests; no oracle owner (tracked in #2264)) +checks full versus getRandomValues-only crypto through write/broadcast, one +reusable descriptor across independent DbClients and cleanup, and injected +factories without ambient IndexedDB globals across typed-key multi-store +restore/import/clear. Wrapper tests distinguish callback, request and native +completion, including durable rows after late callback rejection. Store +creation is additive; declared stores and actual native stores are distinct. +Normal generated persistence tests retain fixed/random campaigns and direct +seed/path replay. Blocked/unblocked open/delete cases check native terminal +settlement and caller connection ownership through later native upgrade/delete. +Native failed opens preserve durable rows/schema. A blocked administrative deletion +retains public and durable snapshots. Its receipt has no row-publication authority; +managed versionchange marks affected Collections errored while retaining rows. Failure to issue +deletion preserves those snapshots. The controlled cases establish no timeout, +cancellation, or automatic in-memory fallback. Native receiving cases below +cover named blocker and release schedules; arbitrary browser lock scheduling +remains open. + +The transport owner also crosses two/three independent managed descriptors with +native upgrade/delete. Exact versionchange recipients and zero blocked events +check automatic close. Deletion retains errored public snapshots while fresh +storage is empty; upgrade preserves durable rows. Both require fresh descriptors +for further persistence. The four +original-source assertion failures and repaired cases distinguish this law from +the earlier shared-descriptor fixtures. Raw unmanaged blockers keep the wrapper's +pending-until-terminal law. These controlled cases do not prove browser scheduling. + +The concurrent extension adds independent per-Collection expectations, raw +publication and Collection-status recording, native transaction gates and +non-awaiting dispatch. It receives the whole-row omission law and rejects a +full-to-partial application mutant. It also rejects a spurious Collection error, +invalid delta semantics and a repaired partial publication. A serial delivery +fixture fails the overlap reach control. Generated histories use fixed/fresh +parity, original-failure-preserving reduction and checked direct replay. + +Executed coverage and remaining package-owned cells: + +- **Connection ownership, CR-02:** native managed versionchange with a held + test-owned readwrite transaction and unmanaged deletion blocking, in all three + engines. The retirement and deletion-queue owners now receive admitted writes, + immediate error notification, obsolete reads, late startup, caller/publication + obligations, and both native blocker-release orders. The retirement owner additionally receives abnormal native closure with ready, + idle, loading and five admitted persistence operations. Chromium receives actual + native close/abort through storage clearing; other engines lack that control. + Raw close bypass and automatic restart remain outside the managed contract. +- **Persistence, PC02:** all six manual acceptance orders for three distinct + Collection identities, two stores, disjoint same-store keys, reused sibling + keys, mixed deletes/inserts and unaffected versions. The compatibility owner + also projects same-ID/disjoint-key payloads by Collection reference, in both + acceptance orders before/after cleanup. Same-ID/same-key core payload merging + remains outside that adapter witness. +- **Settlement, PC04/PC06:** first/middle/last clone failure in a three-row batch + for automatic/manual/import; multi-row held CRUD with both decisions and + independent peer progress. Raw storage, caller, payload, versions, ordinary + suffix and restore cuts remain distinct. +- **Transport/settlement, PC08/PC10:** two to five independent descriptors, + generated read windows across replacement/CRUD, one held whole-row local + intent crossed with peer CRUD/clear/import, and downstream query cuts. Native + import exposed transient union publication and ordered peer persistence exposed + duplicate-insert confirmation; both have source repairs and receiving tests. + The local-order owner now crosses three held local intents with ordered peer + update/delete/replace and checks every decided prefix. Peer work interleaved + between those acceptances, multi-intent intermediate local publications and + reads straddling unordered writes still need receiving witnesses in the + pending/transport owners; final local equality does not prove those cuts. +- **Transport, PC12/CC09:** obsolete receiving read success/abort after cleanup + and restart; native obsolete abort and later restore. Pending mutation caller + outcome/confirmation into a restarted sync run remains a distinct policy and + test obligation; callback invalidation does not specify it. +- **Transport/core optimistic, PC16:** non-truncating omission via ordered + delete/reinsert in controlled and native drivers; active intent, exposed base, + public snapshot and one-use acknowledgement metadata at held/settled cuts. + Nested values and several overlapping local intents remain with the core owner. +- **Settlement, AUX01:** valid post-durability send/publication failure still + needs a native premise and defined caller/source/public outcome. No retry or + rollback-after-commit policy is inferred. +- **Native/consumer, CC10:** Chromium, Firefox and WebKit receive overlapping + callbacks, native read/write abort, startup subscriptions, blockers, + versionchange, controlled page close/reload and restore. Raw evidence survives + acknowledged page destruction with a held handler. A schedule-dependent mutant + reproduces its original law/checkpoint in each engine. The controlled pending + companion checks complete downstream live-query snapshots. Real suspension, + lost-message recovery, quota/eviction, process-crash durability and evidence + lost before runner acknowledgement remain open and require their own premises. + +Native evidence is per engine. Simulated reverse/duplicate messages do not prove +browser delivery ordering. The package has no leader election, timeout or +in-memory fallback. Same-key writers without explicit persistence order, untested mutable-value +shapes and timing guarantees remain unresolved; separate Collection +acceptance calls do not promise cross-Collection atomicity. + +**Typed-key payload identity (HC005):** the +[optimistic transaction payload oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/optimistic-transaction-oracle.property.test.ts) +now crosses four numeric/string pairs, both author orders, insert/update/delete/ +repeat-update, and success/rejection. Exact handler payload multiplicity, order, +types and final public keyed rows reject the original collision. The native +host owner receives numeric 0 and string "0" in one insert, then peer update, +import, clear and restore. Core global mutation identity now encodes Collection +id, key type and key value as a tuple. This does not establish interoperability +between distinct Collections that intentionally reuse the same id and key. + +**Failed mutation callback context:** the optimistic transaction payload owner +checks that restoration publishes after the failed callback's ambient frame +ends. A subscriber's new write is visible but does not enter the failed +transaction payload. Nested success and failure witnesses retain the enclosing +callback's context for subsequent writes. The reviewed implementation fails the +subscriber payload cut; the frame-specific repair passes it and the adjacent +nested histories. These finite histories do not establish arbitrary subscriber +reentry or cross-scope restoration behavior. + +**Failed-callback snapshot work:** the same owner places 16 prior mutations in +one transaction and counts array-entry visits across five no-write `mutate` +callbacks. The eager snapshot on the reviewed code visits 80 entries; capturing +only before the first real `applyMutations` visits none. The neighboring +failed-write histories still check restoration of prior intent. This bound +does not remove the existing linear merge traversal for each real write or +establish elapsed-time performance. + +## Compound joins + +[The replacement evidence record](oracle-reviews/2026-10-05-compound-joins.md) +records RED admission on main, GREEN refinement, and hostile-mutant failures. +The cold-join reconciliation owner now covers compound equality classes, +nullish operands, two/three terms, order/reversal/nesting, all four join types, +scan/index paths, controlled cold acquisition, and keyed replacement histories. +The identity-output-shape owner checks complete predicates through direct, +from-QueryRef and joined-QueryRef boundaries. The includes-context-transport +owner checks parent values used only in a later equality across both operand +sides and direct/joined-QueryRef sources, in all three materialization forms. + +The compiler demand oracle in +`packages/db/tests/query/compiler/lazy-demand.test.ts` independently recomputes +active first-key demand from satisfiable rows. Its finite histories cross +LEFT/RIGHT, two/three fields, both term orders, shared primary keys, nullish +components, and put/remove/restore. Nullish tuples contribute no keyed demand. +The controlled cold provider now applies direct-field IN requests using the +independent value-class labels. Four distinguishing numeric tuples reject +wrong-field demand that the former whole-table provider missed. + +The bounded claim excludes arbitrary expression trees and wider tuples. Real +provider acquisition, eviction/reentry, and exact lazy-demand minimality need +receiving witnesses in the cold-join and adapter owners. Compound async, +optimistic, paginated, and deeper nested include histories remain with the +includes-temporal, includes-optimistic, pagination, and includes owners. This +feature does not claim those cross-products are established by its finite +matrix. Consumer hook caches require framework conformance witnesses; the +identity owner proves the compiler identity boundary and fresh public rows. + +Review follow-ups remain bounded by the evidence above: + +- The cold-join owner must distinguish required acquisition from demand + minimality. A nullish tuple contributes no keyed demand; a satisfiable tuple + still needs a positive witness. Real-provider demand counts remain with + the receiving adapter owner. +- A deterministic 10,000-row-per-side probe measured the new operand-array + cost and the repair: single equality 20,000 to zero arrays, two equalities + 40,000 to 20,000. Whole-query runtime and heap impact remain unmeasured. + The always-tuple alternative restores single-equality allocation, so its + simplification remains a tradeoff for a future measured join benchmark. +- Builder admission checks Boolean join syntax, while the compiler binds + sources and also accepts direct IR. A single-parse representation would need + a design for these separate entry points and measured compile-work benefit. + No parser state or cached validation is added for an unmeasured saving. +- The cold-join driver's cleanup callback unsubscribes before cleaning up its + live query. No legal throwing-unsubscribe witness was found. The owner is + `checkWithCleanup` in the cold-join oracle; any future cleanup change must + demonstrate the throwing path and verify later cleanup plus primary-error + preservation. This is an evidence gap, not a demonstrated production bug. + +### Compound-join planning and identity follow-ups + +The second high-effort review at `5bb211519` distinguishes semantic identity +from physical acquisition. A controlled React `useLiveQuery` rerender keeps +its Collection and region-first demand when only the equality order changes. +Freshly compiled controls use the supplied order. The guide now states that +ordering selects a plan when the Collection is created. This React witness +does not establish other frameworks' reuse behavior. + +- The compiler/lazy-targets and cold-acquisition owners retain the proposed + first-eligible-equality optimization. A receiving witness must cross join + direction, computed operands, direct and subquery sources, and index modes. +- The identity owner retains join-specific unary/duplicate equality + normalization. Public `and()` requires two operands; direct unary AND IR and + `and(eq(...), eq(...))` with duplicate terms currently differ from bare EQ. + A change must preserve general Boolean coercion and test actual Collection + reuse in the receiving framework. Making loading order part of identity is + a separate design choice; current identity remains semantic. +- Exact compound acquisition needs a tuple-preserving demand model in the + compiler/lazy-demand and cold-acquisition owners, plus adapter witnesses. + Independent per-field IN sets admit cross-pairs from different parent tuples. + Current first-field candidate supersets remain permitted by the architecture. + +### IndexedDB review extension: local ordering and boundary admission + +The xhigh review at `6ad2edc42` receives its audit in +[the versioned review record](oracle-reviews/2026-10-05-indexeddb-xhigh.md). +`local-write-order-oracle.test.ts` owns the approved automatic-write ordering +law for one Collection: three authored effects, every handler completion +permutation, every accept/reject mask, held earlier handlers, and synchronous +handler reentry. It checks caller outcomes, durable/public/peer snapshots and +fresh restore. The native suite receives held insert followed by update in +Chromium, Firefox and WebKit. Cross-Collection writers and explicit utility +operations remain outside this ordering contract. + +The compatibility owner adds id changes at options/Collection construction, +automatic/manual acceptance and acceptance after cleanup. Transport adds +none/mixed/all version metadata, typed keys, duplicate delivery, invalid message +exclusion with a valid neighboring read, old-connection error propagation, and +native deletion completion separated from callback delivery and database +recreation. Native multi-page tests receive the deletion/recreation premise in +all three engines with the old success callback deliberately held. + +The wrapper owner observes native error causes. Settlement counts all native +write requests admitted before the first success and retains its atomic abort +matrix. This establishes request admission work, not an elapsed-time speedup. +Runtime and type tests use source without built DB output. Published declarations +have a separate build-consuming lane after concurrent CI runtime tests finish. + +The [law enforcement follow-up](oracle-reviews/2026-10-05-indexeddb-law-audit.md) +records the subsequent calibration and closes the stored-field/type cleanup. + +- **Connection closure, XH-06:** implemented under the subsequent approved + contract. Managed versionchange and explicit descriptor close immediately mark + affected Collections errored. New reads lose publication authority; native- + admitted writes finish and confirm without regaining readiness. Applications + recreate Collections. The retirement owner, core readiness owner, and native + receiving cases enforce these separate obligations. +- **Deletion lifetime authority, XH-07:** closed under administrative deletion. + The stale-receipt counterexample was retained before implementation. The + Collection deletion utility and database-deleted message authority were removed; + `deleteDatabase(name)` only reports the native administrative outcome. The + deletion-queue owner checks old/new receipt cuts, retained errored snapshots, + queued name targeting, and fresh empty storage. The browser owner receives + both unmanaged/native transaction release orders. See the + [TLA refinement audit](oracle-reviews/2026-10-06-indexeddb-tla-refinement.md) + for exact bounds and provider limitations. No persisted identity or deferral. +- **Stored metadata/public types, XH-14:** new version records omit `updatedAt`; + legacy absent/past/future timestamps retain their row semantics. Persistence + checks ordinary writes, untouched records, mixed-format restore and replacement. + Utils now carry output and input types only; source assertions and published + ESM/CJS consumers preserve the transformation distinction and Collection keys. +- **Ownership, XH-02:** equal/distinct Collection IDs, both manual acceptance + orders and live/cleaned-up sync runs retain only reference-owned effects. +- **Transport, XH-03/XH-08:** absent metadata at the receiving read is independent + of startup metadata. Typed keys, initial membership, duplicate delivery and a + successful suffix are checked. Invalid envelopes cannot issue storage work. + Native pages receive missing-metadata rows and duplicate notifications in all + three engines; low-level writes still require their own invalidation. +- **Settlement and diagnostics, XH-04/XH-09/XH-13:** transformed-key collisions + and schema failures at first/middle/last input preserve rows and versions; + exact cause identity survives each native admission branch. Request admission + counters cover automatic insert/update/delete and import, with atomicity + enforced by the existing abort matrix. + +### IndexedDB connection closure: TLA+ refinement owners + +`packages/db/tests/indexed-db/retirement-oracle.test.ts` owns the finish +policy's row, caller, admission, read-authority, accepted-work and error-status +laws. `deletion-queue.test.ts` (fixed tests; no oracle owner (tracked in #2264)) holds administrative receipt authority across +delete/recreate/delete. `packages/db/tests/truncate-readiness-oracle.test.ts` +owns replacement publication with explicit readiness preservation. Their models +remain structurally independent of production connection listeners and caches. +See the [loss audit](oracle-reviews/2026-10-06-indexeddb-tla-refinement.md) for every +formal transition/invariant, recovered distinctions, fault calibration and bounds. + +The browser receiving owner crosses five admitted mutation kinds × three closure +origins × commit/abort in all three engines. It separately receives both releases +of an old native transaction and unmanaged deletion blocker. This last receiving +witness is required: fake-indexeddb 6.2.5 violates the transaction-only deletion +hold. The model law remains normative; the faulty provider does not narrow it. + +The closed-connection finish policy is separate from cleanup/restart, physical +crash, lost notifications, post-durability application listener failures and +uncontrolled browser discard. Those existing owners/boundaries are unchanged. +This bounded translation does not claim every formal graph path is executed by +the TypeScript suite or that finite tests prove unconditional progress. + +### IndexedDB wrapper observer composition + +`packages/db/tests/indexed-db/wrapper-settlement-oracle.test.ts` owns +callback/native settlement independently of native event-observer registration. +Its 20-case matrix crosses native complete/abort, none/additive/property/replaced/ +cleared observers and immediate/held callbacks. It checks caller outcome at native +terminal and callback-release cuts, durable rows and exact application observer +events. Twelve cases failed against property-owned settlement; eight controls +passed. Two adjacent native request-error cases distinguish prevented errors that +permit commit from uncanceled errors that abort. The original wrapper owner keeps +callback failure identity and late rejection after native commit. Provider scope +is fake-IDB; no physical-crash or arbitrary event-suppression guarantee is added. + +### IndexedDB donor coverage receiving extension + +The [sighted donor survey](oracle-reviews/2026-10-06-indexeddb-donor-survey.md) +now has these receiving owners. The [port audit](oracle-reviews/2026-10-06-indexeddb-donor-port.md) +records calibration, validation and the remaining limits. + +- DP08: `compatibility.test.ts` (fixed tests; no oracle owner (tracked in #2264)) starts two/three native opens against + absent same/independent names, with identical complete declarations. It checks + schema, held restore/readiness, disjoint writes, convergence, restore and a later + upgrade with no orphan blocker. + `packages/db/e2e/indexed-db/value-oracle.spec.ts` receives three-open + histories in all engines. These histories settle all opens before writing; + they do not impose an order on open callbacks or union conflicting declarations. +- DP09: `persistence-values-oracle.test.ts` extends the persistence owner with + independent tagged descriptions in `structured-clone-oracle.ts`. Seven value + shapes cross scalar updates, peer delivery, export/import, first/middle/last + nested clone rejection, metadata rollback and fresh descriptor restore. + Type, offsets, backing bytes, dates and Blob content are observed inside the + receiving realm. Native receiving is in + `packages/db/e2e/indexed-db/value-oracle.spec.ts`. +- DP10: the compatibility owner substitutes six legal database/store names into + the same authored multi-store history. Prefix neighbors, typed keys in separate + transactions, version values, peers and restore distinguish routing mistakes. + `packages/db/e2e/indexed-db/host.spec.ts` (fixed tests; no oracle owner (tracked in #2264)) receives all six names + through single-transaction typed-key insertion, peer update, import, clear, export and fresh restore. + Prefix-neighbor and anchor snapshots must remain unchanged. +- DP17: `packed-consumer.test.ts` supplements declaration tests with installed + tarball ESM/CJS consumers and broken-export controls. The browser spec builds + the same consumer without source aliases and executes it in all three engines. + Tarball overrides pin the installed dependency closure; registry range selection, + older Node/tool versions and every public export remain outside this smoke test. + +The ordinary local Playwright WebKit profile rejects Blob preparation with +`UnknownError`; its two Blob-containing cells require exact rejection and a +healthy suffix. The additional disposable persistent WebKit profile requires +native acceptance and executes the full preservation/capture/rejection/restore +histories. Chromium and Firefox receive the same full corpus. The provider +rejection branch cannot certify success in the persistent profile. + +### IndexedDB approved donor follow-up laws + +The [follow-up audit](oracle-reviews/2026-10-06-indexeddb-donor-followup.md) +records the approved contracts and assertion-killed wrong implementations. + +- **DP02, abnormal closure:** the retirement owner adds idle/ready/loading and + five admitted persistence phases. It compares actual native outcome, caller + settlement, retained public rows, error status, rejected later admission and + fresh restore. Its forced-close fixture compensates for fake-IDB's missing + forced transaction abort by aborting those native transactions explicitly. + Native Chromium storage clearing supplies seven actual close witnesses, + including six with admitted work. This proves neither other-engine forced + termination nor physical-crash durability. +- **DP03, blocked diagnostics:** wrapper open/delete and managed opening expose + the optional native event without settling the caller. Native old/new version, + independent blocked reach, callback count and eventual terminal settlement + are checked. No timeout, cancellation or fallback is added. +- **DP12, value capture:** `persistence-values-oracle.test.ts` crosses six mutable + kinds with update/import behind handler/storage gates. Authored descriptions + compare writer, peer, export and durable rows after caller mutation. Native + receiving includes the immediate update snapshot and held settlement cut. + The core detachment owner checks buffer bytes, view range, aliases in both + traversal orders, foreign realms and custom-constructor compatibility. Native + iframe witnesses receive standard foreign ArrayBuffer/Uint8Array/DataView + aliases in each engine. Shared-memory concurrency, detached/resizable buffers + and arbitrary class private state are outside these copy laws. +- **DP13, dedicated workers:** the native host owner uses real worker and page + contexts for typed-key insert, peer update, import, managed close, retained + error snapshot, late-write rejection and worker recreation/restore. Service + workers, SharedWorkers and background delivery are unclaimed. + +Atomic cross-Collection read-modify-write remains a separate API proposal. +Native durability remains the current receipt. These are accepted design limits, +not deferred implementations of the approved follow-up. + +## Electric recovery demand and scoped persisted restore + +[The cache-eviction design](oracle-reviews/issue-2056-cache-eviction-design.md) +records the generation and expiry laws and their unresolved cuts. The +[PR #2069 review at 51563046](oracle-reviews/issue-2069-review-51563046.md) +records the Query startup handoff, index reverse-order challenge, all review +dispositions, and the repair commit's ORC-012 audit. The +[review of code head `7f1df5c82`](oracle-reviews/issue-2056-cache-generation-audit-7f1df5c82.md) +records the ORC-012 outcomes, RED/GREEN witnesses, and evidence gaps. The +[receiving-gap audit at `87aead1be`](oracle-reviews/issue-2069-receiving-gap-audit-87aead1be.md) +records the native Electric, shared-QueryClient, and old-leader timeout +histories, their mutant outcomes, and remaining cuts. The +real-SQLite owner checks generation rotation, legacy-writer isolation, +warm-run claims, rejection of moved or expired claims, uncertified key-set +evidence after partial writes, and physical collection of a retired row table. +A held registration test rejects a read after collection and removes its late +DDL and metadata. Two further real-SQLite held-registration histories move one +claim while a warm peer retains the physical generation. They require both an +initial registry insert and a destructive schema reset to check that claim +inside their SQLite transactions; the warm peer keeps its schema, rows, applied +transaction, and resume metadata. The persisted owner checks demand-triggered +and timer-triggered expiry recovery, queued old source commits, and an +optimistic mutation held through the accepted truncate. It also holds +hydration, row-scan, and replay results across claim expiry and checks +publication or returned rows after recovery. A startup history holds private +rotation during index bootstrap and checks that the source cannot enter until +new storage is bound. The real-SQLite +owner rejects an expired startup resume record and checks that a public index +event from an expired Collection cannot remove a warm peer's persisted index. +The persisted owner also holds cleanup across cache rotation and requires the +late private claim to release. It holds a peer full-reload read across claim +expiry, checking both returned old rows and SQLite-style rejection at its +second claim check; a live-claim I/O failure remains terminal. The real-SQLite +owner accepts a valid eager Collection ID with the internal cache prefix and +rejects late managed registration that tries to recreate collected storage +after the first claim check. +Its explicit-storage-target history rejects bare IDs of both ordinary and +cache-generated form. It checks all eleven adapter data operations after a +managed generation is collected, compares every per-ID catalog and physical +table after each rejection, reopens the adapter, and retries the old managed +target after explicit eager reuse of the same ID. The contract is claim +authority, not a reserved naming prefix: an application that explicitly +declares an eager Collection with those bytes may create eager storage. The +bare-ID fallback mutant fails at the first post-collection read. The +[explicit-storage-target review](oracle-reviews/pr-2069-explicit-storage-targets-6b0192c19.md) +records the checked paths, mutants, and remaining native-host limits. +The persisted owner also keeps a subset demand through rotation from an old +row version 10 to a new version 1 and requires the new peer notice to request +source reacquisition. Four held-rotation histories cross old row versions 1 and 10 +with an old physical cache's commit or reset before rotation binds. The new +notice has row version 3 or 1, so the old notice can have a lower or higher +version. A fixed and random bounded grammar also varies absent, commit, and +reset old notices, both row versions, and absent or present new notices. The +queued old callback cannot add a source request; the new notice must request +source reacquisition. These use a controlled in-process coordinator whose recording +adapter shares row storage across physical IDs. Exact durable row isolation, +arbitrary interleavings, and native multi-tab delivery remain separate checks. +The persisted owner varies whether a custom on-demand source has an active +subset before claim loss. It requires one source cleanup and a new source entry +after rotation, checks that a retained demand reaches the replacement, and +invokes a late callback from the retired session. The callback cannot publish +or persist its row. A real SQLite receiving history expires the first claim, +then checks reentry, public rows, and the replacement physical cache through +a claimed adapter read. Native-host delivery remains separate. +The controlled owner also holds custom rotation until Collection cleanup; it +requires one old-source cleanup and no replacement source entry after disposal. +The same owner crosses cleanup before and after a held scoped refresh settles. +Its raw coordinator release log requires exactly one release in both orders; +the cleanup-first case rejected the prior double-release path at the late +settlement checkpoint. A real multi-tab transport receiving cut remains open. +The persisted owner also rejects a startup resume read after its claim expires, +including two successive claim losses, then enters the source only after a live +read under the replacement claim. A live-claim read error stays terminal. The +real-SQLite owner now holds a resume transaction before its second claim check. +Its fixed and generated histories cross clock cuts before, at, and after expiry; +row inclusion; zero-to-three rows; and a later peer-claim time. An expired +read makes no generation-specific durable access beyond claim validation; the live peer retains exact rows +and metadata. Removing the transaction-local check and moving it after payload +access fail at separate comparisons. The wrapper's response to that rejection +remains a controlled adapter history, not a composed SQLite startup receiving +history. +The persisted owner holds resume certification across claim expiry: an old +answer or a second claim-check rejection starts private recovery, while a +live-claim I/O failure remains terminal. It also holds initial scoped rotation +while aborting one demand and checks prompt rejection with a sibling still +pending. The CLI owner writes a claimed physical ID through sqlite3, and its +subset-failure cases seed the claimed storage while preserving their public +and durable assertions. +The Browser owner checks two concurrent runs: +the recovering demand reaches its own source owner while the warm run keeps +its source-backed row. Another controlled receiving test delivers a peer +notification to an expired claimant before its renewal timer runs and checks +subset reload. Both coordinator owners offer unload before a held source load +settles. The persisted owner now holds claim renewal after wrapper admission +and varies whether the demand retires before the claim check returns. At load +settlement it requires zero coordinator acquisitions for retired demand and one +for retained demand; the retired case rejected the former late-acquisition path. +It also revokes a claim after a cache subset read, then holds the replacement +acquisition. The active demand must request it once and remain pending until it +settles. The old path made two coordinator requests at that cut. A separate +pre-read expiry history and peer-notice histories cover their narrower cuts; +these controlled paths do not prove every real-host scheduling interleaving. +The Browser coordinator owner holds an acquisition across a failed release and +varies whether the acquisition settles before or after release starts. A pending +release retry survives both orders. Its unacquired-release case observes no +outbound RPC. The overlapping case failed before the acquisition-success path +stopped clearing release retry debt. These are controlled transport schedules; +a real tab crash and an indefinitely unreachable leader remain outside them. +The Browser coordinator owner also checks that a managed local mutation carries +its exact claim into the coordinator-created transaction. Its recording-adapter +law failed before the repair at the target-kind comparison. A follower-RPC +claim-loss mutant fails at its success checkpoint. A real SQLite +receiving history accepts that write and rejects a late local mutation after +rotation without recreating the retired table or registry. Direct coordinator +callers that omit a claim remain outside this adapter law; the coordinator +interface still accepts eager Collection IDs. +The Electron IPC receiving owner forwards claim, renewal, rotation, release, +cache-claim read contexts, exact-ID reconciliation, and durable term reservation +through protocol v6. Its in-process main adapter +witness keeps the logical Collection ID for adapter selection while reads +address the claimed physical storage ID. A direct shared-renderer-adapter +claim/read/release history selects the authored logical ID, and a raw managed +envelope without it fails before main-process selection. The shared SQLite adapter contract +runs its claimed-cache cases over that bridge. A custom main-process adapter +can explicitly disable managed cache generations and retain the full-shape +fallback. The local Electron binary is unavailable, so an actual +main/renderer process receiving cut remains with the Electron runtime-bridge +owner. + +The Electric descriptor owner now holds an old SDK snapshot across claim +expiry. The replacement provider session settles both demands from fresh +snapshots, rejects a pending old txid wait, and excludes a late old row from +public and durable state. A second delivery lands during rotation, before the +replacement stream starts; it rejects old callback admission into the new +cache. Initial scoped recovery also forgets imported old txid evidence, and +retiring an incomplete old stream transaction releases its commit turn so a +new demand can start. A progressive atomic swap that throws in `getKey` +leaves its reserved transaction open; when the SDK then gives up, Electric +must abandon it, and a subset load that waited runs. On `7a6f32fd7` the swap +did not mark its transaction open, so no stop could abandon it and the load +waited until its checkpoint timed out. The metadata-only commit shares the +same begin and commit helpers but has no fallible step between them. These histories killed the one-phase restart, retained +acknowledgement, and open-transaction designs at their named checkpoints. The +Electric descriptor owner also holds rotation while aborting one old demand: +that demand rejects promptly and a sibling later applies fresh source rows. +Its overlapping-rotation history rejects a callback from the retired middle +provider session, then requires the final provider session to apply a fresh +subset demand after the second gate opens. +The unabortable restart wait failed at this settlement checkpoint before the +repair. The SQLite owner rejects coordinator stream-position, replay, and +index work from a specific expired claim even while a peer claim is live. An +expired run recovers privately while the healthy current cache remains +claimable. The older Electric resume-race owner explicitly exercises adapters +without managed generations; its unknown and missing on-demand evidence cases +now require full-log replacement. The scoped reset settlement owner uses an +abstract managed claim for that one applied-receipt law; the SQLite owner +checks physical generation separation. +The [bounded cache-authority design grammar](../../review-evidence/cache-authority-tla/README.md) +also checks incompatible resume evidence followed by claim expiry. SQLite's +transaction-time claim test makes that rotation private. The SQLite receiving +owner now holds the real adapter's rotation after a controlled source starts +scoped recovery, expires that run's claim while a peer remains live, then +checks the peer's public and durable rows and unchanged resume metadata, an +empty then fresh subset in the private generation, its reset marker, the +recovering claim's storage ID, and a later claim of the unchanged head. A +mutant that uses the expired claim's remembered storage ID advances the head +and fails at that comparison. The controlled source supplies the recovery +decision. The Electric resume-race owner now supplies an incompatible stored +shape identity, reaches Electric's own scoped-recovery decision, expires its +claim before SQLite's rotation transaction, and checks that a warm peer keeps +its rows and resume metadata while the recovering run gets private storage. +This witness failed before Electric stopped writing a reset marker into the +shared generation. A second real-SQLite witness holds rotation after the +storage transaction but before public truncate. It delivers a retired Electric +callback and starts a distinct demand in that interval. The replacement +session refetches the active empty subset and applies fresh rows for both +explicit demands. Only those rows reach public and durable state. A deliberate +wrong row in the rotated storage fails the held private-cache assertion. The +grammar combines those actions; these fixed receiving cuts supply the missing +implementation evidence. +It does not model physical garbage collection or arbitrary callback schedules. +The separate [receipt-settlement grammar](../../review-evidence/cache-receipt-tla/README.md) +checks the accepted-versus-visible distinction during scoped recovery with a +persisting optimistic transaction. It is bounded model evidence; the +optimistic-history and Electric owners remain the production checks for those +receipt laws. +The installed-SDK delivery owner now holds an old HTTP subset response across +managed rotation and observes fresh demand settlement, public rows, and the +new cache's durable rows after that old response arrives. Its adapter is a +controlled Map; real SQLite and native multi-tab delivery are separate cuts. +The same owner uses real SQLite and the installed SDK for a cold restart after +expiry rotation left a partial row and no reset marker. It observes the row +absent before and after an empty source subset snapshot, including with an +explicit Electric offset or handle. The explicit-cursor cases were RED at the +pre-response public-row checkpoint before the cache-trust predicate changed. +This rules out both early cache hydration and relying on source completion to +delete that row. +The descriptor owner also checks that cleanup during a held rotation fulfills +after rotation settles. It does not establish a prompt cleanup bound while the +adapter rotation remains held. +The Query ownership owner now pairs a real QueryClient with managed Node SQLite. +It advances a cache claim beyond expiry, uses a disjoint demand to trigger +rotation, and requires an already active subset to reacquire its distinct row +without another caller request. A later demand with stale QueryClient data +waits for a new fetch; an old fetch and a public utility refetch cannot publish +or return their retired result. A held real SQLite retention scan cannot delete +a same-key row reacquired in the replacement generation. A released acquisition +cannot launch another fetch after its held old result settles. These are fixed +histories at public-row, request-count, and caller-settlement cuts. A paired-Collection real-SQLite history now gives two on-demand Query +Collections one QueryClient and prefix-overlapping but distinct Query keys. +Only the earlier claim expires; its rotation and fresh fetch must leave the +warm peer's public row, Query cache entry, durable row, current storage ID, +and fetch count intact. A broad prefix-eviction mutant failed at the peer +cache-entry comparison. Shared exact-key cleanup follows the documented Query +cache contract; arbitrary retention schedules and custom provider restart +implementations remain outside these histories. +The expiry and late-fetch histories also read the tested run's claimed physical +SQLite generation after fresh settlement and after the retired fetch resolves; +both cuts require the fresh rows and exclude old rows. A separate active-head +claim would inspect a different generation when an expired run rotates privately. +The same owner distinguishes a private startup rotation from a warm startup: +only the rotated run must refetch a cached QueryClient success. It also holds a +peer Query fetch that began before source entry through that rotation. Both its +late success and late error require a new fetch before demand fulfillment. +Two queued rotations have a separate held-second-rotation cut: the first +recovery cannot reacquire an active Query subset or start a provider fetch. +After the second rotation, that demand must fetch and apply a fresh row. +The Query owner also checks new keys with and without a stale QueryClient value +after claim rotation: one provider fetch satisfies the generation boundary in +either case, with no extra cancelled fetch. +A rejected SQLite rotation instead puts the Collection in public terminal error +and rejects later demands with that same error; this is fail-stop, not a silent +observer freeze. These are real SQLite/QueryClient cuts, not arbitrary provider +failure schedules. +The persisted owner also holds active rotation across index bootstrap and checks +that an index added during the hold targets new storage. Its adapter call log +does not prove native SQLite DDL completion. A virtual-clock history holds the +provider restart after rotation and requires claim renewal once it resumes; +index creation and renewal failures stay best-effort without changing public +readiness. A held bootstrap lets index removal settle before the earlier +creation completes; the wrapper reconciles the final durable index with the +Collection's declaration. Re-adding that index during reconciliation has a +separate fixed witness. The SQLite resume-snapshot owner now runs both +reverse-order histories through the persisted wrapper and real `node:sqlite` +DDL. It holds the first new-generation ensure until removal settles, then +checks the Collection declaration, index registry, and `sqlite_master` after +rotation. Its re-add history makes the replacement ensure finish before the +held reconciliation removal, so a missed final re-ensure leaves the real index +absent. Host-specific native DDL scheduling remains outside these Node cuts. +The persisted Electric interleaving owner holds an uncancelable local cache +read after demand abort. It requires the returned load to remain pending until +the cached row is public, then reject with `AbortError`; both fixed and random +campaigns were RED at the early-settlement checkpoint before the wrapper +separated pre-hydration abort from in-flight baseline settlement. A separate +composed Electric probe confirms prompt abort while a later source snapshot is +held after local hydration. These cuts do not establish cancellation of an SDK +request that has already delivered its rows through the shared stream. + +The persisted owner refuses a managed custom adapter that lacks rotation, +renewal, or release, and refuses recovery from a managed source without a +provider-session restart capability. The Browser owner holds the old leader's +release message while the new generation starts its own source demand. Its +host factory owner checks that configured claim lifetime and clock reach the +SQLite adapter. A composed receiving history advances that clock past expiry +and rejects a peer update before reloading the demanded subset. Cross-package +Browser runs require freshly built core package +artifacts because package exports otherwise resolve stale `dist` files. + +Reachable limits remain. A source that declares a restart capability must +itself retire callbacks synchronously when recovery calls it. Claim expiry +after core publication but before SQLite accepts the write rejects the receipt +and leaves the Collection in terminal error; a real-SQLite hostile probe +confirmed the row can remain public without becoming durable. The ordinary +durability-failure law permits that fail-stop, while automatic expiry reload +covers admission before publication. The first new-format +claim isolates but does not reclaim legacy storage. +The Browser coordinator owner now drops an old leader's release reply after +its adapter unload, transfers leadership, lets the real RPC timer expire, and +checks that retry retires the outbound acquisition without replaying demand or +unloading twice. A release-without-retirement mutant failed at the new owner's +unexpected load. Claimless direct-adapter access to a physical ID after its +catalog row is collected remains unproved and has a reproduced orphan-write +probe; rejecting it while retaining arbitrary eager Collection IDs requires a +durable distinction for former physical IDs. +The Browser owner +now has a combined receiving history over real wa-sqlite and the controlled +Browser coordinator wire. Two runs hold distinct claims on one generation; +only the earlier claim expires before its renewal timer. A real durable peer +write precedes a matching, manually delivered notice. The expired run reloads +into private storage while the warm peer retains its exact public and durable +row, live claim, and source ownership for another demand. The current head +remains unchanged. An always-rotate-head mutant failed at the head-ID +comparison. Native coordinator emission, multi-tab or OPFS scheduling, and a +composed SQLite-to-wrapper startup rejection were open at that review head. +The SQLite owner now composes a real claim rejection during startup with the +persisted wrapper: the expired run gets a private cache and a fresh demanded +row, while a later live peer keeps its public row, durable row, and resume +metadata under the unchanged head. Its separate three-generation reachability +model varies release, expiry, renewal, zero or two rows, and collection +checkpoints immediately before and at both renewed expiries. It compares +retained row, tombstone, resume, and key contents; both physical tables; +native indexes; every generation-scoped catalog; and claim identities. +Wall-clock passage alone is not a sweep checkpoint. The persisted notice +grammar now holds the fresh source receipt behind an optimistic transaction +and checks a pending constrained demand, stale-row quarantine, the public +publication trace, and release of the exact `(storage ID, options)` refresh +acquisition. Its controlled coordinator supplies the receipt wait. The fixed +Browser receiving cut below supplies that premise with a real coordinator on +one schedule. +The Browser OPFS owner now has a fixed two-context receiving history in which a +real source commit makes the coordinator emit a physical-ID notice over native +BroadcastChannel. One receiver callback is held until its claim expires; +its private reload leaves the warm peer's public and durable rows and current +head intact. An explicit demand stays pending until the fresh source receipt +applies. The renewal timer lies beyond the fixed test window, and the receiver +checks no refetch before releasing the delivered callback. The same fixed +two-context history now also runs the installed Electric SDK +in the expired tab against controlled HTTP source snapshots. It holds an old +subset request and the new source response across the native notice, then +checks pending demand, public and durable rows, and the warm peer. Dropping the +held native callback fails at the refetch-entry checkpoint. Arbitrary native +scheduling and a live Electric service rather than controlled HTTP remain +separate unproved cuts; these +fixed receivers do not close every cache-eviction interleaving. + +The [cache-generation oracle audit](oracle-reviews/issue-2069-cache-generation-oracle-audit-53ded62d8.md) +records the generated grammar controls, killed mutants, direct replays, and +the remaining handoff limits for code commit `53ded62d8ef70ccd63b75d0e6dbc0a964f14b17c`. +The [cache-claim gap audit](oracle-reviews/issue-2069-cache-claim-gap-audit-575fb9473.md) +records the added receiving witnesses, hostile checks, direct replay, and +remaining limits for code commit `575fb9473dd7d8eccbe469d0831dc9f71cffddd4`. +The [native SQLite index receiving audit](oracle-reviews/issue-2069-native-ddl-receiver-b4d57ced6.md) +records the two reverse-order DDL witnesses, hostile checks, and remaining +host-specific limit for code commit `b4d57ced63d513dd0dcb794b8e8bb2a840577758`. +The [follow-up review audit](oracle-reviews/issue-2069-native-ddl-review-followup-b42b94b73.md) +records the exact recovery-settlement and target-table assertion repairs for +code commit `b42b94b73ddba79a6a87fa93e71787dd126ea1bd`. + +[Issue #2056 evaluation](oracle-reviews/issue-2056-full-mode-recovery.md) records +the original RED and candidate comparisons on head +`f43a16522c990134ae993235a312d2d5e433dc8a`. The later scoped design +revises the Electric guide's on-demand cold-recovery contract. The earlier +“Complete” literacy status is not a claim that every recovery law or provider +assumption is enforced. + +Primary owner: `packages/electric-db-collection/tests/electric-descriptor-isolation-oracle.test.ts`. +Its tag-history fixture has no managed cache claim, so an incompatible on-demand +resume waits for a full source replacement while cached rows remain visible +until the source transaction begins. The controlled ShapeStream rejects snapshot +requests in full mode. Tagged cold, legacy, explicit reset, warm resume, +interruption, partial replacement, and late demand have named cuts. Managed +scoped recovery has separate descriptor and installed-SDK histories. The +fixed/random tag campaigns share budget and grammar; a direct seed/path replay +runs only the selected property. An unconstrained subset demand can fetch all +rows because the caller requested all rows; it does not prove bounded network +work. + +Receiver: `packages/electric-db-collection/tests/electric-sdk-delivery-oracle.property.test.ts`. +A real installed SDK receives two launches through the Electric adapter and +persistence wrapper with controlled HTTP and durable storage. Direct demand and +public live-query preload settle through changes-only scoped snapshots in a +managed fixture with separate durable stores for retired and current cache +generations. The receiver verifies `id = 1` and its bound parameter on both +launches. It permits one snapshot to satisfy the live query's active demand +and checks its public and durable row after settlement. +It does not require a second physical request for the same subset. Lost tags, +changed shape, and malformed resume state keep stale B in the retired store +without exposing it after an A snapshot or an empty B +snapshot. The changed-shape case makes B's empty snapshot first. A third launch +with no demand starts no full-shape transport or unrestricted local subset +read. The fixture models generation routing, while real SQLite independently +checks claim enforcement and physical tables. Another receiving witness rejects +concurrent SDK snapshot invocation on the shared cursor. Compatible untagged +resumes and the unmanaged full-log fallback remain separate controls. +An installed-SDK abort witness holds subset HTTP across external stream +retirement, then returns a valid row. The demand rejects, the row stays absent, +and an identical demand is not certified as loaded. +The two-launch receiver also delivers a coordinator invalidation after a +paginated scoped snapshot. The persistence owner crosses targeted, paginated, +full-reload, and reset notifications after an active source row. Its +post-invalidation public-row checks distinguish source authority from durable +cache; the non-scoped paginated reload remains a neighboring control. +The persistence owner also checks that a scoped cache clear preserves initial +Collection loading until the source marks ready. A held optimistic handler +checks that the same clear settles before the handler releases: core applies a +truncate immediately, so the proposed accepted-versus-applied deadlock does +not arise on this path. Its subset settlement law remains separate. +The persistence owner now calls baseline hydration after starting scoped +recovery with a stale durable row. It requires that no row enter the public +Collection and that no full-row resume snapshot be read. This is a direct +capability-ordering witness; ordinary Electric startup does not call baseline +hydration after choosing scoped recovery. The buffered partial-update recovery +read has a gated failed-local-load history: a partial source update may appear, +but it cannot inherit stale durable fields. Removing the scoped read guard +fails at that public-field checkpoint. +`electric-resume-snapshot-races.test.ts` also holds a real SQLite metadata +read across row loss or a committed replacement. Unknown and missing key-set +evidence start changes-only without publishing the cached rows; a row-1 demand +then applies only its source snapshot. This is a fixed Node SQLite receiving +witness, not a generated-history claim or a native Expo result. +Its two cache-generation receiving histories use Electric's real resume +classifier and wrapper with real SQLite. An incompatible stored shape reaches +private rotation after claim expiry without overwriting a warm peer's resume +metadata. A later active-session claim expiry holds the boundary after SQLite +rotation and before public truncate; a retired callback cannot write there, +and a demand started in that interval settles after its replacement subset +applies. The ShapeStream callback schedule is controlled. An installed-SDK HTTP delivery +at this exact intermediate cut and native-host execution are not established. + +Applied-settlement owner: `packages/electric-db-collection/tests/electric-oracle.property.test.ts`. +Held optimistic application distinguishes readiness from subset completion for +demand both before and after readiness. A later same-run reset distinguishes a +current replacement obligation from a permanently fulfilled startup gate. +Concurrent initial-error and cleanup cuts observe settlement and lifecycle +authority; cleanup quietly settles in-flight adapter loads. A sibling-demand +abort cut requires one canceled demand to reject promptly while its sibling +and a replacement of the canceled demand wait for the full source snapshot and +succeed after its row applies. A separate external stream abort rejects its +current full-log demand and never certifies an identical later demand as loaded. +These full-log wait obligations still apply when scoped recovery is unavailable; +the scoped path uses SDK snapshots and applied receipts. +The changes-only path has the same refusal both when a subset transport returns +after stream retirement and when an applied receipt is held across that +retirement. +The full-mode retry histories now reject their pre-error demand, then require a +new demand to succeed after a same-run recovered snapshot, both with and +without a held applied receipt. An installed-SDK controlled-HTTP receiver +exhausts 503 backoff, invokes the user's retry +handler, and acquires a new demand while the retry HTTP request is open. It +requires that demand to wait, then succeed after the new full-log up-to-date. +A missing-header response checks that the SDK's refusal to retry rejects later +demands rather than leaving a renewed gate pending. The +scoped reset history acquires a snapshot before must-refetch, then requires +subset-end to apply its row before demand success without waiting for a full +up-to-date. The neighboring full-mode reset retains that stronger wait. The +Electric durable fixture has an adversarial insert check that rejects retained +old columns or tag metadata; it is driver validation, not a new model owner. +The Electric adapter owner now holds an asynchronous user retry decision and +checks that a new full-recovery demand remains pending until source replacement, +while the pre-error demand rejects. It also checks caller abort before subset +transport settlement, while a request is queued behind the SDK cursor, and +while an applied receipt is held. Each original behavior failed at the caller +settlement cut; the fixed driver requires prompt `AbortError` without certifying +that demand. The explicit-resume neighbor keeps malformed durable metadata +untouched until source evidence arrives. The persistence owner now invokes a +retired scoped-recovery capability after cleanup and restart; the original +cleared the replacement Collection, and the lifecycle guard preserves its row. +These fixed schedules do not prove arbitrary overlapping retry decisions or +all failure paths through `startScopedRecovery`. A valid scoped-clear commit +failure with an exact public sync-error observation still needs a witness; +an attempted subscriber-throw fixture did not reach that callback during the +local clear and supplies no evidence for the review's error-report claim. + +Remaining witnesses before broader closure: + +- Electric tag/history owners: unknown or missing key-set evidence combined + with tagged scoped demand, multiple resets overlapping held application, + and partial updates arriving before a scoped baseline. Repeated provider + health, SDK retry-loop exhaustion, and a + scoped reset overlapping a held applied receipt also need witnesses. The + untagged SQLite metadata-read race is covered above. +- Persistence/coordinator owners: native SQLite reads, browser/electron + cross-tab transport delivering an invalidation during cache quarantine, and + source versus local-only truncate durability. Controlled coordinator + delivery and installed-SDK receipt cover the wrapper response, not the + multiprocess transport premise. Scoped recovery also needs an explicit + durable-cache eviction or certification policy: the current reset marker + makes later uncertified starts repeat quarantine, and deleted durable rows + can remain on disk. Neither a safe offline subset certificate nor bounded + storage growth is proved here. +- Installed-SDK delivery and Electric service E2E owners: actual subquery/tag + emission, overlapping provider responses, and the scoped restart schedule + against a live service. +- Expo `expo-persisted-collection.e2e.test.ts` and emulator owners: the offered + node:sqlite fixture, native storage, and actual process relaunch. +- Descriptor/tag and SDK DNF owners, if durable tags are selected: complete + selected membership and silent active-condition changes persisted atomically + with rows and the resume certificate, including migration/reset admission. + +No individual maintainer assignment or approved deferral is implied by these +executable ownership destinations. The bounded controlled-HTTP and Map-backed +persistence witnesses do not establish class closure across those hosts. + +## Paced persistence serialization + +Issue #2058's primary owner is +`packages/db/tests/paced-mutations-oracle.test.ts`. Its held-write model +crosses debounce/throttle, explicit leading/non-leading and omitted leading +with trailing enabled, and successful release one tick before, at, one tick +after, or ten ticks after the successor's timer edge. It observes handler +concurrency, start times, transaction states, receipt settlement and identity, +and optimistic rows at admission, timer edges, release, and drain. The original +implementation fails 18 of 24 histories at the held edge; the six +release-before-edge controls pass. The model's immediate handoff after both +prerequisites hold is the chosen pacing policy. The original overlap violates +the independent serialization promise regardless of that policy. + +Adjacent histories check renewed debounce quiet, throttle spacing from actual +starts, leading-only admission isolation, same/distinct-key success or failure, +pending cancellation, manual rollback of a persisting transaction, and +synchronous nested admission. Manual-rollback witnesses cross all three serial +strategies, including queue, and observe handler overlap after the public +receipt has rejected. The wrong implementation fails at that intermediate cut; +the repaired implementation waits for the handler itself to return. + +The owner also drives a leading debounce successor canceled while the first +handler is held. A third admission after either direct rollback or same-key +cascade must start after its own quiet edge and the held handler's release; +the old callback cannot consume the third call's persistence obligation. The +reviewed implementation fails at that handler-start cut, while the ownership +repair passes both histories. A failed optimistic callback must not retain a +newly created empty pending transaction: admitted debounce, dropped debounce, +and queue paths now have scope-retention witnesses that fail on the reviewed +implementation and pass after cleanup. The owner checks the chosen group-rejection law for a nested admitted call +both with and without an earlier pending call. All receipts sharing the failed +pending transaction reject with the callback error, no member reaches the +backend, and a later independent call persists. The earlier-pending regime +failed at the transaction-state cut before the group rollback repair; both +factories now pass at the throw and scheduled edges. A separate held-handler +matrix crosses debounce, throttle, and queue: manual `commit()` throws before +changing a pending receipt, and the strategy still starts that write only +after its timer edge and the prior handler's release. + +The owner also checks one strategy shared by two managers. Reentrant +leading/trailing and ordinary trailing histories retain both separate +transactions at one timing boundary. A renewed-quiet history moves another +manager's eligible callback to the new quiet edge while the first handler is +held. A held-handler rollback keeps the second manager's write behind the first +handler. The reviewed code fails the second +backend-start assertion for both debounce and throttle; the keyed pending-run +repair passes. The owner does not yet cover provider echo, cleanup during a +held handler, longer arbitrary bursts, held default/omitted-trailing +combinations, or more than two managers. It owns those future source-level +witnesses. Leading-only +throttle admission-window timing while a write is held remains a separate +policy question; the existing serialization and minimum actual-start spacing +do not depend on its resolution. Real server ordering, framework unmount +scheduling, and published npm behavior require receiving integration witnesses; +the present evidence is controlled DB source execution. The revision-bound +review record is `docs/contributing/oracle-reviews/issue-2058-paced-serialization.md`. + +## Opt-in operator bundles + +Grouping, includes, and pooling ship only with the value that names them: an +aggregate or `groupKeys`, an include function, or `pooledLiveQueries`. Joins +are core. `packages/db/tests/opt-in-operator-bundle.test.ts` owns that law. It +bundles small apps from source with esbuild and reads which modules reach the +output. Each of the 10 operator values runs alone, and every one of the 8 +combinations of operator families runs with rotating values. An operator's +modules must be present exactly when the app imports one of its values. The +join compiler and public-key reduction must be present in every app. The +single-value runs are positive controls, so a renamed or moved module cannot +pass the absence checks. A mutant that statically imports +`BucketFacadeAdapter` into the live-query builder, and one that uses a +`group-by.ts` helper from the live-query Collection builder, fail it. + +The owner checks module reachability only, not byte budgets; `pnpm +size:compare` measures bytes. It bundles `@tanstack/db` alone, with other +packages external, so it does not check db-ivm operators or framework +adapters. Query results under the new API are owned by the existing query and +includes oracles, which run with migrated queries; no oracle compares a +query's old and new spellings on one input. + +Operator code is part of query identity. The +[IR identity oracle](https://github.com/TanStack/db/blob/main/packages/db/tests/query/ir-stable-identity-oracle.test.ts) +runs every combination of `groupBy` (none, bare keys, `groupKeys()`), a select +aggregate (none, built, without its grouping compiler), and an include (none, +built, without its runtime), checks each compile outcome against a small +model, and requires distinct identities for any two combinations whose +outcomes differ. Removing the identity mark for `groupKeys()`, for an +aggregate without its compiler, or for an include without its runtime each +fails it. A second check runs a wider product: `groupBy`, a select, HAVING, +and `orderBy` aggregate each built or without its compiler, an include as a +field, in a `caseWhen` branch, or with a child aggregate, each built or +stripped, and an `eq`-filtered query without a select that a pool can serve. +It takes each outcome from production compilation and requires one outcome +per identity, for the IR identity and for the prepared identity with +`pooledLiveQueries`. Each identity mark's removal fails it, and so does a +pool shortcut that accepts a `groupBy` query. Bare and `groupKeys()` keys +beside an aggregate may share an identity or not; the law leaves that open. + +Open counterexample, not fixed by this change: the ordered-window random +campaign in +`packages/db/tests/query/load-subset-replay-refinement-oracle.test.ts` fails +with `TANSTACK_DB_ORACLE_SEED=1593300187` (path `121:1:2:3:2:2`) and with +seed `-1005431615` (path `68:1:2`). Both shrink to one history: `hold`, +`truncateMarkers`, `settle` with a load failure, over an ascending limit-one +window on an on-demand marker source with includes. The convergence check +sees a terminal error. Main at `65d9ada50` and the merge-base `5649a07d4` +fail with the same counterexample, so the operator change did not introduce +it. The campaign reaches this defect in ordinary runs, so CI can fail on it +intermittently until the defect is fixed. Owner: the load-subset replay +owner. Needed witness: a decision whether a truncate during a held, then +failed, load may end the window in a terminal error, and the fix or model +change that follows. + +Open counterexample on `main`, not caused by this change: the random +campaign of `packages/db/tests/query/ordered-work-oracle.property.test.ts` +(`eager indexed ordered windows > keeps rows and per-change work bounded`) +fails with `TANSTACK_DB_ORACLE_SEED=1023848973` (path +`34:3:3:3:3:4:3:3:4:4:4`). The history is an on-demand, refetch-signal, +anti-joined, unindexed, ascending limit-one window with nulls first: upsert +row 0 as ineligible, then delete the visible row. The check finds two +full-source requests after a signalling refetch, where it expects none. +`main` at `5649a07d4` fails the same way. Seed `-436579979` reaches the same +check through a joined window (`filter: joined`, `via: link`), fails on +`main` at `5649a07d4` too, and in CI timed out at 5 seconds while it shrank. +The campaign can fail CI intermittently until the defect is fixed. Owner: the ordered-work owner. +Needed witness: whether this unindexed window may use full-source recovery, +which this map allows for unindexed underfilled continuations; if so the +check is too strict for it, and if not production is wrong. + +The same comparison fails on main `6c1ca8f8f` and PR #2229 at `7fff3a7e0` +with seed `1902868771`, path `19:2:2:4:4:4:5:9:18:15`. This joined-filter +history changes eligibility through `link`, then touches another row. Both +exact replays report two full-source requests where the check expects zero; +neither reports type errors. The existing contract question remains open. + +Open observation without a trace: in one CI run of this change, the random +campaign of `packages/query-db-collection/tests/refetch-ordered-prefix-oracle.test.ts` +timed out at 120 seconds before it printed a seed. The previous CI run took +29 seconds, and twelve local runs with random seeds passed in 38 to 50 +seconds each. Owner: the refetch ordered-prefix owner. Needed witness: a +seed that reproduces the slow history, and whether it is a slow shrink of a +real counterexample or a slow passing history. + +Open observation without a trace: during this change, one run of the random +campaign in `packages/db/tests/query/includes-oracle.property.test.ts` failed +on the branch. The run did not record its seed or counterexample. Thirteen +later dedicated runs and two full-suite runs on the branch passed, and the +campaign was not run on `main` at that time. Owner: the includes oracle owner. Needed witness: the failing seed and +path from a later run. Until then, this entry cannot tell an operator +regression from an older defect. A later failure of that campaign must record +its seed here before anyone calls it pre-existing. + +### Ordered signalling-refetch work scope, October 27 + +Owner: `packages/db/tests/query/ordered-work-oracle.property.test.ts`. The +architecture's "Ordered requests, continuation, and recovery" law permits +full-source fallback for an underfilled joined prefix without an order index. +The signalling-refetch assertion therefore forbids extra full-source requests +only when finite continuation is available, indexed joined windows and direct +source-filter windows. Unindexed joined histories retain all public row, order, +publication and other applicable work comparisons. They do not claim a bounded +refetch transfer. The authority is the existing architecture contract, not a +new behavior decision. Replay seed `-931507916`, path +`21:3:3:3:4:3:4:4:4:5:4:4:4:4` reaches the permitted fallback after an ineligible +upsert into an empty source and a joined-marker change. This was an overly +broad oracle assertion, not a production repair. + +### RC Effect explicit source metadata (2026-10-28) + +Owner: `packages/db/tests/query/virtual-row-fields-effect-oracle.test.ts`, sharing +the small `virtual-row-fields-source-oracle.ts` source judgment with the root +metadata owner. It recomputes explicitly selected ordinary metadata fields and +full before/after deltas independently, then receives actual Effect onBatch and +named callbacks across 200 finite single-source, LEFT-join and populated-union +histories. It preserves previous values, multiplicity, field absence/undefined, +typed source keys, and opaque event-key continuity only within active intervals. +Held mutations, independent confirmations/outcomes, both settlement orders, +remote updates, peer exit/reentry and next-use reach 5,512 lane/channel/cut cells +exactly once. No implicit Effect-root metadata or output Collection ID is claimed. + +Open P09: all 64 LEFT-join histories emit a startup update for a never-entered +unmatched occurrence after its load finished. Those initial comparisons remain +RED on unchanged `858b174a6`; histories continue and all later comparisons pass. +The other 136 native histories and 16 controls pass. Independent hostile +overlays reject dispatch, previous-value, duplication, colliding-key and extra +undefined defects; lawful key renaming, fresh reentry and empty partitions retain +the baseline. Per-lane omissions and seeded partial selections now fail reach. +The finite owner explicitly disables the irrelevant shared replay exemption; +O26 retains a broader campaign inventory for that harness class. See chunk 16 +in the RC chunk record for exact fingerprints, evidence and review. + +Limits: eager controlled core sources, scalar rows, one mutation per source, +and these named paths. No real provider/framework, arbitrary scheduling, +ordered repair, same-source overlap, callback-input sanitization or exact +internal graph-run callback-count guarantee follows. P09 requires production +repair after the full oracle campaign; this entry makes no closure claim. + +### RC bound schema mutation receivers (2026-10-28) + +Owner: `packages/db/tests/live-query-bound-options-oracle.ts`; shared driver: +`live-query-bound-options-support.ts`. React `bound-schema-options-oracle.test.tsx` +and Preact `bound-schema-options-oracle.test.ts` receive the same model through +real live hooks, plus ready-source React Suspense. All 216 finite native +histories pass on unchanged `858b174a6`: nine ordered absent/trim/uppercase +pairs, query/queryKey replacement, independent key proposals and two padded +inputs. Held client updates use retained schemas until identity changes; +synced rows remain raw. Immediate reads, committed hook data/state, handler +original/modified/changes, settlement and later next-use are compared across +2,592 distinct reach cells. Extra own undefined fields and omitted followup +mutations now fail; validation-bypass and source-input-corruption overlays fail +the intended comparisons. See the RC chunk record, chunk 15, for frozen hashes +and independent evidence. Limits: valid synchronous schemas, scalar rows, +explicit string keys, settled mutations before replacement and ready-source +Suspense. This JavaScript runtime receiver makes no typed-schema, initial +suspension, SSR, concurrent-render or provider claim. Default-getKey absence +and async/invalid schemas remain outside it. + +### RC mutation confirmation expansion (2026-10-28) + +Shared rule: `packages/db/tests/mutation-confirmation-oracle.ts`; receivers: +Query `ownership-lifecycle.oracle.test.ts`, Electric `electric-oracle.property.test.ts`. +The finite grammar covers CRUD, absent/present peer, fulfilled void/legacy returns, +rejection before/after a server write, no confirmation versus explicit Query +refetch/direct write or Electric txid acknowledgement. Real utility waits are +held before response delivery. Full ordinary rows, exact errors, terminal state, +settlement-waiter snapshots and real fetch/provider-work counts are checked. +78 Query and 42 Electric histories pass on unchanged 858b174a6; 13 calibration +controls reject wrong rows, outcomes, errors, early acknowledgement and extra work. +O17 receiving expansion:24 ordered CRUD/outcome/invalidation histories and six +post-handler durable-receipt histories now pass. The latter rejects an in-memory +leaked-handler-count mutant in all six histories after a calibrated task-turn +cut. Ordered observations cover a two-row numeric window and eventual fixed +point; late application rejection, concurrent handlers, native SQLite and +live provider timing remain outside these expanded witnesses. +Adapter generated return-type admission now has the O14 source receiver below. Persistence wrappers, +multiple concurrent handlers and live Electric providers remain separate paths. + +The [chunk 18 review](oracle-reviews/2026-10-28-pr-2171-oracle-chunks.md#chunk-18-actual-authored-mutation-handlers-reviewed) +adds exact authored-handler receivers. `packages/db/tests/documented-mutations-oracle.ts` +owns the finite guidance grammar and handler-Promise judgment; its support module +extracts actual initializer ASTs. Query and Electric's +`tests/documented-mutations-oracle.test.ts` use the shared mutation judgment with +complete ordinary rows. Eighteen fences supply 30 current and three explicitly +negative handlers. Their 190 histories distinguish transport rejection from a +resolved HTTP 503, and server commit from handler confirmation and settlement. +Query has 132 passing histories and two G01 settlement failures; Electric's 56 +pass. G01's missing confirmation stages remain unreached and fail reach +separately. Forty-three controls pass. The original instrumentation supplied a +missing await through a handler-return gate; independent review demonstrated +two false greens. The repaired receiver observes the actual handler Promise +while genuine source delivery is held. Both identical omitted-await overlays +now fail, while already accepted direct-write neighbors pass. Shared seeds +preserve reach, and partial native selections cannot waive missing cells. +G01 remains open for the later guidance-repair phase. This is exact-handler +reception through controlled backend/SDK collaborators, not full factory, +schema, component, concurrent-handler, persistence-wrapper or live-provider +coverage. Broader authored guidance and diagnostic consistency remain O21 work. + +### RC public mutation-handler type admission (2026-10-28) + +Owner: `packages/db/tests/mutation-handler-types-oracle.test.ts`. An independent +output/transport rule checks generated Query/Electric consumers through Config, +explicit factory and inferred factory entry points, with/without schema and +Query rows/envelope select.36 consumers contain4,212 cells; all702 declared +states reach six histories. Promise accepts void/undefined and rejection, +including a synchronous never result; fulfilled values and synchronous void +are rejected. Six controls calibrate missing/duplicate/misattributed diagnostics. +All42 tests pass on unchanged858b174a6. Independent compiler probes reject +unrelated same-code and body-local Promise errors substituted for handler +errors. Source imports do not establish packed declarations or npm consumers; +O22 owns that receiving boundary. Full evidence is in the RC chunk record. + +## RC release histories and replay observations + +The shared independent release owner is `scripts/release-history-oracle.js`, +received by `release-history-oracle.property.test.js` and +`publish-history-oracle.property.test.js`. The [chunk 13 review record](oracle-reviews/2026-10-28-pr-2171-oracle-chunks.md#chunk-13-release-histories-actual-changesets-commands-and-finite-replay) +records policy authority, grammar, reach, source fingerprints and calibrations. +It checks 72 version timelines, 14 seed failure/retry timelines and 40 actual +Changesets command histories, with six publish checker controls. Numeric 9/10 +boundaries, strongest pending bumps, standalone stable work, dependency profiles, +all three exit forms, exact consumption/changelog attribution, rollback and +publish retries are exercised. Version/seed has 86 passes. Publish has 34 passes +and 12 intended tag failures: with only prior RC registry history, real +Changesets publishes candidates to latest instead of rc (P01). This remains +unrepaired until the full oracle campaign completes. Strict local shims provide +command metadata and retain raw requests; real npm HTTP, tarballs, installed +consumers and Actions remain separate receiving work under O13/O22. + +Canonical release replays use the shared property registry/manifest and a new +finite comparison witness; setup is not recorded as a completed comparison, +and primary/writer/cleanup failures remain distinct. The older standalone +`recordOracleReplay` comment promises never to throw, but a real RunDetails plus +a directory channel throws EISDIR (P03). Its owner is the guarded replay/ +standalone recorder checks in `packages/db/tests/oracle-replay.test.ts`; the +needed witness is an explicit writer-failure policy comparison for that older +API. The finite wrapper's guarantee does not settle that policy conflict. + +The lint observation owner is +`scripts/eslint-plugin-oracle/replay-observation-oracle.test.js`. Its independent +source-intent model and actual ESLint receiver distinguish typed completed-run +output from literal generated-run budgets. All 3,848 source programs and two +checker controls pass; 1,924 states each reach twice. With ordinary rule/harness +controls the final suite has 3,984 passes. Exported/aliased/budget uses, runtime +JSON shadows and custom serialization hooks remain rejected; type-only uses are +allowed. Broad flow and initializer mutants fail 520 and 200 comparisons. +Only the known plain-data writer and unmodified standard JSON serializer are +recognized; this is not dynamic whole-program flow analysis or a general +serializer policy. Ordinary harness controls stay outside oracle definitions. + +The [chunk 14 review record](oracle-reviews/2026-10-28-pr-2171-oracle-chunks.md#chunk-14-actual-npmpnpm-http-publication) +adds the same release judgment through actual npm 11.17.0, pnpm 11.1.0 and +Changesets 2.29.8 over a loopback HTTP registry: +`scripts/publish-http-oracle.property.test.js`. Fourteen histories cross stable, +prior-RC, never-published and already-current metadata with both RC-only tag +forms and legal failure phases. Raw attempts preserve duplicates; actual +uploaded tarballs supply manifest identity and digest. Exact local bytes are +checked before retry/settlement judgments. Eight histories and three origin +controls pass; six histories fail intended assertions. Three confirm P01's +wrong `latest` tag. Three expose P05: RC-only/no-latest metadata causes uploads +of existing RC versions on retry or an already-current release. Both publisher +defects remain unrepaired. Metadata/restoration reach14, failure/partial/retry +reach8, packed-upload10, already-current1 and completed comparison8 distinguish +earlier assertion failures from later unreached states. Eight ordinary fixture +controls and three repeated overflow probes pass after test-tool repairs. +This receiver establishes real HTTP/packed identity reception, not installed +dependency/API capability, clean workspace setup, production authentication +or Actions. O22/O25/O26 retain those boundaries. The fixture supplies local +pack-time links without an install and requires POSIX process groups. + +The merged-main [chunk 22 extension](oracle-reviews/2026-10-28-pr-2171-oracle-chunks.md#chunk-22-publication-eligibility-without-dist-tags) +preserves those 14 histories and adds 15 neighbors. Exact-version membership, +not tag presence, determines upload eligibility. It covers current candidates +with no tags or stale RC tags, the exact wrong-tag aftermath (`rc` points older, +`latest` points current), absent targets without tags, reversed heterogeneous +candidate versions, retained candidate success before a later failure, and +stable packages whose current versions lack `latest`. Stable and candidate +metadata reach are separate; a stable-phase duplicate failure receives no +candidate-path credit. All 29 histories use the same independent ledger and +raw-attempt comparison. Native results are 21 passes / 14 intended assertion +REDs including six calibrations: seven P01 tag failures and seven P05 duplicate +failures. The stable-path witness receives two actual 409 responses before any +candidate GET. No production repair or class closure is claimed. The extension +uses Vitest 4.1.12 with the existing actual npm/pnpm/Changesets tools and retains +the earlier receiver's limits. Clean setup/build evidence and unresolved release +gates are recorded [separately](oracle-reviews/2026-10-29-pr-2171-release-gates.md). + +The [chunk 23 SortedMap reach repair](oracle-reviews/2026-10-28-pr-2171-oracle-chunks.md#chunk-23-constructive-deferred-row-order-reach) +constructively reaches changed-deferred order restoration before executing +each original generated suffix from its original empty starting condition. +The current-value sorting law, independent Map/full-sort model, four examples, +both 100-run budgets and minimum five remain. Native and independent campaigns +pass 48 tests; changed-deferred comparison reach is 96 per comparator campaign. +Positive/negative premise controls reach 16/0. Exact one-history native and +in-memory stale-placement mutant replays respectively pass and fail the shared +ordering comparison. No production change or broader public receiving claim +follows; the owner's other sampling and contract limits remain. +### Solid publication recorder receiving repair + +The [chunk 25 record](oracle-reviews/2026-10-28-pr-2171-oracle-chunks.md#chunk-25-native-solid-publication-recording-and-ci-import-census) +closes the undefined-Accessor observation gap in the shared conformance owner. +The shared `conformance/publication-laws-oracle.ts` requires complete modeled +rows at the existing final cut while preserving permitted loading turns and +earlier ID membership. All 32 source-ID histories per registration and both +pooled a/b cuts run through actual affected framework drivers. Native Solid's +62 tests, whole-package typecheck and the affected React/Preact/Vue/Svelte/Angular +selections pass. Independent reader mutants fail the intended comparison; +failing-path mounts dispose exactly once. This is test-machinery coverage, +with no library change or broader scheduler, status, backend or device claim. + + +### RC generated error catalog receiving boundary (2026-10-29) + +The existing Production error messages owner now compares the actual copied +generator's stdout with its full frozen catalogs, independent of committed +generated-page freshness. Every coded class requires its heading and real HTML +ID; every coded site requires its source and target. Canonical text-message +fences cannot supply HTML IDs. Independent R3 review retains valid alternate +HTML syntax and rejects missing or fenced-only class IDs through the same check. +The unchanged tooling run has 27 passes and 12 intended assertion failures; +stdout reception reaches once and the completed catalog comparison remains zero +because the old generator emits empty stdout. Ordinary CLI and root-command +controls are separate files. The [chunk 26 record](oracle-reviews/2026-10-28-pr-2171-oracle-chunks.md#chunk-26-generated-error-catalog-observation-and-command-ownership) +retains exact source/evidence hashes and all ORC outcomes. Actual production +bundle erasure, Typedoc generation, tooling GREEN and restored-old-tooling +mutants remain required receiving checks. Generated docs remain CI-owned. diff --git a/docs/contributing/oracle-reviews/2026-09-24-query-result-settlement.md b/docs/contributing/oracle-reviews/2026-09-24-query-result-settlement.md new file mode 100644 index 0000000000..b39cd71a16 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-09-24-query-result-settlement.md @@ -0,0 +1,161 @@ +# Query Collection result-settlement oracle review + +Reviewed semantic head: `f718de65992911218e1ad16e59e6179abc551baf` + +Scope: the public result-settlement law added to +`packages/query-db-collection/tests/ownership-lifecycle.oracle.test.ts` and its +production refinement. This record does not review the separate accepted-result +and application-waiting work for issue #1828. + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Pass. `RefetchFn` promises an explicit terminal result for invalid or deferred successful Query results. The Collection deferral contract and existing load-subset waiting behavior authorize waiting for an authoritative replacement application. A replacement retired by a newer write cannot fulfill its caller. The oracle names #1828 accepted-result generations and diff-free signals as limits. | +| ORC-002 | Pass. The model uses only `applicable`, `invalid-shape`, deferral, replacement retirement, and operation retirement. It does not import or reproduce production shape checks, Query state, fetch counters, settlement maps, observer scheduling, or sync transactions. | +| ORC-003 | Pass. The oracle opening states the contract and limits; `advanceResultSettlementModel` is the model; `ResultSettlementModelAction` is the action grammar; the tests drive `collection.utils.refetch()`; `expectPublicRefetchObservation` checks public settlement and rows. | +| ORC-004 | Not applicable. The law uses a bounded deterministic grammar and makes no generated-history coverage claim. Valid empty, invalid, eager recovery, deferred replacement, authority retirement, overlapping barriers, and cleanup retirement are pinned cells. | +| ORC-005 | Pass. The driver executes the public refetch and `clearError()` boundaries across eager and shared on-demand Query ownership. It observes pending versus fulfilled or rejected settlement and public Collection rows at the terminal checkpoint. | +| ORC-006 | Pass. The checker calibration supplies silent-fulfillment observations for invalid, deferred, rebound, and authority-retired results. At `f718de65^`, the on-demand authority replay fulfilled while the model remained pending, and valid eager recovery rejected with the prior `InvalidQueryResultError`; both pass at the reviewed head. | +| ORC-007 | Not applicable. No important generated property was added or changed. | +| ORC-008 | Pass. `waiting-for-result` and `waiting-for-refresh` remain distinct because replacement actions are legal only after deferral. `refresh-retired` leaves the operation waiting because the retired result and the public call have different lifetimes. Terminal state remains distinct because any later action would create a second terminal outcome and is rejected. | +| ORC-009 | Pass. The oracle declares that model `applicable` combines adapter validation and materializability, and that `refresh-retired` abstracts loss of result authority without modeling fetch counters. Shared promise, settlement, application, and retirement terms follow the project glossary. | +| ORC-010 | Pass. There is no shrinking or normalized capture. Invalid-shape diagnostics remain observable, and cleanup turns a pending deferred refetch into an explicit `CancelledError` instead of erasing or hanging its settlement. | +| ORC-011 | Not applicable. No reviewer named a semantic fault that the abstract terminal-outcome model and production could plausibly share; the public promise observation directly distinguishes the reported faults. | +| ORC-012 | Pass. This versioned record contains the outcomes for ORC-001 through ORC-011 and identifies the exact reviewed semantic head. | + +## Same-result reentry follow-up + +- Starting head: `755a1d39c49826c6d935259687023915388c7f12` +- Reviewed semantic head: `74e554937a439e89dcadef8ad4a0f61fef0635d4` +- Primary owner: `packages/query-db-collection/tests/ownership-lifecycle.oracle.test.ts` + +This follow-up covers one deterministic history. A Collection change callback +drops the last subscriber and immediately adds another. Query DB then applies +the same `QueryObserverResult` again while the outer synchronous application is +still publishing. The nested application has a controlled pending applied +receipt. The public refetch must remain pending until that current application +settles. + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Pass. The documented public refetch contract requires fulfillment to wait for every accepted Collection application when no mutation blocks publication. The result-settlement owner already names that law and its mutation-phase limit. This fixed history does not claim coverage for a different Query hash, mutation-phase fetch settlement, or arbitrary observer scheduling. | +| ORC-002 | Pass. The existing `RefetchCallSettlementModel` independently says that one fulfilled Query fetch with one pending accepted application leaves the public refetch pending. It imports no controller, result-settlement map, observer, or commit implementation. | +| ORC-003 | Pass. The owner opening now includes same-result subscriber reentry in its bounded action grammar. The existing call-settlement model supplies the expected result. The driver uses a real QueryClient and public refetch. The refinement check compares public promise settlement before and after the controlled applied receipt. | +| ORC-004 | Not applicable. This is one deterministic fixed regression. It makes no generated-history coverage claim. | +| ORC-005 | Pass. The production driver proves the Query function ran twice, the subscriber callback reentered, and exactly one nested commit reached the held applied receipt. It observes that the public refetch is pending at that checkpoint, then fulfilled with the updated row after release. | +| ORC-006 | Pass. At the starting head, the focused regression failed with public outcome `resolved` while the nested applied receipt remained held. A temporary identity trace recorded one result object under outer controller 4 and reentrant controller 5: controller 5 first recorded the pending settlement, then controller 4 overwrote that same result with a resolved promise while controller 5 remained current. The permanent controller-identity fence makes the same probe pass. | +| ORC-007 | Not applicable. No important generated property or campaign changed. | +| ORC-008 | Not applicable. The fixed history adds no state to the existing call-settlement reference model. | +| ORC-009 | Pass. `same-result` means the exact current Query observer result object. `reentrant application` is the current Collection application started during outer publication. The controlled promise is its applied receipt, not Query fetch settlement or Collection readiness. | +| ORC-010 | Pass. The fixed history performs no shrinking or normalized capture. Cleanup releases the controlled receipt, unsubscribes the final listener, cleans the Collection, and clears the QueryClient without replacing an assertion failure. | +| ORC-011 | Not applicable. The independent public promise observation directly distinguishes the reported settlement overwrite; no plausible shared semantic fault requires another formulation. | +| ORC-012 | Pass. This versioned follow-up records ORC-001 through ORC-011 against the exact semantic head above. | + +Verification at the semantic head: the focused probe passed 1/1; the complete +ownership owner passed 140/140; the load-subset owner and Query Collection +runtime passed 219/219. Query Collection source type-check, build, lint, format, +and diff checks passed. Lint retained the package's existing warnings and +reported no errors. + +## Same-result grammar closeout + +- Production semantic head: `74e554937a439e89dcadef8ad4a0f61fef0635d4` +- Oracle/test head: `fb6d61421480980ef38037d13ef579e74a3de491` +- Runtime: Node `24.19.0`, pnpm `11.1.0`, Vitest `3.2.4` + +No production behavior changed in this closeout. The loss audit showed that the +single fulfilled receipt proved waiting but did not preserve the whole public +law: receipt rejection, exact error identity, and publication cardinality were +missing. + +### Model + +The preserved properties are: + +| ID | Preserved property | Authority | +| --- | --- | --- | +| SR-P1 | A fulfilled Query fetch does not settle its public refetch while any accepted application for that call remains pending. | Public refetch/application-barrier contract | +| SR-P2 | Same-result subscriber reentry transfers settlement authority to the current nested application; the outer application cannot overwrite it. | Reviewed semantic behavior | +| SR-P3 | A fulfilled nested applied receipt fulfills the refetch; a rejected receipt rejects it with the exact application error. | Public promise settlement contract | +| SR-P4 | Reapplying the exact current result during publication does not publish the row change twice. | Collection publication contract | +| SR-P5 | The single public change is exactly `Initial → Updated`; subscriber churn is a trigger, not another data change. | Public change-event contract | +| SR-P6 | The row remains `Updated` after either receipt outcome; receipt rejection reports application failure and does not roll back the already published Query value. | Established application/publication boundary | +| SR-P7 | Different-result identity, mutation-phase settlement, and arbitrary observer schedules remain outside this fixed history. | Existing owner limits | + +The minimal grammar is one same-result reentry history with a two-cell receipt +outcome: `fulfilled | rejected`. Both cells share the same fetch, outer +publication, subscriber drop/add reentry, one nested commit, and pending +checkpoint. Only the nested receipt outcome varies. The existing +`RefetchCallSettlementModel` supplies the expected pending and terminal states; +the production driver supplies the real QueryClient, Collection, subscription, +and public refetch. + +### Evidence + +Both cells reconstruct the intended law. Before receipt settlement, each proves +two Query executions, one reentrant commit, a pending public refetch, and exactly +one `Initial → Updated` publication. The fulfilled cell then resolves. The +rejected cell rejects with the identical `Error` object. Both retain the updated +row and the one-publication trace. + +One-at-a-time ablation keeps only consequential structure: + +- Without the pending receipt checkpoint, the original premature-resolution + bug becomes invisible. +- Without the outcome coordinate, the grammar cannot distinguish fulfillment + from application rejection. +- Without exact error identity, swallowing or replacing the application failure + can pass. +- Without exact publication cardinality and values, duplicate publication or a + wrong previous value can pass. +- Without the same-result reentry trigger, the history no longer reaches the + controller-identity collision fixed at the semantic head. + +The nearby invalid forms are a refetch that resolves while the nested receipt is +pending, a rejected receipt that fulfills or throws a replacement error, and a +second publication of the same update. A different Query result or hash and +mutation-phase publication are legal neighboring domains, but they require +their existing grammars rather than an extra coordinate here. + +The principal decomposition loss is scheduler breadth. This fixed history +proves the concrete synchronous drop/add reentry and its two receipt outcomes; +it does not claim every observer interleaving. Keeping that limit explicit is +more accurate than multiplying a guessed scheduling axis into the model. + +### Process + +The source was frozen at the production semantic head. The existing public +contract, result-settlement model, concrete reentry driver, and starting-head +failure were the extraction evidence. The fulfilled/rejected outcome survived +ablation because it changes public settlement; publication count and exact +error identity remained observations rather than new model state. No guessed +scheduler coordinate was admitted. + +Calibration remains anchored by the starting semantic head: the focused +history observed the public refetch as resolved while the nested receipt was +still pending. At the semantic head, the controller-identity fence keeps it +pending. The new rejection and publication assertions would also reject a +swallowed/remapped application error or duplicate update at their exact public +checkpoints. + +### ORC disposition at the oracle/test head + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Pass. SR-P1 through SR-P7 identify the public law, authority, and explicit scheduling and mutation limits. | +| ORC-002 | Pass. `RefetchCallSettlementModel` reasons over accepted applications and outcomes; it imports no production controller, observer result, settlement map, or commit implementation. | +| ORC-003 | Pass. The file opening states the contract and grammar, the existing call-settlement model supplies expected state, the test drives real production, and public promise/row/publication assertions form the refinement check. | +| ORC-004 | Not applicable. This is a two-cell bounded enumeration, not an important generated-history property. Reconstruction, ablation, range, and invalid neighbors are nevertheless recorded above. | +| ORC-005 | Pass. The real QueryClient and public `collection.utils.refetch()` path reach two fetches, subscriber reentry, one nested commit, promise settlement, rows, and exact change publication. | +| ORC-006 | Pass. The starting head is a reached-path assertion failure with premature public resolution. Exact rejection identity and publication cardinality calibrate the two recovered observations. | +| ORC-007 | Not applicable. The fixed two-cell history makes no random campaign or shrinking claim. | +| ORC-008 | Not applicable. The closeout adds no model state; fulfilled and rejected are terminal outcomes already distinguished by the model. | +| ORC-009 | Pass. Same-result means the exact current Query observer result; applied receipt, public refetch, publication, and reentrant application retain their glossary and owner meanings. | +| ORC-010 | Pass. There is no shrinking or capture. Rejection preserves exact error identity; the owner attempts all registered cleanup callbacks with `Promise.allSettled` and reports cleanup failures separately from the test assertion. | +| ORC-011 | Not applicable. No remaining plausible shared semantic fault was named that a second formulation could distinguish; the public promise plus exact publication observation directly checks this fixed history. | +| ORC-012 | Pass. This section records every ORC-001 through ORC-011 outcome against exact semantic and oracle/test heads. | + +Verification at the oracle/test head: the complete ownership owner passed +227/227 across runtime and source type-check projects. The ordered owners passed +298/298, and both DB and Query Collection builds passed. Changed-file lint +reported no errors (two existing `require-await` warnings remain elsewhere in +this owner), and format plus diff checks passed. diff --git a/docs/contributing/oracle-reviews/2026-09-25-query-readiness.md b/docs/contributing/oracle-reviews/2026-09-25-query-readiness.md new file mode 100644 index 0000000000..9df900b300 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-09-25-query-readiness.md @@ -0,0 +1,305 @@ +# Query readiness oracle review + +- Reviewed semantic head: `44d1d4507455064df2a51235ca17212706987e90` +- Base: `34c78be670b215a994394a73bdfb800a48c91ddd` +- Runtime: Node `24.19.0`, Vitest `3.2.4` +- Primary owner: `packages/db/tests/query/ordered-lifecycle-oracle.property.test.ts` +- Reported behavior: TanStack DB #1894 + +The repair adds a bounded initial-settlement refinement to the existing ordered +lifecycle owner. It does not change the owner's generated 192-cell lifecycle +product or nullable multi-term product. It adds the missing fixed-seed campaign +for the existing nullable property without changing that property's domain. + +| Requirement | Outcome | +| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| ORC-001 | Pass. `ARCHITECTURE.md` now names the synchronous initial-query observation cut: an ordinary initial ordered chain whose acquisitions return literal `true` after their establishing applied receipts are visible drains its synchronous continuations and graph work before the initiating call stack returns. It excludes source exhaustion, broader coverage, explicit windows, repair, replay, and framework render-time behavior. | +| ORC-002 | Pass. `expectedInitialSettlementObservation` derives the expected rows and initial-query status from a two-row finite source and the public literal-`true` versus Promise contract. It imports no ordered-loader classifier, continuation state, generation, or production helper. | +| ORC-003 | Pass. The opening states the contract and limits; `InitialSettlementShape` plus the four indexed/prefix cells are the bounded grammar; `expectedInitialSettlementObservation` is the model; `observeInitialSettlement` is the production driver; and the observation, acquisition-path, boundary-continuation, and release checks form the refinement check. | +| ORC-004 | Not applicable to the new refinement. It is a fully enumerated deterministic 2×2 grammar and makes no generated-history coverage claim. The four cells reconstruct both settlement shapes across an observed indexed page and unindexed prefix request; both require the predicate-only boundary continuation. The Promise cell is the adjacent non-synchronous state. | +| ORC-005 | Pass. The driver uses a real on-demand source, `createLiveQueryCollection`, a live subscription, and public `preload()`. It records the same-call-stack and settled rows/status, exact first-request shape, predicate-only boundary continuation, and request/release identity. `sync.commit() === true` is the positive witness that every establishing write and event was applied before the adapter returned literal `true`. | +| ORC-006 | Pass. `rejects the old Promise-wrapped observation at the synchronous checkpoint` supplies the prior design's exact wrong answer. The refinement check rejects its empty/loading observation as an assertion failure. A temporary adversarial mutation that routed indexed acquisition through the prefix path initially passed all five focused tests; after the request classifiers were added, both eager-index cells failed with observed `prefix` versus expected `page`. Existing direct-loader controls separately reject synchronous write-then-throw, cleanup reentry, and unsafe continuation designs. | +| ORC-007 | Pass. The owner has two important generated properties. Ordered lifecycle and nullable multi-term lifecycle now each run identical fixed-seed and seedless-random lanes with the same arbitrary, production observer, refinement check, run count, and timeout. The random nullable lane retains direct seed-and-path replay through `ordered-work.nullable-lifecycle`. The deterministic initial-settlement refinement remains outside those generated campaigns. | +| ORC-008 | Not applicable. The new finite model is stateless recomputation; it does not introduce, combine, split, or remove state in a stateful reference model. | +| ORC-009 | Pass. Model `synchronous` means the adapter returns literal `true`; `promise` means it returns `Promise`. Existing `AcquisitionPath` prose maps indexed ordered work to `page` and unindexed ordered work to `prefix`. The observed checkpoint is named initial query readiness, distinct from Collection and subscription readiness. | +| ORC-010 | Pass. The refinement performs no shrinking or capture. Its driver releases the live subscription and cleans both live and source Collections in `finally`; it then checks exact request/release identity. The existing lifecycle owner retains failure diagnostics, abort observations, terminal cleanup, and one-release checks for failing histories. | +| ORC-011 | Pass. The named shared-fault risk is collapsing an adapter's return with public visibility before applied receipts or continuation complete. The source driver requires `commit() === true`; warm Query-cache integration tests exercise a different adapter path with zero fetches; and React receiving-driver tests independently observe first non-idle layout-commit rows/status while preserving render-time inactivity. | +| ORC-012 | Pass. This versioned record reports ORC-001 through ORC-011 and is tied to the exact reviewed semantic head above. | + +## Verification receipts + +- Unfiltered ordered lifecycle owner: 228 tests passed, including fixed and + random lanes for both generated properties and the four new + initial-settlement cells. +- Guarded nullable-lifecycle replay reached seed `93472`, path `0`, executed 20 + runs, and reported the requested property witness. +- Full DB oracle campaign: 40 files and 2,135 tests passed. +- Ordered loader, loader-state, lifecycle, work, pagination, default-work, and + demand-retirement suites: 662 tests passed. +- Query Collection runtime: 180 tests passed; 172 source type checks in the + same owner plus package `tsc` passed. +- React regular and infinite conformance: 63 tests passed. +- DB IVM, DB, Query Collection, and React DB builds passed. + +The source-isolated loss audit is preserved outside the repository at +`/private/tmp/pr2-query-readiness-loss-audit.md`; verdict-critical evidence is +repeated here so this record does not depend on that temporary file. + +## Cross-source synchronous work follow-up + +- Base: `0eb5b85ddf127e7c2fe5f23dbed2c3f9056c0ea7`. +- Reviewed semantic head: `c4689b746b2e3c402909ee7f05fe38a85a3005c0`. +- Added history: an indexed ordered `FROM` source reaches its predicate-only + boundary continuation while a sibling `LEFT JOIN` source synchronously adds + enough rows to fill the initial limit. + +| Requirement | Outcome | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| ORC-001 | Pass. The architecture now requires graph work before another ordered acquisition when any sibling subscriber added synchronous input. The evidence is limited to the initial synchronous cut and does not claim behavior for asynchronous settlement, deoptimized joins, repair, replay, or explicit window moves. | +| ORC-002 | Pass. The expected two-row join and two-request trace come from a finite one-left/two-right relation and the graph-wide input law. The check imports no loader revision, continuation, or top-K helper. | +| ORC-003 | Pass. The existing owner retains its contract, finite reference, bounded grammar, real live-query driver, and public refinement checks. The fixed cross-source history adds request-work and ready/immediate row observations without changing the generated models. | +| ORC-004 | Not applicable. The follow-up is one deterministic regression and makes no generated-history coverage claim. | +| ORC-005 | Pass. The driver uses two real source subscriptions and an indexed ordered live query. It proves the page and boundary paths ran, records the exact adapter request trace, and checks rows at the ready callback, immediate preload return, and settled checkpoint. | +| ORC-006 | Pass. At the base, the exact test failed because production issued a third cursor acquisition after the sibling source had already committed enough joined rows. The graph-wide revision removes that request while preserving both rows at every readiness checkpoint. | +| ORC-007 | Not applicable. No important generated property or campaign changed. | +| ORC-008 | Not applicable. The follow-up adds no reference-model state. | +| ORC-009 | Pass. `Sibling source input` means changes sent by another `CollectionSubscriber` to the same D2 graph. The production `graphInputRevision` records that graph-wide progress; it is not Collection readiness or source coverage. | +| ORC-010 | Pass. The regression has no shrinking or normalized capture and cleans the live query plus both source Collections in `finally`. | +| ORC-011 | Not applicable. The exact physical request trace directly distinguishes the reported stale-work fault; no plausible shared semantic fault needs a second formulation. | +| ORC-012 | Pass. This versioned, base-identified follow-up records every ORC-001 through ORC-011 outcome and names the exact reviewed semantic head. | + +The focused RED run observed page, boundary, then stale cursor acquisition. The +GREEN run observes only page and boundary, with both joined rows visible at all +three readiness checkpoints. Inner and right joins, ordering by a non-`FROM` +alias, and other unsafe plans deoptimize to full-source acquisition and remain +outside this fixed regression. + +Verification after the semantic commit: the focused regression passed 1/1; the +complete ordered-lifecycle owner passed 229/229; six surrounding ordered-loader, +ordered-work, and live-query suites passed 311/311; DB build, changed-file lint, +format, and diff checks passed. + +## Loss-audit and design-grammar closeout + +- Production semantic head: `74e554937a439e89dcadef8ad4a0f61fef0635d4` +- Oracle/test head: `fb6d61421480980ef38037d13ef579e74a3de491` +- Runtime: Node `24.19.0`, pnpm `11.1.0`, Vitest `3.2.4` +- Extraction target: the smallest oracle grammar that preserves ordered initial + readiness, graph progress, repair precedence, and failure evidence without + multiplying unrelated lifecycle dimensions. + +The closeout changed tests and oracle evidence only. The intended production +behavior remains the behavior at the semantic head. The loss audit recovered +independent ordering directions, continuation depth, Promise-parent ownership, +captured-failure replay, and primary-versus-cleanup diagnostics as material +properties that the earlier review did not fully preserve. + +### Model + +The preservation contract was frozen before the final grammar changes: + +| ID | Preserved property | Authority | +| --- | --- | --- | +| DG-P1 | A literal-`true` initial ordered acquisition publishes its complete initial window before the initiating stack returns. | Architecture and existing public readiness contract | +| DG-P2 | A Promise-returning acquisition promises the settled checkpoint, not the synchronous checkpoint. | Adapter settlement contract | +| DG-P3 | A synchronous ordered continuation drains until it reaches real pending work, exhaustion, failure, or graph input that requires a graph turn. | Architecture and reviewed semantic behavior | +| DG-P4 | Sibling-source input reaches the shared graph before the loader decides that another ordered acquisition is necessary. | Architecture and cross-source regression | +| DG-P5 | Ordering repair takes precedence over a staged ordinary continuation and an explicit window cannot consume stale work. | Reviewed repair semantic head | +| DG-P6 | A fulfilled Promise parent remains owned when its continuation throws; repair debt is recorded without retiring the successful acquisition. | Acquisition ownership and repair contract | +| DG-P7 | Nullable multi-term ordering varies each term's direction and null placement independently. | Public order-by semantics | +| DG-P8 | Readiness evidence includes rows and status; work evidence includes the exact acquisition and release trace. | Oracle observation contract | +| DG-P9 | A primary oracle mismatch remains primary while every cleanup failure remains distinguishable and every cleanup is attempted. | `oracle-rubric.md` ORC-010 | +| DG-P10 | Explicit-window, replay, repair, and Effect scheduling laws keep their existing owners; this grammar does not silently absorb them. | Coverage map and user-confirmed scope | + +The surviving grammar has four separate subgrammars rather than one artificial +Cartesian product: + +1. Initial readiness is `acquisition path × settlement` (`page` or `prefix` by + literal `true` or Promise), exactly four cells. +2. The existing ordered lifecycle product remains 192 cells: + acquisition path × delivery phase × window action × outcome × sync-run + generation × initial/replay barrier. +3. Post-success failure remains its bounded settlement × failure-phase grammar; + it is not multiplied into initial readiness. +4. Query same-result reentry remains with the Query Collection result-settlement + owner and is reviewed in the companion result-settlement record. + +Graph progress is expressed as three overlays: a quiet multi-step continuation +drain, sibling-source graph progress, and ordering-repair precedence. The +nullable lifecycle grammar retains its 22 declared lifecycle cells and adds a +four-cell primary/secondary direction overlay. Random generation varies both +directions independently. This preserves the missing relation without turning +every lifecycle cell into another four-way product. + +### Evidence + +Reconstruction reaches every declared cell: four initial-settlement cells, all +192 original lifecycle histories, all 22 nullable lifecycle cells, and all four +direction pairs. Exact uniqueness checks prevent a duplicated declaration from +masquerading as coverage. A four-step synchronous trace +`page → boundary → page → boundary` proves continuation depth rather than only +one-step reentry. + +One-at-a-time ablation gave each retained coordinate a distinct job: + +- Removing settlement shape erases the synchronous-versus-Promise readiness + boundary; removing acquisition path erases indexed page versus unindexed + prefix behavior. +- Removing any original lifecycle axis erases a distinct public checkpoint, + request shape, terminal outcome, sync generation, window transition, or + replay barrier. +- Coupling the two sort directions removes `asc/asc` and `desc/desc`; the + four-cell overlay prevents that false grammar. +- Removing quiet drain, sibling progress, or repair precedence respectively + permits shallow return, stale extra acquisition, or stale-continuation work. +- Removing Promise-parent retention permits a continuation failure to discard a + successful acquisition that repair still needs. +- Removing failure-safe cleanup lets a teardown error hide the oracle mismatch. + +The nearby invalid designs are therefore concrete: readiness before applied +receipts and continuations, a third stale acquisition after sibling progress, +ordinary continuation before repair, early parent release after a continuation +failure, coupled sort directions, and cleanup replacement of the primary +failure. Explicit-window histories, replay histories, repair histories, and +Effect scheduling remain valid neighboring domains, but their laws stay with +their existing owners. Framework render-time scheduling is outside this +grammar. + +The independent formulations have deliberately narrower equivalence claims: + +- Direct source and warm Query cache must agree on final rows and readiness, + while a warm cache has no source acquisition chain to compare. +- Direct source and React must agree on rows and status at React's receiving + checkpoint; this says nothing about render-time scheduling. +- The sibling-source wait-for-graph history adds an exact physical request + trace, so it constrains work as well as final rows. + +### Process + +The extraction source was frozen at the production semantic head, with the +architecture, oracle guide, coverage map, ordered lifecycle owner, loader +regressions, Query Collection owner, and existing framework/cache formulations +as named evidence. Production semantics were not changed to satisfy the +grammar. Candidate coordinates survived only when reconstruction or +one-at-a-time ablation showed a distinct law, checkpoint, exclusion, or active +overlap; otherwise they remained separate overlays or with their existing +owner. + +Two hostile controls demonstrate detection and replay: + +- A temporary early-release mutant made the Promise-parent continuation test + fail at the exact lease assertion. Restoring production made the focused test + pass. +- A temporary `secondary-order` fault failed the random nullable property at + seed `-1998078481`, shrink path `0:0:0:0:0`, after four shrinks. Direct replay + with `TANSTACK_DB_ORACLE_PROPERTY=ordered-work.nullable-lifecycle` reproduced + the same `success-rows` mismatch with zero additional shrinks. Restoring the + oracle made that exact replay pass. + +The cleanup control injects one primary mismatch and two teardown failures. It +proves both teardown callbacks run, the `AggregateError.cause` is the primary +mismatch, and `errors` retains the primary plus both cleanup diagnostics in +order. + +The decomposition loss is explicit: separate subgrammars do not prove every +cross-product interaction. The overlays preserve the intersections implicated +by the issue and loss audit; unrelated combinations remain with their existing +owners. This avoids combinatorial confidence without hiding the boundary. + +### ORC disposition at the oracle/test head + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Pass. DG-P1 through DG-P10 name the intended laws, authorities, and exclusions. | +| ORC-002 | Pass. Expected rows come from finite-source sorting and slicing; settlement and ownership expectations come from public contracts, not loader continuation or repair machinery. | +| ORC-003 | Pass. The opening contract, scenario grammar, finite reference, real live-query driver, and mismatch/refinement checks remain visible and distinguishable. | +| ORC-004 | Pass. Reconstruction, one-at-a-time ablation, bounded range, overlays, and nearby invalid designs are recorded above; executable uniqueness checks guard the enumerated cells. | +| ORC-005 | Pass. Real source Collections and live queries expose rows, status, requests, releases, repair, and readiness at named checkpoints. | +| ORC-006 | Pass. The early-release mutant and secondary-order fault both reached their intended checkpoints and failed by assertion; neither was a setup failure or timeout. | +| ORC-007 | Pass. Both important generated properties retain equal fixed and random campaigns, and the captured nullable failure was replayed directly by seed, shrink path, and property name. | +| ORC-008 | Not applicable. The closeout introduces no reference-model state and neither combines nor splits existing model states. | +| ORC-009 | Pass. Settlement, acquisition, graph input, repair, readiness, replay, and applied receipt retain their architecture/glossary meanings; overlay-only terms are identified above. | +| ORC-010 | Pass. The harness preserves the primary failure as cause, retains every cleanup diagnostic, attempts all cleanup callbacks, and has an executable hostile control. | +| ORC-011 | Pass. Warm-cache, React receiving-driver, and sibling wait-for-graph formulations state exactly which observations are equivalent and which work or scheduling facts differ. | +| ORC-012 | Pass. This section records every ORC-001 through ORC-011 outcome against exact semantic and oracle/test heads. | + +Verification at the oracle/test head: the ordered loader and lifecycle owners +passed 298/298 with test type checking; the Query Collection ownership owner +passed 227/227 across runtime and source type-check projects. DB and Query +Collection builds passed. Changed-file lint reported no errors (two existing +`require-await` warnings remain elsewhere in the Query Collection owner), and +format plus diff checks passed. + +## Empty-boundary explicit-window follow-up + +- Base: `93169bd295a628d157e26858eed6a489a75fb44f` +- Reviewed semantic head: `9cf8ebec9d6ce7d5610cc698bcd571d3bea17734` +- Reviewed oracle/test head: `94cdc79df0876d941c08c2452d709027b466b292` +- Runtime: Node `24.19.0`, pnpm `11.1.0`, Vitest `3.2.4` +- Finding: CodeRabbit review `5325930839`, inline comment `4111391073` + +The architecture already required an explicit underfilled window to acquire its +missing prefix. It now states the adjacent continuation rule directly: a +staged boundary with no boundary row starts no acquisition, so consuming it +cannot settle the enlarged window operation. + +The bounded design grammar crosses two independent axes: + +- settled source boundary: absent or present; +- remaining explicit-window demand: satisfied or widened. + +`expectedStagedWindowTrace` is a stateless immediate-work model. Every history +starts with the finite page that creates the staged continuation. A present +boundary then requires its tie acquisition. A widened window then requires a +page acquisition; with no boundary, that page starts at the source prefix. The +four-cell calibration proves that every declared combination appears once. + +The production driver reaches the loader path without timing: + +1. An indexed finite acquisition returns literal `true` with no rows while the + graph-input revision changes. +2. The loader retains an ordinary staged boundary continuation but no settled + source boundary. +3. The driver varies boundary presence, either satisfies or enlarges the + window, and calls `loadMore(1)`. +4. The refinement check compares the complete page/boundary/page request trace, + synchronous versus pending settlement, and exact widened request shape. + +At the base, the same-path RED failed because `loadMore(1)` returned `undefined` +after consuming the empty continuation and started no second acquisition. The +fix returns only when continuation consumption starts pending work. Otherwise, +normal demand selection handles the enlarged window. The active-state check +also preserves synchronous disposal reentry. + +The earlier grammar was incomplete. It covered synchronous +multi-continuation draining, staged-continuation repair precedence, and public +window settlement separately. They did not cross graph-input change, an empty +finite result, the resulting staged boundary, and a wider explicit window in +one history. The new overlay owns that intersection without multiplying the +192-cell public lifecycle product. Removing the boundary axis loses the tie +versus prefix distinction. Removing the demand axis loses the difference +between an absent boundary that is finished and one that remains underfilled. +The nearby invalid histories are a phantom request for the satisfied absent +cell and a widened prefix that skips the required present-boundary tie. The +public ordered lifecycle owner remains the broader window-settlement authority. + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Pass. The architecture owns the empty-boundary continuation law and limits it to an explicit window whose consumed continuation starts no acquisition. It does not claim source exhaustion or revise replay, repair, or framework scheduling. | +| ORC-002 | Pass. The expected request follows from the independently stated window and demand law: an empty boundary proves no prefix, and an enlarged underfilled window requires acquisition from offset zero. The test imports no production continuation classifier. | +| ORC-003 | Pass. The architecture states the law; `expectedStagedWindowTrace` is the stateless model; the two-axis product is the history grammar; the fixture drives the real loader; and exact work plus settlement form the refinement check. | +| ORC-004 | Pass. The four-cell product reconstructs present and absent boundary histories with satisfied and widened demand. Removing either axis loses a distinct legal result. The range is the minimal Boolean product; phantom satisfied work and skipped tie work are the nearby invalid states. | +| ORC-005 | Pass. The driver invokes the real `OrderedSourceLoader.start()` and `loadMore(1)` entry points, proves the staged continuation exists, and observes the complete acquisition trace, exact widened request, and settlement at the post-`loadMore` and completed-chain checkpoints. | +| ORC-006 | Pass. Unchanged production at the base reached every absent/widened precondition and failed by assertion: the pending result was `undefined`, and the second acquisition was absent. The semantic and oracle/test heads pass the strengthened refinement. | +| ORC-007 | Not applicable. No important generated property or campaign changed. | +| ORC-008 | Not applicable. The trace model is stateless. | +| ORC-009 | Pass. Window, boundary, graph-input revision, acquisition, and settlement retain their project glossary and architecture meanings. `expectedStagedWindowTrace` is explicitly an immediate-work projection, not a production lifecycle state. | +| ORC-010 | Pass. Every deferred acquisition is resolved in `finally`, loader disposal still runs, and cleanup cannot replace the recorded refinement mismatch. | +| ORC-011 | Not applicable. The exact pending result and acquisition trace directly distinguish the one disputed control-flow fault; no plausible shared semantic classifier requires a second formulation. | +| ORC-012 | Pass. This versioned follow-up records every ORC-001 through ORC-011 outcome against the exact base, semantic head, and oracle/test head. | + +Verification at the oracle/test head: the complete four-cell grammar and its +calibration passed 5/5; the complete ordered-loader suite passed 67/67; the six ordered loader, +state, lifecycle, work, default-work, and demand-retirement owners passed +437/437. All runs reported no type errors. Package TypeScript, DB build and +declaration generation, changed-file lint, Prettier, and diff checks passed. diff --git a/docs/contributing/oracle-reviews/2026-09-27-remote-subset-two-tab.md b/docs/contributing/oracle-reviews/2026-09-27-remote-subset-two-tab.md new file mode 100644 index 0000000000..a0bd2e17e3 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-09-27-remote-subset-two-tab.md @@ -0,0 +1,23 @@ +# Two-tab remote-subset browser oracle + +- Reviewed oracle commit: `15301fd409fdebdc84eebbbf0ae9e1f095b3b26c` +- Comparison main: `d3c38c8f4e83cef4545e8011dc3419be1fcf527a` +- Owner: `packages/browser-db-sqlite-persistence/e2e/remote-subset-two-tab.opfs.spec.ts` +- Runtime: real Chromium, two same-origin pages, one OPFS database, QueryClient-backed on-demand source, filtered live-query Collection + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Pass within the stated scope. The browser coordinator's documented remote-subset contract keeps live `signal` and `subscription` references local while it transports clone-safe request data. Issue #1498 supplies the two-tab failure history. The test does not run React, Firefox/Zen, or a live backend. | +| ORC-002 | Pass. The fixed query input independently specifies the one expected public row and label. Neither the coordinator's transport projection nor its response computes that expectation. | +| ORC-003 | Pass for a fixed history. The test header states the law, model, history, driver, checkpoint, and limits. The adjacent fixture drives the real QueryClient, Collection, coordinator, BroadcastChannel, and OPFS path. | +| ORC-004 | Not applicable. This test executes one deterministic two-tab history. It makes no generated-history grammar claim. | +| ORC-005 | Pass. The follower's load carries `where`, `signal`, and a callback-bearing subscription. A remote-subset RPC post occurs, the follower makes no upstream Query call, and its public live-query row matches the fixed input after preload. An invalid nested function fails with its exact wire path before another post. | +| ORC-006 | Pass. A temporary mutant stored raw `options` instead of `transportedOptions` in the outbound acquisition. The focused test failed at follower readiness with four `DataCloneError` posts for the `AbortSignal`. This reached the intended browser path; it was not a setup failure or timeout. The mutant was removed, and the focused test passed again at the reviewed commit. | +| ORC-007 | Not applicable. This is not an important generated property. | +| ORC-008 | Not applicable. The fixed expected row adds no stateful reference model. | +| ORC-009 | Pass. The source and live-query Collection names follow the glossary. The follower post is a transport observation, not evidence that every provider request or browser host works. | +| ORC-010 | Pass. The test closes both pages and calls fixture cleanup. It keeps a primary mismatch separate from each cleanup error through an `AggregateError` cause and errors. It does not shrink or normalize a history. | +| ORC-011 | Not triggered. No plausible semantic fault shared by the fixed input model and transport implementation requires a second formulation. React rendering, Firefox/Zen, and a live backend remain unverified host boundaries. | +| ORC-012 | Pass. This versioned record accounts for ORC-001 through ORC-011 against the exact oracle commit above. | + +At the reviewed commit, the complete Chromium OPFS suite passed 3/3 tests. The package TypeScript check, ESLint, Prettier, and `git diff --check` passed. The local pre-commit hook could not start `pnpm lint-staged` because pnpm tried to replace the worktree's linked dependency directory. Equivalent ESLint and formatting checks ran directly before the normal commit. diff --git a/docs/contributing/oracle-reviews/2026-09-27-retained-demand-terminal.md b/docs/contributing/oracle-reviews/2026-09-27-retained-demand-terminal.md new file mode 100644 index 0000000000..1d91d0afdd --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-09-27-retained-demand-terminal.md @@ -0,0 +1,23 @@ +# Retained-demand terminal failure oracle + +- Reviewed semantic commit: `3c6e9df082f6a5db0c351a6f58631d9d72104d80` +- Comparison main: `f473a36201abe9af43d36a83492a2649668756d8` +- Owner: `packages/db-sqlite-persistence-core/tests/persisted.test.ts`, property `persistence.retained-demand` +- Runtime: local Node/Vitest 3.2.4; recording persistence adapter, not a browser or OPFS host + +| Requirement | Result | +| --- | --- | +| ORC-001 | Pass. RFC #1659 invariant 7 and the existing terminal-failure load contract require a later exact retained demand to reject with the same terminal error. A cached row is not evidence that durability recovered. The oracle does not claim real-host behavior. | +| ORC-002 | Pass. The expected readiness comes from an independent set of retained demand names. After a failed source receipt, the expected result is its exact terminal error with no new upstream call; the model does not read production's hydrated-demand cache or terminal classifier. | +| ORC-003 | Pass. The file header states the contract and limit; the demand operation arbitrary and fixed terminal suffix form the grammar; the set plus absorbing failure rule form the model; the real persisted wrapper is the driver; result identity and upstream-call count are checked after the failure. | +| ORC-004 | Pass within this bounded grammar. The RED shrink reconstructed `[acquire one]`; acquire/release and inserted truncate positions retain the prior ownership/reset histories. Removing the terminal suffix lets the old fast-path defect survive. The range is 1–20 demand actions with at most four truncate insertions, two exact demand names, and one terminal source write. Unknown demand names are excluded; release without an active owner is a legal no-op. This does not generate concurrent host schedules. | +| ORC-005 | Pass. The driver calls the wrapper's `loadSubset`, retains a hydrated demand, fails a source commit through `applyCommittedTx`, then calls `loadSubset` again. It observes the applied-receipt error, the next load result, and absence of a new upstream call at that checkpoint. | +| ORC-006 | Pass. Before the production guard, the random property failed after one case and shrank to `[acquire one]`: the later load completed instead of rejecting, and the fast path called upstream. This was an assertion failure at the intended checkpoint, not a setup failure or timeout. | +| ORC-007 | Pass. Fixed seed `186001` and seedless random lanes run the same generator, driver, comparison, and 50-run budget. Guarded replay with property `persistence.retained-demand`, seed `186001`, and path `0` reached the named owner and passed. | +| ORC-008 | Pass. Healthy-retained and terminal-retained states cannot be combined: the next identical acquisition returns `true` in the first and rejects in the second. The model's terminal suffix keeps that distinction without mirroring the production cache. | +| ORC-009 | Pass. A demand is the request's exact predicate/window identity; the upstream-call counter observes acquisition dispatch, not the full lifetime of an acquisition lease. The terminal receipt is distinct from collection readiness. | +| ORC-010 | Pass. The RED shrink preserved the post-terminal acquisition mismatch. The driver cleans the collection in `finally`; cleanup does not replace the primary assertion. | +| ORC-011 | Not triggered. No plausible shared semantic classifier was identified. The separate sibling-live-query test covers healthy warm readiness; this property covers terminal rejection at the persisted wrapper, not React rendering. | +| ORC-012 | Pass. This versioned record accounts for ORC-001 through ORC-011 at the exact semantic commit above. | + +The first candidate fix threw synchronously and failed four established terminal-load tests. The final guard returns a rejected promise, preserving that caller contract. Final local verification: the full persistence-core package passed 334 tests with one existing TODO; typecheck, build, staged lint, formatting, and guarded direct replay passed. The unrelated `@tanstack/db` random optimistic-history failure from PR CI is tracked separately; this record does not mark that check green. diff --git a/docs/contributing/oracle-reviews/2026-09-28-deferral-cleanup.md b/docs/contributing/oracle-reviews/2026-09-28-deferral-cleanup.md new file mode 100644 index 0000000000..9a6577d5c3 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-09-28-deferral-cleanup.md @@ -0,0 +1,153 @@ +# Publication deferral cleanup review + +## Reviewed state and law + +- Base commit: `33a194941c8d51f8f98babb999fef2987dd6ff8b`. +- Reviewed implementation and oracle head: + `8a6a024e` (the review record follows that immutable commit). +- Owner: `packages/db/tests/collection-subscription-lifecycle-publication.property.test.ts` + with its deferral histories in `collection-subscription-lifecycle-grammar.ts`. + +Collection cleanup ends a sync run. A publication deferral from that run cannot +discard or publish a later run's subscriber batch. The new run publishes its +committed changes when its own deferral closes with `publish()`. Its own +`discard()` suppresses those changes. The driver compares the exact subscriber +batch after each handle closes and checks for early delivery after an old +handle closes. + +The bounded grammar has two sync runs and one or two new rows. It crosses an +inner handle's `publish()` or `discard()`, four old outer-handle close modes, +and the new handle's `publish()` or `discard()`. One close mode leaves the old +handle open. Another closes it before the new deferral. The last two close it +with `publish()` or `discard()` while the new deferral is active. The grammar +contains 32 unique histories. + +## RED, repair, and GREEN + +On the base commit, the first controlled witness reached the new subscriber +callback checkpoint. It expected `[["c"]]` and observed `[]`. The first 16-case +grammar produced four assertion failures. Each failure required an inner +discard before cleanup and a new publication after restart. + +A one-line reset of the old discard flag made those 16 cases pass. An +independent prep review then found a second legal history. The old outer +handle could close the new run's active deferral. With an old `publish()`, the +subscriber received the new batch before the new handle closed. With an old +`discard()`, the subscriber missed the new batch. The expanded 32-case grammar +rejected that reset-only repair in 12 cases. These were assertion failures at +the intended old-handle or new-handle checkpoint. They were not setup failures +or timeouts. + +The repair gives each deferral its own record. Each handle captures that record. +Cleanup retires the record. A handle from the retired run cannot close the new +record. The 32 histories and the adjacent sync reentrancy suite passed, 160 +tests total. The DB oracle campaign passed 42 files and 2,669 tests with no +Vitest type errors. Changed-file ESLint, Prettier, and `git diff --check` +passed. Direct package `tsc --noEmit` still reports two unresolved +`@tanstack/db` self-imports in conformance files in this unbuilt checkout. It +reports no error in the changed files. + +## ORC-001 through ORC-011 + +| Requirement | Outcome | +| --- | --- | +| ORC-001 authority and limits | Pass. Collection cleanup and publication contracts require a restarted run to publish independently. The executable opening and coverage map state the finite history limit. | +| ORC-002 independent judgment | Pass. The reference selects post-restart writes and the new handle's close action. It does not read production depth, discard flags, revision fields, or event queues. | +| ORC-003 distinct responsibilities | Pass. The opening states the law. The shared grammar defines histories and the reference. The driver calls the real Collection sync, cleanup, restart, deferral, and subscription APIs. Subscriber batches at close are the refinement check. | +| ORC-004 grammar controls | Pass for bounded enumeration. The grammar reconstructs the reported sticky-discard history and the adjacent stale-handle overlap. Without inner discard, the original fault is absent. Without overlap, the second fault is absent. One and two rows distinguish loss from batch grouping. A new discard is a negative control. The range is two runs, one old row, and one or two new rows. Construction excludes a new-handle close before its open and restart before cleanup. | +| ORC-005 path and observation | Pass. The driver records two actual sync invocations and uses a real `subscribeChanges` callback. It checks no early batch during writes or an old-handle close, then checks the exact batch when the new handle closes. | +| ORC-006 checker calibration | Pass. The base failed four of 16 cells. The reset-only wrong design failed 12 of 32 cells. Both reached the intended subscriber comparison with an assertion failure. | +| ORC-007 campaigns and replay | Not applicable to this bounded enumeration. The owner's existing fixed-seed and seedless generated properties remain in the 42-file campaign. | +| ORC-008 model minimality | Pass. Post-restart row keys and the new close action determine this law's expected batch. An old-run close cannot change it. A different new row or close action changes the promised observation. | +| ORC-009 vocabulary | Pass. `cleanup` ends a sync run. `restart` begins a new sync run. Each `write` is one immediate sync transaction. A callback batch is the public publication observation. The grammar's handle labels identify deferrals, not acquisitions. | +| ORC-010 failure fidelity | Pass. The driver keeps the primary mismatch, attempts unsubscribe and cleanup separately, and places any cleanup errors after the primary error in an `AggregateError` with its cause. | +| ORC-011 second formulation | Not triggered. The identified wrong designs are rejected by the reference and real callback trace. A second semantic formulation has no named shared-fault hypothesis here. | + +This versioned record supplies ORC-012 evidence for the reviewed head. + +## Coverage boundary and review loss audit + +The Collection lifecycle publication owner covers the direct Collection path +for the 32 histories. The includes publication owner still needs a compiled +includes witness: cleanup during a discarded source deferral, then a parent or +child publication observed at its callback boundary. The coverage map names +that owner and witness. This direct-path result does not establish all includes +graph schedules or arbitrary numbers of nested handles. + +The source audit's three recorded claims and the prep review's adjacent +finding are accounted for. The sticky-discard and stale-handle product bugs +are fixed now. The missing lifecycle transition is covered by the bounded +grammar. The proposed one-record shape was useful but needed an identity check +to isolate old handles. No finding is refuted or silently deferred. The +compiled includes witness is an explicit remaining coverage item. + +## Follow-up review: subscriber change payloads + +- Reviewed executable head: `126ee08789d1146dcb1db3b173c66a39e8933701`. +- The review record follows that immutable commit. This follow-up changes the + oracle only; the Collection implementation and 32-history grammar are the + same as in the first review. + +The [Collection API](../../reference/interfaces/Collection.md) says that +`_deferPublication()` delays subscriber events until a coherent commit ends, +`cleanup()` clears the Collection and permits a later sync run, and +`subscribeChanges()` delivers change messages. The [glossary](../glossary.md) +defines cleanup as the end of a sync run. This PR makes one explicit design +decision at their boundary: a deferral handle belongs to the sync run in which +it opened. Cleanup retires that handle's authority over later publications. + +| Requirement | Follow-up outcome | +| --- | --- | +| ORC-001 authority and limits | Pass with the API, glossary, and design decision above. The direct Collection path and finite-history limits remain as stated above. | +| ORC-002 independent judgment | Pass. The reference still selects only post-restart fixture writes and the current handle's close action. It now describes each expected insert's key and row value. | +| ORC-003 distinct responsibilities | Pass. The existing opening, shared grammar, real Collection driver, and subscriber comparison remain visible. | +| ORC-004 grammar controls | Pass. The same 32 histories, ablations, range, and exclusions apply. | +| ORC-005 path and observation | Pass. The callback recorder now preserves each change's type, key, row value, and prior value. It keeps callback boundaries and excludes virtual fields from this bounded comparison. | +| ORC-006 checker calibration | Pass. A temporary mutant changed deferred callback row values to `-1`. Before this follow-up, all 32 histories survived. With the stronger recorder, 16 histories failed at the new-handle subscriber assertion. The original base and reset-only controls remain rejected. | +| ORC-007 campaigns and replay | Not applicable to the bounded enumeration, as above. | +| ORC-008 model minimality | Pass. The reference remains a stateless projection of fixture writes and the current close action. | +| ORC-009 vocabulary | Pass. The command-to-production mapping above remains unchanged. | +| ORC-010 failure fidelity | Pass. The driver still preserves the primary assertion and secondary cleanup errors separately. | +| ORC-011 second formulation | Not triggered. No new shared-fault hypothesis arose from the payload observation. | + +The focused publication and lifecycle suites passed 334 tests with no Vitest +type errors. Changed-file ESLint, Prettier, and `git diff --check` passed. The +compiled includes witness remains open with the same coverage-map owner. + +## Follow-up review: retired queue ownership and null prior values + +- Reviewed executable head: `cda3e71fbf900d1bcdab0bd28be027fca8c605ac`. +- This record follows that immutable commit. The production change releases + queued messages from deferrals retired by cleanup or their final close. + +A later CodeRabbit review of `9b069242` identified a retained queue. A stale +handle remains reachable after cleanup and captures the old deferral record. +Cleanup removed the manager's reference, but left its queued publications in +the captured record. `changes.ts` was byte-identical at the later `4e615774` +head, so the same finding applied there. A focused test queued one publication, +held the old handle, and checked the retired record after cleanup. Before the +repair it failed with one retained publication at the queue-cardinality +assertion. The adjacent final-close case retained the queue as well. The repair +empties each retired queue while preserving the messages needed for a normal +`publish()`. Both cases pass, and stale handles still cannot affect a new run. + +Queue retention has no public subscriber observation: a subscriber sees the +same messages whether a retired, reachable handle retains an array or releases +it. The focused internal invariant test is the appropriate witness for this +memory-ownership law. The 32 public publication histories remain the oracle +for delivery and isolation. The direct Collection path still does not prove +the compiled includes witness named above. + +A separate checker control exposed a null-value observation gap. A temporary +subject mutant injected `previousValue: null` at the final subscriber callback. +The first payload recorder treated that prior value as absent, and all 32 +histories stayed green. The corrected recorder preserves `null` and omits only +`undefined`; the mutant then failed 16 of 32 histories at the subscriber +equality assertion. Unmutated production passed all 32. This control changes +the oracle's observation fidelity, not the product contract or history grammar. + +The focused publication, sync reentrancy, and lifecycle suites passed 361 of +361 tests with no Vitest type errors. Changed-file ESLint, Prettier, and +`git diff --check` passed. Against `origin/main`, production `changes.ts` is +36 lines added and 30 removed, a net increase of six lines. Tests and review +documentation are accounted for separately. diff --git a/docs/contributing/oracle-reviews/2026-09-28-optimizer-aggregate-pushdown.md b/docs/contributing/oracle-reviews/2026-09-28-optimizer-aggregate-pushdown.md new file mode 100644 index 0000000000..c2b9cfabad --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-09-28-optimizer-aggregate-pushdown.md @@ -0,0 +1,53 @@ +# Optimizer aggregate pushdown oracle review + +Reviewed implementation: `37350e5b58b6006f24d9e7c68b894bac7b45ec46`. +Baseline with the original optimizer: `33a194941c8d51f8f98babb999fef2987dd6ff8b`. +The review record and changeset follow the reviewed implementation. They do not +change the oracle or production code. + +## Claim and evidence + +An outer predicate on an aggregate subquery must run after aggregation. The +optimizer may move it below the aggregate only when that move preserves the +result. The original `selectHasAggregates` missed `add(sum(v), 0)` and +aggregates inside `caseWhen`. The optimizer moved `isUndefined(s.k)` to the +source row's `k` field. The global aggregate omits `k` from its public row. +The moved predicate rejected both source rows and removed the public result. + +The oracle uses source rows `{k:1,v:10}` and `{k:2,v:20}`. Independent +recomputation gives total `30`. A materialized aggregate Collection and a +nested aggregate under a left join must both expose the same post-filter row. +The direct aggregate and grouped query are nearby controls. A left join keeps +the aggregate row when no matching joined row exists. An earlier inner-join +probe returned no row even without a WHERE clause for both direct and wrapped +aggregates, so it could not isolate optimizer pushdown. + +On baseline, the direct and grouped controls passed. The arithmetic wrapper, +the `caseWhen` condition-only aggregate, and the `caseWhen` default-only +aggregate failed the public `toArray` assertion. Each returned `[]` instead +of the modeled row. These are assertion kills at the intended checkpoint. +After the fix, all five oracle cases passed. The surrounding query directory +passed 87 files and 3,440 tests before the two final conditional cases were +added. The final focused optimizer and oracle run passed 51 tests. TypeScript, +ESLint, and Prettier checks passed. + +## ORC-001 through ORC-011 + +| Requirement | Outcome | +| --- | --- | +| ORC-001 Contract authority and limits | SQL aggregate semantics and the optimizer's documented safety rule require post-aggregate filtering. The owner checks the listed expressions and public snapshot under a left join. The coverage map names the omitted predicates, wrappers, join types, and incremental histories. | +| ORC-002 Independent judgment | The expected sum comes from `Array.reduce` over source rows. The expected outer result follows from applying the predicate after projection. Neither computation imports the optimizer's aggregate detector. | +| ORC-003 Distinguishable responsibilities | The oracle file states the law, stateless sum model, bounded expression grammar, public Collection driver, and `toArray` refinement check at synchronous publication. | +| ORC-004 Generated-history controls | Not applicable. The file enumerates fixed cases and does not claim generated-history coverage. | +| ORC-005 Production path and observation | `createLiveQueryCollection` compiles nested aggregate and outer join queries. The test compares exact public rows after `startSync: true`. Direct and materialized controls show the public path reaches the aggregate and predicate. | +| ORC-006 Checker calibration | The original optimizer is the wrong-design control. Its wrapped and conditional cases fail by value assertion at the named checkpoint. The direct and grouped controls pass on the same baseline. | +| ORC-007 Fixed/random replay | Not applicable. The owner has no important generated property. | +| ORC-008 Stateful-model minimality | Not applicable. The model recomputes one sum and retains no transition state. | +| ORC-009 Vocabulary mapping | No model-only lifecycle or state term is introduced. “Source rows,” “public row,” and “public snapshot” follow the glossary. | +| ORC-010 Failure fidelity and cleanup | Not applicable. The fixed cases do not shrink, capture mutable traces, or run cleanup that can replace the assertion failure. | +| ORC-011 Second formulation | The materialized aggregate Collection and the nested subquery have the same public projection and predicate. The former prevents cross-boundary pushdown and catches the plausible shared-fault risk in the sum model. Each expected result has one row, so order and multiplicity do not need normalization. | + +The bounded repair covers the contract × fixed source rows × nested left-join +path × first public snapshot cells above. It does not prove all optimizer +rewrites safe. The coverage map owns the remaining cells and the separate +global-aggregate inner-join observation. diff --git a/docs/contributing/oracle-reviews/2026-09-28-retracted-value-retention.md b/docs/contributing/oracle-reviews/2026-09-28-retracted-value-retention.md new file mode 100644 index 0000000000..3163c0387f --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-09-28-retracted-value-retention.md @@ -0,0 +1,105 @@ +# Held publication retracted-value review + +## Reviewed state and contract + +Baseline: `33a194941c8d51f8f98babb999fef2987dd6ff8b`. +Reviewed change: the Git tree containing this record, the builder fix, and the +expanded includes publication oracle. The final commit identifies that tree. +The audit source is DEC-02 in +`scripts/code-weight/BUGS_AND_ORACLE_GAPS.md` in the local code-weight audit +worktree. PR #1917 covers other optimizer and IR-shaped-value findings. + +The maintainer chose the last subscriber-visible value and position as the +comparison baseline for a held publication. The authority is the coherent +publication and atomic ordered-window contract in +`packages/db/src/query/live/ARCHITECTURE.md`. The primary executable owner is +`packages/db/tests/query/includes-publication-oracle.test.ts`. Effect callbacks +use the same held source history. Their update event's `previousValue` is also +specified by `packages/db/src/query/effect.ts` and the existing +`packages/db/tests/effect.test.ts` multi-step update case. + +## RED, fix, GREEN, and limits + +The oracle starts with two public parent rows in rank order `[1,2]` and one +inline child for row 1. An ordered repair holds publication while the source +writes row 1 with rank 2.5 and values `A → B → A`. The final public order is +`[2,1]`; the payload returns to `A`. On the baseline, the subscriber receives +no callback. The new exact test failed at the final callback comparison: +expected one layout-only callback with the final rows and `changes: []`, +actual no callback. The unchanged final value does not require a value-update +event. The single-step `A → A` reorder and `A → B → C` value-change controls +passed in the same RED run (1 failed, 2 passed, process exit 1). + +The builder had retained the last retracted value and position, `B` at the +new position. It now retains the first pair, `A` at the old position, in the +same existing accumulator. No new lifecycle state was added. The same three +tests passed after the change (3 passed, process exit 0). The complete includes +publication file passed 63 tests; the complete Effect file passed 74 tests. +The production diff in the builder is net zero lines. The architecture states +the decided rule. + +The Effect side of the new oracle observes no value callback for `A → B → A` +and one update from `A` to `C` for `A → B → C`. A hostile combined Effect +design that retained the last private retraction and used it instead of the +callback-visible row failed at those callback assertions: it emitted a false +`B → A` update and reported `B` as the previous value for `C`. These were +assertion failures at the intended checkpoint. Changing `deleteValue ??=` to +assignment *alone* survived the existing one-transaction Effect test and a +separate two-source join probe. Those paths emitted only one relevant +retraction. The compound mutant proves the public-state rule; it does not +prove that each internal branch is independently necessary. + +This claim covers the bounded in-memory ordered repair with two parent rows, +one inline child, three source writes, root Collection reads and subscriber +callbacks, and an ordered Effect's callbacks. It does not prove every ordered +repair schedule, Collection-valued child facade layout, or concurrent +optimistic overlay. The coverage map names the receiving owners and witnesses. +No reachable counterexample remains in the tested grammar. + +## ORC-001 through ORC-011 + +| Requirement | Outcome | Evidence | +| --- | --- | --- | +| ORC-001 authority and limits | Pass | The architecture now states the last subscriber-visible comparison rule. The oracle header, this record, and the coverage map bound the claim. | +| ORC-002 independent judgment | Pass | A plain source-row Map sorts by rank and derives child IDs. It does not inspect D2 output or call the builder's order detector. Effect event expectations follow initial and final values. | +| ORC-003 distinguishable responsibilities | Pass | The held-publication block states the law and bounded grammar, defines the Map recomputation, drives real Collections and an Effect, and checks reads and callback observations before and after release. | +| ORC-004 generated grammar controls | Not applicable | The added cases are a bounded three-cell matrix, not a generated-history claim. The existing generated properties in the same file are unchanged. | +| ORC-005 production path and observation | Pass | A real on-demand ordered source holds `loadSubset` while three sync transactions reach a compiled include query. The test asserts that a hold exists, no intermediate callback appears, public rows remain old during the hold, and final callbacks match the model. | +| ORC-006 checker calibration | Pass | The unchanged builder failed the final subscriber assertion. A last-private-retraction Effect design failed its callback assertions. The single-step and final-value controls distinguish an always-quiet result from the intended repair. | +| ORC-007 fixed/random replay | Not applicable | This change adds deterministic bounded cells, not an important generated property. Existing generated campaigns and replay wiring remain unchanged. | +| ORC-008 model-state minimality | Not applicable | The model is a plain current source-row Map. It introduces no new stateful reference lifecycle or state merge. | +| ORC-009 vocabulary mapping | Pass | The model Map represents source rows only. It does not claim to represent acquisitions, private D2 relation rows, or publication state. “Held repair” is the adapter's pending ordered acquisition; “public snapshot” is the last subscriber-visible Collection state. | +| ORC-010 failure fidelity and cleanup | Pass | `withHistoryCleanup` releases every held request and disposes the Effect and Collections. It preserves the primary assertion separately from cleanup errors. No shrinker or serializer rewrites the failing history. | +| ORC-011 independent second formulation | Not applicable | The named fault is last-versus-first retraction retention. The sorted source Map and observed public callbacks already distinguish it. No different query formulation is needed to adjudicate this bounded rule. | + +ORC-012 is this versioned, baseline-identified record and its coverage-map +link. The final commit and PR identify the immutable reviewed change. + +## Review finding audit + +The DEC-02 review made four distinct claims or proposals. The task-local +ledger preserves their original wording, source order, and probe details. + +| ID | Final disposition | Evidence and durable destination | +| --- | --- | --- | +| DEC02-01 | Fixed now | The baseline's last-retraction rule lost the layout callback in the held `A → B → A` case. The builder now retains the first retraction. The publication oracle checks the resulting callback. | +| DEC02-02 | Already fixed | Effect already keeps the first retracted value. The new Effect callback checks observe the final value against the last callback-visible value. The separate assignment mutant survived, so the record does not claim that this line alone is calibrated. | +| DEC02-03 | Deferred | The facade map uses `serializeValue` for key identity, but its accumulator does not retain a prior value or position. A facade-held reorder needs its own witness in `includes-collection-oracle.property.test.ts`, as the coverage map now states. | +| DEC02-04 | Fixed now | The maintainer chose the last subscriber-visible comparison rule. The held three-write history was RED on the baseline and GREEN after the builder repair. The architecture now states the rule. | + +Loss audit: four raw items equal two fixed now, one already fixed, and one +deferred. No item lacks a disposition. The facade witness remains outside this +PR's bounded root Collection claim. + +The reviewer identified a real difference between the builder and Effect and +proposed the right multi-step history. The review marked the builder bug as +tentative and did not specify a public failure or an authority for the rule. +Its technical accuracy and signal were high, while its proof depth was +limited. I would hire this reviewer for code review with an expectation that +behavioral findings include a failing public observation. + +A separate PR review found that the new Collection callback check initially +kept only each change type and key. A stale update payload could have passed +that check. The oracle now also asserts that the `A → B → C` update reports +`previousValue: A` and `value: C`. The two focused suites passed 137 tests +after this review fix. Changed-file ESLint and formatting checks passed. diff --git a/docs/contributing/oracle-reviews/2026-09-30-guide-portfolio-followup.md b/docs/contributing/oracle-reviews/2026-09-30-guide-portfolio-followup.md new file mode 100644 index 0000000000..6766a6e586 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-09-30-guide-portfolio-followup.md @@ -0,0 +1,112 @@ +# Oracle guide portfolio: bounded follow-up + +Executable revision: `18d3ad92dab02a162cff2067a00ae95bea284883` +Guide revision: `a1d75726` +Baseline review: [September 30 portfolio audit](2026-09-30-guide-portfolio.md), +which assessed executable revision `7e4266bb`. + +This append-only follow-up assesses the executable revision above. It covers +17 previously audited files named `oracle` or `property`, one Chromium OPFS +receiving spec, and Electric cleanup support. The follow-up changes tests, +test support, and the coverage map, with no production-source change. The +original 90-owner audit remains the inventory; this record reports only the +changed evidence and carries its unresolved portfolio gaps forward. A passing +expected-failure guard records a product counterexample, not a passing product +law. No universal bug-class closure is claimed. + +## Verification at the executable revision + +| Campaign | Result | +| --- | --- | +| Changed DB oracle files | 678/678 within the full campaign | +| Full DB oracle command | 3,707/3,707 across 55 files; no type errors | +| Query DB Collection oracle command | 474/474 across 16 files; TypeScript passed | +| Offline FIFO and serializer properties | 44/44; TypeScript passed | +| PowerSync oracle command | 12/12, no skips; TypeScript passed | +| Browser SQLite Node oracle command | 113 passed, 4 typecheck placeholders skipped, 0 failed | +| Chromium OPFS configured suite | 4/4, no skips; fairness spec 2/2 | +| Electric runtime oracle command | 328/328 across seven files, no runtime skips | +| Formatting, lint, diff check | Prettier and diff check passed; ESLint exited 0 with seven warnings and no errors | + +The literal Electric `test:oracles` command exits 1 after its runtime tests +because the typecheck project cannot resolve three unchanged imports of +`@tanstack/electric-db-collection` in shared `db-collection-e2e` move and +progressive suites. A seven-file runtime command with typecheck disabled exits +0. No edited Electric file produced a reported type error. The browser's four +skips are typecheck placeholders; its runtime lifecycle properties executed. + +The cross-formulation owner's checked commands are printed beside its two +calibration tests. Seed `1658004`, path +`0:0:0:0:0:0:0:0:0:0:0:0:0:0`, and property +`includes-cross-formulation.equivalence` reproduce one intentional main-property +failure at cross-formulation checkpoint 0. Seed `1658005`, path +`0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0`, and property +`includes-cross-formulation.ordered-window` reproduce one at the ordered-window +checkpoint 0. Each command selects the requested replay directly; no normal +campaign runs first. Removing its explicit calibration switch makes the same +seed/path replay pass. The serializer directly replayed seed `20260914` and +path `0:1:0:0:0:1:1:1:1:1` at its wire checkpoint. The intentionally failing +cross-formulation commands are calibration evidence, not a package test failure. + +## Guide requirement verdicts + +These outcomes apply to this follow-up and preserve the baseline's wider +partial verdicts. `Conditional` means the requirement applies only where its +trigger holds. The executable files below supply the contract, model, +grammar, driver, and check; this record supplies exact-revision evidence and +limits. + +| Requirement | Follow-up outcome and evidence | +| --- | --- | +| ORC-001 authority and limits | **Partial.** The unsupported FIFO one-timer/no-poll assertion was removed while the documented deadline/order law stayed. Comparison normalization authority is stated and bounded. Offline settlement still contradicts its README: a fulfilled configured `mutationFn` followed by failed fake-storage outbox deletion fulfills the caller although the README requires durable deletion. Product authority must be chosen before that owner can be closed. | +| ORC-002 independent judgment | **Bounded.** Array/Map recomputation, independent branch semantics, and authored provider histories remain separate from the tested production machinery. The new product counterexamples compare against the documented query meaning, not current output. No claim is made for unmodeled query forms. | +| ORC-003 visible responsibilities | **Bounded.** Each changed owner retains its visible contract, model, legal-history grammar, production driver, and public refinement cut. Electric's setup helper is directly named by its driver. No file-format change is required. | +| ORC-004 generated grammar controls | **Improved, still partial.** Central includes now pins transition placements, route shapes, destinations, batch cells, exclusions, and public effects of ablation. Cross-formulation and utils add reconstructed boundary histories and independent branch/value controls. Legal two-descendant rekeys at `[depth,target]` `[3,1]` and `[4,2]` have fixed composed witnesses but remain outside the generated two-transition family. Larger includes scenario, scalar, alpha-renaming, and some full-row/noise grammars remain outside complete ablation evidence. | +| ORC-005 production path and public observation | **Bounded.** New Collection facade, same-root optimistic, D2 live-query, Query DB demand, PowerSync watcher, and Chromium OPFS witnesses reach their named entry points and compare public rows, events, calls, or logical completion at named cuts. Controlled and real-host limits remain distinct. | +| ORC-006 checker calibration | **Improved, still partial.** Exact wrong-result controls, replayed hostile failures, and expected-failure product guards reject named plausible mistakes. Synthetic checker controls calibrate their comparisons; they do not alone prove production capture. Other laws in the portfolio still lack a demonstrated distinguishing kill. | +| ORC-007 fixed/random parity and direct replay | **Improved, still partial portfolio-wide.** Serializer captured a failing shrink path and directly replayed its wire comparison. Cross-formulation captured hostile production-driver failures and replayed each reduced counterexample through the selected main property at public checkpoint 0. Its two checked seed/path/property commands intentionally exit 1 with one named failure; the same commands without explicit calibration pass. Other portfolio replay gaps remain. | +| ORC-008 stateful-model minimality | **Triggered and bounded.** New source-row Maps in the held facade, held optimistic, aggregate QueryRef, and joined `findOne()` references retain distinctions that legal next actions expose. A held facade can show the same old public rows while private rank/value changes make release publish a different order or event. A hidden joined second candidate changes the result after deletion of the first; item/person IDs determine later aggregate matches. These Maps retain source identity and join/rank/value fields, not production queue or compiler state. The optimistic rank argument includes one adjacent legal branch not executed by this fixed witness. | +| ORC-009 vocabulary mapping | **No new divergent model abstraction.** New histories use the glossary's source, Collection, acquisition, replay, publication, and checkpoint terms. Driver-local fault labels are identified as harness diagnostics. The baseline's conditional verdict carries forward. | +| ORC-010 failure fidelity and cleanup | **Improved, still partial portfolio-wide.** Load-subset releases its mutation gate and Collections while optimistic transaction settlement may remain pending, and forwards a rejected applied receipt without unhandled child promises. Separate D2 calibrations resolve held replay work and label multiple cleanup faults. Electric restores an SDK spy and HTTP provider on setup failure; comparison and serializer preserve the first mismatch through shrink/replay. These calibrations are bounded to their harnesses; a cleanup callback that never settles is not covered. | +| ORC-011 second formulation | **Conditional.** Cross-formulation compares independently computed ternary branches and nested/flat/per-parent results. The optimizer's materialized-Collection path is a different formulation that returns the expected aggregate row while the nested QueryRef path fails. No second path is claimed where no shared-fault hypothesis was named. | +| ORC-012 review evidence | **Recorded with gaps.** This record names the exact executable revision, changed owners, applicable requirement outcomes, product counterexamples, campaign results, and remaining coverage owners. It does not promote a partial result into portfolio conformance. | +| ORC-013 distinguishing boundary witness | **Improved, still partial.** Finite child repair and facade events, same-root optimistic overlap, disconnected Query DB branches, virtual root projection forms, OPFS K=1 persist backlog, and D2 scan/restart cuts now distinguish nearby wrong consequences. Matching nested QueryRef aggregates and initial joined `findOne()` remain reachable product counterexamples. | +| ORC-014 controlled-premise handoff | **Improved, still partial.** The same fixture-held storm runs through a real Chromium OPFS worker and is checked at logical adapter completion; native local SQLite plus an SDK-delivered watcher supplies one PowerSync metadata-update premise. Electric live-service framing, external D2/load-subset provider delivery, native Temporal/browser storage, held PowerSync callback ordering, and remote upload remain owned, unproved handoffs. | + +## Changed owner evidence and limits + +| Owner | New evidence and applicable ORC IDs | Remaining limit | +| --- | --- | --- | +| Browser OPFS fairness spec | Real worker cold-read reach, K=1 backlog, five logical persist completions in FIFO order (005, 013, 014) | One held-BEGIN Chromium history; no durable reopen order or browser matrix. | +| DB comparison property | Contract authority and first-failure fidelity through normalization, shrink, and replay (001, 006, 010) | Bounded value encodings and sampled histories. | +| D2 source reconciliation property | Scan/index truncate and live-query cleanup/restart cuts; labeled fault cleanup and held replay release (005, 010, 014) | External provider event shape and arbitrary graph shapes remain open. | +| Includes Collection property | Held finite ordered repair request, exact facade event payload, identity, silence, and A→B→A value/order history (005, 008, 013, 014) | Controlled provider timing; real-provider ordering open. | +| Includes cross-formulation property | Per-branch ternary relation, bounded grammar, production-driver hostile replay (004, 006, 007, 011) | Longer histories, deeper trees, and async provider races. | +| Includes optimistic property | Same-root optimistic write during held finite repair; exact public snapshots/events, request options, and retained source-rank distinction (005, 008, 013) | Controlled repair request and bounded history. | +| Central includes property | Pinned transition, route, and batch grammar controls with public differences (004, 006) | Two-descendant rekeys `[3,1]`/`[4,2]` are fixed witnesses only; broad scenario, scalar, and alpha-renaming axes need separate ablation review. | +| Load-subset property | Primary and secondary cleanup errors in helper-level calibration; cleanup-protected setup, pending optimistic settlement, and controlled applied-rejection forwarding (010) | Controlled adapter delivery; real-provider cancellation and nonsettling cleanup callbacks are open. | +| Optimizer semantics oracle | Matching inner-join global aggregate now has exact initial public counterexample and materialized control (005, 011, 013) | Nested QueryRef positive law fails; other join forms and updates unproved. | +| QueryRef user-value oracle | Green source-update and later singleton/empty histories; retained source-identity distinction; exact initial aggregate and `findOne()` counterexamples (005, 008, 013) | Two initial QueryRef product defects; other `singleResult` and join forms open. | +| Virtual row fields oracle | Implicit, expression, and functional root publication with wrong-field control (005, 013) | Nested projections and framework receiving cuts open. | +| Utils property | Non-ring grammar reconstruction, adjacent boundaries, and wrong-comparator controls (004, 006, 013) | Declared value classes and graph shapes only. | +| Electric lifecycle and SDK delivery properties | Labeled concurrent assertion/cleanup faults and setup-failure spy/provider cleanup (006, 010, 014) | Authored HTTP plus installed SDK; live Electric frames not observed. | +| Offline FIFO property | Unsupported timer-count claim removed; executor deadline, order, and outbox counts remain checked (001, 013) | Public caller settlement and deletion-failure behavior belong to the settlement owner. | +| Offline serializer property | Captured seed/shrink-path direct wire replay and simultaneous fault cleanup (007, 010, 014) | Controlled Temporal and fake storage; native receiving witness open. | +| PowerSync correctness oracle | Native SQLite, real SDK callback delivery, exact queued CRUD patch for metadata-bearing `Collection.update` (005, 014) | Held callback and zero-field cases controlled; remote upload unproved. | +| Query DB includes-work oracle | Disconnected branch at preload leaves public rows unchanged with exact acquisition/provider calls (005, 013) | Provider scans, facade allocation, and internal index traversal unmeasured. | + +## Product counterexamples and unresolved handoffs + +| Boundary and owner | Present observation | Required next witness or decision | +| --- | --- | --- | +| Optimizer nested QueryRef inner join, `optimizer-semantics-oracle.test.ts` | Matching global aggregate yields `[]` at initial public snapshot, expected `[{ total: 30 }]`. | Production correction plus adjacent accepting/rejecting and update histories; keep the materialized formulation as control. | +| Flat aggregate QueryRef join, `subquery-user-value-oracle.test.ts` | Initially matching aggregate row is omitted. | Production correction; run first-publication and source-update histories through the same public check. | +| Joined QueryRef `findOne()`, `subquery-user-value-oracle.test.ts` | Two initial candidates are published instead of the first. | Production correction; keep later singleton/empty/restored controls. | +| Offline settlement, `transaction-settlement.property.test.ts` and README | Caller fulfills after a fulfilled configured `mutationFn` and deletion failure while a fake-storage outbox row remains; README promises success only after durable deletion. | Product decision on provider completion, durable deletion, or separate outcomes, then aligned README, model, driver, restart/idempotency witness. | +| Electric live provider, `e2e/electric.e2e.test.ts` | Controlled SDK frames lack a live-service receiver for the exact snapshot-end/subset-end and tagged move premise. | Capture those frames from a live Electric/PostgreSQL service and compare the same public cut. | +| D2 and load-subset provider handoff, their named owners plus Electric E2E | Controlled event and cancellation timing. | Real-provider event/cancellation witness for the specific timing before a cross-boundary claim. | +| Serializer runtime handoff, `transaction-serializer.property.test.ts` | Controlled Temporal constructors and fake storage. | Native Temporal and browser storage receiving cases. | +| PowerSync remote path, `correctness-oracle.test.ts` | Local SQLite and SDK watcher; no backend upload fixture. | Remote upload receiving witness and separate held callback/zero-field Collection path if those claims are made. | + +The [coverage map](../oracle-coverage.md) retains additional scoped owner cells. +These open rows are evidence boundaries, not exemptions from the guide. diff --git a/docs/contributing/oracle-reviews/2026-09-30-guide-portfolio.md b/docs/contributing/oracle-reviews/2026-09-30-guide-portfolio.md new file mode 100644 index 0000000000..2146271cba --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-09-30-guide-portfolio.md @@ -0,0 +1,169 @@ +# Oracle guide portfolio review — 2026-09-30 + +Executable revision: 7e4266bb7a38b55528fbf023921061320eba792e +Guide revision: a1d75726 +Review scope: the 90 executable files under packages whose basenames contain oracle or property and end in .test.ts, .test.tsx, or .test-d.ts. Each owner was assigned to a separate fresh agent. This inventory rule includes three compile-time type oracles. Other specialized contract tests in the coverage map remain separate owners. + +This record assesses the executable revision above. Later edits require a new record or an append-only follow-up tied to their own revision. A passing sampled campaign is evidence inside its declared domain, not a proof over all legal histories. No bug-class closure or general real-provider claim is made by this portfolio audit. + +## Result and verification + +85 owners were revised; 5 were retained after review. The revision changes tests, test support, package oracle commands, and the coverage map; it changes no production source. The guide added ORC-013, which asks for a distinguishing boundary witness, and ORC-014, which asks for a receiving witness or an explicit handoff when a controlled premise is cited across a provider or host boundary. + +| Runtime campaign | Result | +| --- | ---: | +| DB oracle/property files, including guarded replay | 3,541/3,541 | +| DB IVM | 181/181 | +| Query DB Collection | 272/272 | +| Browser SQLite persistence | 67/67 | +| PowerSync | 11/11 | +| TrailBase | 41/41 | +| Node SQLite persistence | 52/52 | +| SQLite persistence core | 29/29 | +| Vue | 6/6 | +| Offline transactions | 124/124 | +| Electric | 294/294 | + +These runs cover 87 runtime files and 4,618 passing tests. The three type-only owners were checked through their package TypeScript builds. TypeScript checks passed for DB, DB IVM, Query DB, Browser, PowerSync, TrailBase, Node SQLite, SQLite core, Vue, and Offline. Electric's runtime campaign passed; its package TypeScript command still reports three imports of @tanstack/electric-db-collection from unchanged shared end-to-end suites. Prettier, the staged ESLint hook, and git diff --check passed. The DB replay integration used filesystem access for child Vite temporary files. + +## ORC-001–014 verdict ledger + +This is a portfolio-level ledger. The executable file is the primary evidence for each owner; the inventory below identifies every reviewed file. A partial outcome means the named trigger remains open for at least one owner. A conditional outcome applies only where the requirement's trigger holds. The coverage map names receiving owners for cross-boundary cells. + +| Requirement | Outcome at executable revision | Evidence and remaining limit | +| --- | --- | --- | +| ORC-001 contract authority and limits | Partial | Opening contracts and bounds were added or clarified across owners. The comparison normalization rule and FIFO one-timer assertion still need explicit authority. Offline settlement has a direct README/test contradiction, listed below. | +| ORC-002 independent judgment | Bounded | Independent arrays, maps, ledgers, algebraic relations, and authored provider responses remain the expected-result sources. The comparison audit found and corrected a false reference rule: signed infinity is a valid comparator result. Shared semantic faults remain possible outside the bounded formulations. | +| ORC-003 visible responsibilities | Bounded | Agents checked the contract, model, grammar, production driver, and refinement check in each assigned owner or directly named companion. The compile-time owners state their type-level boundary rather than claiming runtime delivery. The five retained owners were reviewed without a source edit. | +| ORC-004 grammar controls | Partial, generated owners only | Added reconstruction, axis contribution, bounds, marginal cases, and exclusions in many generated owners. Full ablation/reconstruction evidence remains incomplete in some large includes and utility grammars. Fixed-only and type-only owners do not trigger this rule unless they claim a generated grammar. | +| ORC-005 production path and observation | Bounded | All runtime owner campaigns reached their named production driver and comparison; the package results above are reach evidence. Controlled callbacks, fake storage, authored HTTP, and synthetic host events establish only their declared boundary. The type-only owners do not make a runtime observation claim. | +| ORC-006 checker calibration | Partial, important generated or repaired owners only | Wrong-result controls and captured hostile failures were added, including comparison, cursor, index update, leadership, scheduler, Electric SDK delivery, and settlement. Some laws still lack a demonstrated distinguishing kill at the intended checkpoint; a green campaign alone was not counted as calibration. | +| ORC-007 fixed/random parity and direct replay | Partial, important generated owners only | Named paired campaigns and seed/path replay were added broadly. The guarded replay suite passed 28/28 after the manifest learned computed includes and pagination IDs. Captured failing shrink-path replay is still unshown for some secondary generated laws, including the offline serializer. Fixed-only, diagnostic, and type-only owners do not trigger this rule. | +| ORC-008 stateful-model minimality | Conditional | No portfolio-wide state-minimality proof is claimed. Owners that changed a model's state mapping explain distinctions locally; a file without an introduced, removed, combined, or split model state does not trigger this rule. Any unrecorded state change remains an audit gap. | +| ORC-009 vocabulary mapping | Conditional | Model-only Pair, Scope, ledger, tag-bit, and clock abstractions were mapped to production boundaries in the relevant files. A file with no divergent model term does not trigger this rule. The project glossary remains authoritative for shared terms. | +| ORC-010 failure fidelity and cleanup | Partial | The guarded replay harness now preserves the primary failure as an AggregateError cause and labels cleanup diagnostics. Many owner cleanups were strengthened. The core load-subset owner still has cleanup paths that may mask a mismatch; the Electric SDK shared cleanup helper has not had a simultaneous cleanup fault injected. Some multi-assertion properties do not retain a pre-shrink failure signature. | +| ORC-011 second formulation | Conditional | The includes cross-formulation owner exercises distinct query formulations. Where no concrete shared-fault hypothesis was named, the trigger does not apply. A newly named shared fault requires a separate formulation or an explicit tracked reason under this rule. | +| ORC-012 review evidence | Recorded with gaps | This exact-revision ledger and inventory provide the closeout record. Partial entries and the owner table below remain unresolved; the record does not convert them into passes. No bug-class closure is claimed. | +| ORC-013 distinguishing boundary witness | Partial | Added legal premise-reaching and nearby distinguishing cases, including includes-space leaf counts 0/1/10, comparator infinity/NaN, pagination margins, optimizer accepting/rejecting filters, retry deadlines, and FIFO K=1/K=2. The held finite child repair/facade subscriber and other cells below remain open. | +| ORC-014 controlled-premise handoff | Partial where cross-boundary claims are made | The coverage map now names controlled premises and receiving owners. A controlled-only claim does not trigger a real-provider obligation. Real OPFS persist FIFO, live Electric-service framing, native Temporal/browser storage, and several provider paths have no matching receiving witness at this revision. | + +## Material findings and open owner cells + +| Owner | Requirement | Finding or next witness | +| --- | --- | --- | +| packages/db/tests/comparison.property.test.ts | ORC-001, ORC-010 | Corrected the reference model to accept signed infinity, matching the established comparator/index contract. Exact authority for some internal normalization encodings remains weaker; multi-assertion shrink reports do not retain the original mismatch separately. | +| packages/offline-transactions/tests/KeyScheduler.property.test.ts | ORC-005, ORC-014 boundary | Corrected the modeled retry sequence to updateTransaction before markFailed. This scheduler owner does not observe the executor's awaited outbox update or public caller settlement; transaction-settlement owns the receiving cut. | +| packages/offline-transactions/tests/transaction-settlement.property.test.ts | ORC-001 | README says success follows durable outbox removal. The existing acknowledgement-delete-failure test expects fulfillment while the outbox row remains. The contract owner must resolve this conflict before claiming the settlement law closed. | +| packages/offline-transactions/tests/fifo-retry.property.test.ts | ORC-001 | FIFO and retry-deadline behavior are documented; the exact one-timer/no-poll assertion still needs explicit contract authority. | +| packages/offline-transactions/tests/transaction-serializer.property.test.ts | ORC-007, ORC-014 | The generated grammar now reconstructs its ten-edit pinned history. Direct seed/path replay runs, but a captured failing shrink-path replay was not demonstrated. Native Temporal and browser-storage receiving witnesses are not supplied. | +| packages/db/tests/query/load-subset-oracle.property.test.ts | ORC-010, ORC-014 | Exact demand-key witnesses were added. Some cleanup paths can still replace a primary assertion; controlled adapter delivery does not prove real-provider behavior. | +| packages/electric-db-collection/tests/electric-sdk-delivery.property.test.ts | ORC-010, ORC-014 | Same-checkpoint shrink/replay now rejects row-drop and silent-move faults through the installed SDK and controlled HTTP. Simultaneous secondary cleanup failure is untested; a live Electric service has not supplied these authored frames. | +| packages/browser-db-sqlite-persistence/tests/shared-driver-fairness-oracle.test.ts | ORC-014 | Node seam distinguishes K=1/K=2 and full logical persist FIFO. Chromium OPFS checks K=1 hydrate, but full real OPFS persist completion order remains open with the Browser OPFS fairness spec owner. | +| packages/db/tests/query/includes-collection-oracle.property.test.ts | ORC-013 | A held truncate replay witness was added. A finite child A→B→A repair with a Collection facade subscriber is still needed; the coverage map keeps this with the same owner. | +| packages/db/tests/query/includes-optimistic-oracle.property.test.ts | ORC-013 | A concurrent optimistic update to the same root row during held repair still needs public snapshot and event comparison. | +| packages/query-db-collection/tests/includes-work-counter-oracle.test.ts | ORC-013 | Current nested tree makes every branch reachable. An irrelevant-branch acquisition witness is still needed to support a broader work claim. | +| packages/db/tests/query/subquery-user-value-oracle.test.ts | ORC-013 | DISTINCT support transitions are checked; aggregate source-update histories and joined findOne/singleResult are open. | +| packages/db/tests/query/optimizer-semantics-oracle.test.ts | ORC-013 | Accepting and rejecting outer filters are now checked. Inner-join global-aggregate behavior remains an open path. | +| packages/db/tests/utils.property.test.ts | ORC-004, ORC-006, ORC-013 | Ring boundaries and a replayed hostile comparator are checked. Non-ring generator ablation, some law calibrations, and adjacent witnesses remain incomplete. | +| packages/powersync-db-collection/tests/correctness-oracle.test.ts | ORC-014 | Native SQLite and unmocked ordinary watcher delivery run. Held callbacks are controlled; synthetic no-op/metadata mutations do not prove Collection.update can construct them or that remote backend upload behaves identically. | +| packages/db/tests/d2-source-reconciliation-oracle.property.test.ts | ORC-014 | Controlled indexed Effect consumption does not establish graph restart/truncate, scan routes, or external provider delivery. | +| packages/db/tests/query/virtual-row-fields-oracle.test.ts | ORC-013 | Direct public field presence is checked at publication. Projection-shape and framework receiving boundaries remain outside this fixed owner. | + +The coverage map contains additional scoped limits for ordered acquisition, provider transport, type-only oracles, and includes publication. These entries are unresolved cells, not waiver text. A new closure claim must supply the missing legal history, production path, public observation, and named checkpoint. + +## Owner inventory + +Revised means changed in executable revision 7e4266bb; retained means the assigned reviewer kept the source as it was. Every owner below is bounded by the verdict ledger above. + +| Executable owner | Disposition | +| --- | --- | +| packages/browser-db-sqlite-persistence/tests/opfs-page-lifecycle-oracle.test.ts | revised | +| packages/browser-db-sqlite-persistence/tests/opfs-worker-diagnostics-oracle.test.ts | revised | +| packages/browser-db-sqlite-persistence/tests/per-collection-coordinator-oracle.test.ts | revised | +| packages/browser-db-sqlite-persistence/tests/shared-driver-fairness-oracle.test.ts | revised | +| packages/db-ivm/tests/hash-failure-retry.property.test.ts | revised | +| packages/db-ivm/tests/hash-graph.property.test.ts | revised | +| packages/db-ivm/tests/hash-mixed-graph.property.test.ts | revised | +| packages/db-ivm/tests/hash.property.test.ts | revised | +| packages/db-ivm/tests/incrementalization-law.property.test.ts | revised | +| packages/db-ivm/tests/multiset-consolidate-oracle.property.test.ts | revised | +| packages/db-ivm/tests/operators/topk-relation-oracle.test.ts | retained | +| packages/db-ivm/tests/operators/topk-support-window-oracle.test.ts | revised | +| packages/db-ivm/tests/temporal-group-key-oracle.test.ts | revised | +| packages/db-sqlite-persistence-core/tests/persisted-options-type-oracle.test-d.ts | revised | +| packages/db-sqlite-persistence-core/tests/persisted-readiness-oracle.test.ts | revised | +| packages/db/tests/btree-map-oracle.test.ts | revised | +| packages/db/tests/change-event-history-oracle.test.ts | revised | +| packages/db/tests/cleanup-queue.property.test.ts | revised | +| packages/db/tests/collection-cleanup-restart-oracle.test.ts | revised | +| packages/db/tests/collection-metadata-publication-oracle.property.test.ts | revised | +| packages/db/tests/collection-mutation-startup-oracle.test.ts | revised | +| packages/db/tests/collection-state-retention-oracle.property.test.ts | revised | +| packages/db/tests/collection-subscription-lifecycle-history.property.test.ts | revised | +| packages/db/tests/collection-subscription-lifecycle-oracle.test.ts | revised | +| packages/db/tests/collection-subscription-lifecycle-publication.property.test.ts | revised | +| packages/db/tests/collection-subscription-reentrancy-oracle.test.ts | revised | +| packages/db/tests/collection-subscription-replay-oracle.property.test.ts | revised | +| packages/db/tests/collection-truncate-ownership-oracle.property.test.ts | revised | +| packages/db/tests/comparison.property.test.ts | revised | +| packages/db/tests/cursor.property.test.ts | revised | +| packages/db/tests/d2-source-reconciliation-oracle.property.test.ts | revised | +| packages/db/tests/db-client-hydration-authority-oracle.test.ts | revised | +| packages/db/tests/effect-disposal-oracle.test.ts | revised | +| packages/db/tests/index-suggestion-oracle.test.ts | revised | +| packages/db/tests/index-update.property.test.ts | revised | +| packages/db/tests/live-query-observer-history.property.test.ts | revised | +| packages/db/tests/mutation-handler-type-oracle.test-d.ts | revised | +| packages/db/tests/optimistic-transaction-oracle.property.test.ts | revised | +| packages/db/tests/replay-guard-calibration.fixture.test.ts | retained | +| packages/db/tests/oracle-replay.test.ts | revised | +| packages/db/tests/paced-mutations-oracle.test.ts | retained | +| packages/db/tests/query/cold-join-reconciliation-oracle.test.ts | revised | +| packages/db/tests/query/identity-output-shape-oracle.test.ts | retained | +| packages/db/tests/query/includes-collection-oracle.property.test.ts | revised | +| packages/db/tests/query/includes-context-transport-oracle.test.ts | revised | +| packages/db/tests/query/includes-cross-formulation-oracle.property.test.ts | revised | +| packages/db/tests/query/includes-functional-projection-oracle.test.ts | revised | +| packages/db/tests/query/includes-optimistic-oracle.property.test.ts | revised | +| packages/db/tests/query/includes-oracle.property.test.ts | revised | +| packages/db/tests/query/includes-publication-oracle.test.ts | revised | +| packages/db/tests/query/includes-query-shape-oracle.test.ts | revised | +| packages/db/tests/query/includes-space-oracle.test.ts | revised | +| packages/db/tests/query/includes-temporal-oracle.test.ts | revised | +| packages/db/tests/query/includes-work-counter-oracle.test.ts | revised | +| packages/db/tests/query/index-path-collision-oracle.test.ts | revised | +| packages/db/tests/query/load-subset-oracle.property.test.ts | revised | +| packages/db/tests/query/load-subset-replay-refinement-oracle.test.ts | revised | +| packages/db/tests/query/load-subset-source-readiness-refinement-oracle.test.ts | revised | +| packages/db/tests/query/load-subset-transaction-refinement-oracle.test.ts | revised | +| packages/db/tests/query/optimizer-semantics-oracle.test.ts | revised | +| packages/db/tests/query/ordered-lifecycle-oracle.property.test.ts | revised | +| packages/db/tests/query/ordered-work-oracle.property.test.ts | revised | +| packages/db/tests/query/pagination-oracle.property.test.ts | revised | +| packages/db/tests/query/subquery-user-value-oracle.test.ts | revised | +| packages/db/tests/query/virtual-row-fields-oracle.test-d.ts | revised | +| packages/db/tests/query/virtual-row-fields-oracle.test.ts | revised | +| packages/db/tests/utils.property.test.ts | revised | +| packages/electric-db-collection/tests/electric-oracle-lifecycle.test.ts | revised | +| packages/electric-db-collection/tests/electric-oracle.property.test.ts | revised | +| packages/electric-db-collection/tests/electric-recovery-oracle.test.ts | revised | +| packages/electric-db-collection/tests/electric-sdk-delivery.property.test.ts | revised | +| packages/electric-db-collection/tests/pg-serializer.property.test.ts | revised | +| packages/node-db-sqlite-persistence/tests/expression-index-oracle.test.ts | revised | +| packages/offline-transactions/tests/KeyScheduler.property.test.ts | revised | +| packages/offline-transactions/tests/connectivity-replay-oracle.test.ts | revised | +| packages/offline-transactions/tests/fifo-retry.property.test.ts | revised | +| packages/offline-transactions/tests/leadership-replay.property.test.ts | revised | +| packages/offline-transactions/tests/oracle-lifecycle.test.ts | revised | +| packages/offline-transactions/tests/transaction-serializer.property.test.ts | revised | +| packages/offline-transactions/tests/transaction-settlement.property.test.ts | revised | +| packages/powersync-db-collection/tests/correctness-oracle.test.ts | revised | +| packages/query-db-collection/tests/cursor-pagination.boundary-oracle.test.ts | revised | +| packages/query-db-collection/tests/cursor-pagination.cache-oracle.test.ts | revised | +| packages/query-db-collection/tests/cursor-pagination.oracle.test.ts | revised | +| packages/query-db-collection/tests/cursor-pagination.publication-oracle.test.ts | revised | +| packages/query-db-collection/tests/includes-work-counter-oracle.test.ts | revised | +| packages/query-db-collection/tests/load-subset-lifecycle-oracle.test.ts | revised | +| packages/query-db-collection/tests/ownership-lifecycle.oracle.test.ts | retained | +| packages/trailbase-db-collection/tests/lifecycle-oracle.property.test.ts | revised | +| packages/vue-db/tests/useLiveQuery-publication-oracle.test.ts | revised | diff --git a/docs/contributing/oracle-reviews/2026-09-30-nan-ranked-window.md b/docs/contributing/oracle-reviews/2026-09-30-nan-ranked-window.md new file mode 100644 index 0000000000..3e2cf59b30 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-09-30-nan-ranked-window.md @@ -0,0 +1,61 @@ +# NaN ranked-window oracle review + +## Reviewed state and claim + +Base: `bef12e24`, the freshly fetched `origin/main` used to create this branch. +This record reviews the Git tree that contains it. The final commit or pull +request identifies that immutable tree. + +The public live-query guide gives `NaN` PostgreSQL float semantics: it equals +itself and sorts after finite values in ascending order. A descending top-three +window over ranks `10, 20, NaN, 30, 40` therefore exposes keys `[3, 5, 4]`. +The Collection must publish that window without a graph error. This change +repairs equality of copied numeric `NaN` leaves and invalid Date timestamps +inside structural contributor signatures. + +The claim covers eager direct-source ordering at initial publication and the +listed rank updates, deletion, and offset move. It does not establish on-demand +NaN rank acquisition, joined-query signatures, null/NaN combinations, or +typed-array NaN equality. The coverage map assigns those public query gaps to +the pagination owner. + +## RED and GREEN evidence + +On the unchanged base, the primary pagination oracle passed its ascending NaN +and descending finite controls. The descending NaN window and its update +history threw `Query contributors with the same row key are not congruent` in +the real graph at `compiler/index.ts:1282`. These are graph-run failures before +the public row assertion, not wrong-row assertion kills. A separate equality +test returned `false` for copied `{ rank: NaN }` values. + +The final fixed lane includes the original five-row case, a three-row shrink, +two NaN values with a public-key tie, ascending and finite controls, a window +move, and equal, changed, restored, and deleted source ranks. The same public +driver and independent full-sort model compare ordered rows after preload and +each action. A wrong-order control sends the correct three members in the wrong +order to the same row comparison and gets an assertion failure. + +The initial focused GREEN run passed 314 tests across the pagination and +structural-equality suites. Final verification receipts belong in the pull +request because later edits can change their exact boundary. + +## ORC-001 through ORC-012 + +| Requirement | Outcome | +| --- | --- | +| ORC-001 Contract authority and limits | Pass. The live-query guide defines NaN ordering and equality. The pagination oracle and coverage map state the bounded path and omitted cases. | +| ORC-002 Independent judgment | Pass. The model sorts copied source rows with `Number.isNaN`, relational comparison, and numeric public keys. It does not call the production comparator or structural equality helper. | +| ORC-003 Distinguishable responsibilities | Pass. The pagination file states the law, full-sort model, fixed ranks and actions, public Collection driver, and ordered-row checks at preload and action checkpoints. | +| ORC-004 Generated-history controls | Not triggered by this fixed lane. Existing generated integer histories and their grammar are unchanged. | +| ORC-005 Production path and observation | Pass. `createLiveQueryCollection` compiles a real eager `orderBy(..., 'desc').limit(3)` query. The driver reads complete public rows after preload and after each action. The original graph error is also observed. | +| ORC-006 Checker calibration | Pass. The original implementation fails before the row checkpoint and is classified as a graph-run failure. The wrong-order control fails the same row comparison by assertion. | +| ORC-007 Fixed/random replay | Not triggered by the new fixed lane. The existing generated campaigns retain their seed, replay, and budget. | +| ORC-008 Stateful-model minimality | Not triggered. The new lane uses the existing source-row map and stateless full recomputation. It adds no model state. | +| ORC-009 Vocabulary mapping | Pass. Source rows, public rows, window, graph run, and publication follow the project glossary. The model adds no lifecycle abstraction. | +| ORC-010 Failure fidelity and cleanup | Pass. `withHistoryCleanup` releases the source when graph construction fails and preserves a primary failure if cleanup also fails. It releases both Collections after successful construction. | +| ORC-011 Independent second formulation | The literal key vector `[3, 5, 4]` checks the full-sort model. Ascending, finite, and duplicate-NaN cases challenge the rank rule without copying production's comparison code. A separate backend formulation is outside this eager Collection claim. | +| ORC-012 Review evidence | This base-identified record gives each outcome and the bounded claim. The coverage map links it and names the remaining public paths. | + +The bounded repair covers the NaN ordering law × the listed eager source-row +histories × direct top-K compilation × public ordered rows after preload or a +source action. It does not claim every legal NaN query history. diff --git a/docs/contributing/oracle-reviews/2026-09-30-offline-deletion-settlement.md b/docs/contributing/oracle-reviews/2026-09-30-offline-deletion-settlement.md new file mode 100644 index 0000000000..e9d1a4edca --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-09-30-offline-deletion-settlement.md @@ -0,0 +1,75 @@ +# Offline outbox deletion settlement: bounded follow-up + +Executable revision: `9b6b8316fcad08be37c8ddca51dd66096b3a80b1` +Guide revision: `a1d75726` +Prior review: [Oracle guide portfolio follow-up](2026-09-30-guide-portfolio-followup.md). + +This append-only record resolves the prior offline settlement authority conflict. +The maintainer chose outbox deletion as the success boundary. The +[README](https://github.com/TanStack/db/blob/9b6b8316fcad08be37c8ddca51dd66096b3a80b1/packages/offline-transactions/README.md) promises that the +configured `mutationFn` returns and the storage adapter acknowledges deletion +before `commit()`, `isPersisted.promise`, or the per-ID waiter fulfills. A +provider effect is confirmed only to the extent that `mutationFn` waits for it. +The prior review remains an accurate record of its earlier executable revision. + +## Contract, witnesses, and limits + +| Dimension | Evidence at the executable revision | +| --- | --- | +| Contract | Caller success follows `mutationFn` fulfillment and acknowledged outbox deletion. A durable `deletion-pending` phase records provider fulfillment before deletion is attempted. | +| Legal histories | Held deletion; failed deletion followed by in-process retry with a queued peer; failed phase write followed by in-process retry; failed deletion followed by restart and `beforeRetry: () => []`; old unmarked row replay; permanent provider failure with failed rejection cleanup. | +| Production paths | `OfflineTransaction.commit()` through `OfflineExecutor`, `TransactionExecutor`, `OutboxManager`, serializer, and storage adapters. The restart witness creates a second executor over the same fake storage. | +| Public observations and checkpoints | Exact `commit()` and `isPersisted` states, per-ID waiter, provider call IDs and idempotency key, queued-peer order, local transaction state, provider-applied rows, and outbox contents before and after deletion acknowledgement. | +| Wrong designs rejected | The original early-fulfillment implementation fails the failed-delete and restart tests at the pending-state checkpoint. Swallowed adapter deletion errors fail the localStorage and IndexedDB tests. Recalling the provider for a marked row fails the restart call ledger; allowing a peer past the retained FIFO head fails the queued-peer ledger. | + +The failed-deletion and restart tests were RED before the executor repair: +`commit()` and `isPersisted.promise` fulfilled while the row remained. The two +adapter failure tests were RED before error propagation was repaired. The +permanent-provider-failure branch remains separate: its caller rejects with the +provider error even if removing the rejected row also fails. + +The phase write may itself fail after the provider succeeds. The current +executor retains the phase in memory, keeps the caller pending, and retries the +write before deletion without repeating the provider call. A crash before the +phase is durably written leaves an unmarked row, which can replay. The provider +must honor the supplied idempotency key to avoid a repeated effect. Older app +versions may also replay a marked row. Storage-adapter acknowledgement does not +prove physical power-loss durability. No exactly-once provider claim is made. + +## Guide review + +| Requirement | Outcome | +| --- | --- | +| ORC-001 authority and limits | **Met for this boundary.** The README and maintainer decision agree on deletion-gated success; the adapter, provider, crash, and old-reader limits are explicit. | +| ORC-002 independent judgment | **Met for this boundary.** Expected caller and outbox states come from the README law and authored provider/storage gates, not executor phase logic. | +| ORC-003 visible responsibilities | **Met.** The settlement oracle's opening contract, prefix model, generated grammar, production driver, and public refinement checks remain directly visible; fixed deletion histories extend that owner. | +| ORC-004 generated grammar controls | **Bounded, unchanged.** The existing generated prefix grammar retains its reconstruction, ablation, range, and exclusion controls. This follow-up adds fixed lifecycle histories and claims no generated deletion grammar. | +| ORC-005 production path and observation | **Met at the controlled boundary.** The tests reach the real executor and compare caller settlement, outbox state, and provider calls at held and completed deletion cuts. | +| ORC-006 checker calibration | **Met.** The original early-success code and swallowed adapter errors produced assertion failures at the intended checkpoints before the fixes. | +| ORC-007 fixed/random campaigns and replay | **Partial for the existing generated property.** The package runs paired fixed and seedless campaigns with the same property and budget, and the file accepts seed/path replay. A captured failing settlement seed and shrink path has not been directly replayed at this revision. The fixed deletion histories do not trigger this rule by themselves. | +| ORC-008 stateful-model minimality | **No new trigger.** No reference-model state was introduced, removed, combined, or split; `deletion-pending` is production outbox state exercised by fixed histories. | +| ORC-009 vocabulary mapping | **Met.** The phase is explicitly an outbox row state after provider fulfillment; `settlement`, `provider`, and `optimistic transaction` retain their glossary meanings. | +| ORC-010 failure fidelity and cleanup | **Met for the new witnesses.** Gates release in `finally`, and `cleanupOfflineOracle` preserves a primary assertion separately from cleanup failures. An unbounded storage operation that never settles is not covered. | +| ORC-011 independent second formulation | **No new trigger.** No plausible shared semantic fault requiring a second model was named; adapter tests independently check that deletion errors cross the storage boundary. | +| ORC-012 review evidence | **Recorded with the ORC-007 gap.** This record ties every applicable outcome, RED/GREEN witness, and remaining cell to the exact executable revision. | +| ORC-013 distinguishing boundary witness | **Met.** Held deletion versus completed deletion and failed deletion versus successful retry distinguish early settlement; marked restart versus unmarked replay distinguishes provider replay policy. | +| ORC-014 controlled-premise handoff | **Bounded.** Fake-storage failure is received by localStorage `removeItem` and IndexedDB write-store failure tests. Native browser restart, multi-owner handoff, real provider idempotency, and power-loss durability remain unproved. | + +## Verification and code weight + +At the executable revision, the offline package suite passed **229/229 tests +across 17 files**. Package typecheck, source lint, build, Prettier, and Git diff +checks passed. Lint on touched test files exited 0 with three pre-existing +`require-await` warnings in `OfflineExecutor.test.ts` and no errors. + +The repair adds a net **38 production lines** for the persisted phase, +deletion-gated settlement, retry, and adapter error propagation. Tests add a net +**818 lines** and contract/coverage documents add **32 lines**. It uses the +existing FIFO scheduler and retry timer; it does not add a second lifecycle +machine. The large test addition records separate held, failed, retry, restart, +and legacy checkpoints with explicit cleanup. + +The [coverage map](../oracle-coverage.md) owns the remaining crash-window, +old-reader, native restart, multi-owner, real-provider idempotency, and captured +settlement replay witnesses. A reachable replay effect in those cells prevents +an exactly-once claim. diff --git a/docs/contributing/oracle-reviews/2026-09-30-offline-fail-stop-followup.md b/docs/contributing/oracle-reviews/2026-09-30-offline-fail-stop-followup.md new file mode 100644 index 0000000000..f4ef9e9a07 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-09-30-offline-fail-stop-followup.md @@ -0,0 +1,36 @@ +# Offline outbox storage failure: fail-stop follow-up + +Executable revision: `d3d00a67fbc9509cde632578a9e36241caceb4e2` +Prior review: [Offline outbox deletion settlement](2026-09-30-offline-deletion-settlement.md). +Review inputs: the maintainer's four findings and test-gap note for PR #1968, plus CodeRabbit review 5373142655 at `400d8d86970b2cc6368b30a68554bbdcad734bfd`. + +The maintainer decided that an outbox deletion failure means storage is seriously broken: stop the current executor and throw the storage error. This deliberately revises the earlier in-process deletion-retry law recorded at the prior executable revision. A phase-write failure takes the same fail-stop path because it prevents a durable record of the provider outcome. + +## Contract and evidence + +| Boundary | Executable witness at this revision | +| --- | --- | +| Successful provider, failed delete | `transaction-settlement.property.test.ts` holds a provider and queues a peer. After delete fails, `commit()` and `isPersisted.promise` reject with the exact storage error, the local transaction fails, the peer stays pending, and the head's durable `deletion-pending` marker remains. An online notification causes no second deletion or peer provider call. A later admission rejects before its outbox write. | +| Permanent provider failure, failed delete | The direct executor witness rejects `execute()` with the exact storage error, rejects the transaction waiter with the original `NonRetriableError`, and retains both FIFO entries. Another `executeAll()` on the stopped executor rejects with the same storage error even after storage recovers. | +| Failed phase write | The public witness rejects the head caller with the marker-write error, does not delete or retry, and leaves the queued peer untouched. The durable row is unmarked; a crash can still replay provider work. | +| Restart after a durable marker | Separate successful and permanently rejected provider histories construct a fresh executor over the same storage. It removes the marked row without another provider call; the rejection path does not restore optimistic state. The successful path also checks that restart does not rewrite an already durable marker. | +| Timer and leadership boundary | The leadership witness injects one failed terminal deletion, advances fake time by 60 seconds, and observes no automatic retry or later provider call. | + +The two direct stop-and-throw witnesses were RED against the uncommitted retrying candidate: both `execute()` calls resolved when they had to reject. They are GREEN at this revision. The full offline suite passed **237/237 tests across 18 files**; package typecheck, build, Prettier, and edited-file lint passed with no lint errors. The five `require-await` warnings in the leadership test predate this change. + +The production diff is net **+62 lines**: a durable rejection marker, one shared cleanup boundary, a stopped-executor error latch, and a pre-admission check. Tests are net **+208 lines** and contract documents net **+28 lines**. The latch does not schedule a recovery lifecycle. Automatic marked cleanup resumes only in a fresh executor after storage recovers. + +## Review disposition + +| Item | Disposition at this executable revision | +| --- | --- | +| R1: rejection-path delete error escaped before the handled-provider-error flag | Fixed by explicit storage-error propagation after recording the terminal marker. The batch stops; the affected waiter retains its original provider error. | +| R2: constant deletion retry delay and FIFO head blocking | The constant-delay defect was real at the reviewed revision. The maintainer chose fail-stop, so no deletion retry or backoff remains. Queued peers stay untouched. | +| R3: failed terminal deletion could replay `mutationFn` after restart | Fixed by durable `rejection-pending`. Both provider-call count and optimistic restoration are checked. | +| R4: repeated marker writes on deletion retry | A marked row on restart now goes directly to removal. The original unconditional skip was unsafe after a failed first marker write; this revision stops on that failure. | +| R5: missing terminal-failure deletion dimension | Added direct batch, public caller, FIFO timer, and same-storage restart witnesses to the settlement and leadership owners. | +| CodeRabbit CR5: backoff and `lastError` suggestion | Overlaps R2. Backoff is superseded by fail-stop; overwriting `lastError` with the deletion error would erase the original provider failure needed for terminal replay. | + +## Limits and open work + +Fake storage controls exact failure and restart order. Native browser restart, multi-owner handoff, physical power-loss durability, and real provider idempotency remain outside these witnesses. A failed phase write leaves an unmarked replay window; the provider must honor the supplied idempotency key. Already admitted queued callers remain pending when this executor stops. Their eventual disposition after a fresh executor starts is a separate lifecycle observation, owned by the settlement oracle and the broader offline policy in [RFC #1659](https://github.com/TanStack/db/issues/1659). The earlier review record remains an accurate description of its own revision and is not edited. diff --git a/docs/contributing/oracle-reviews/2026-09-30-prep-pr-followup.md b/docs/contributing/oracle-reviews/2026-09-30-prep-pr-followup.md new file mode 100644 index 0000000000..7d1c668f4e --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-09-30-prep-pr-followup.md @@ -0,0 +1,38 @@ +# Oracle audit PR preparation: review follow-up + +Executable revision: `cc25b383` (merge of current `origin/main` after review repairs). +Guide revision: `a1d75726`. +Earlier evidence: [portfolio follow-up](2026-09-30-guide-portfolio-followup.md) and +[offline deletion settlement](2026-09-30-offline-deletion-settlement.md). + +This append-only record covers four findings from the PR preparation review. +It adds evidence to the earlier records without changing their verdicts at +their reviewed revisions. The QueryRef product counterexamples remain open. + +| Finding | Missing oracle evidence | Repair and distinguishing check | +| --- | --- | --- | +| Active manual outbox removal could strand a caller | Settlement histories held storage deletion, but did not remove the row while the provider was still active. | Two fixed histories call `removeFromOutbox` and `clearOutbox` while the provider is held. Both keep `commit()` and `isPersisted` pending until the provider returns, then check the public transaction state, provider ledger, and empty outbox. The witnesses failed on the pre-repair executor and pass at this revision. | +| Equal-time mixed-phase restart could reverse FIFO order | Replay histories did not put a `deletion-pending` row before an unmarked peer with the same timestamp. | A fixed restart history checks storage insertion order, `beforeRetry` input, deletion of the first row, and the peer's later provider call. It failed on the pre-repair mixed-phase sorting path and passes at this revision. | +| Named pagination and index oracle replay ran unrelated campaigns first | The replay wrapper checked the target result, but not direct selection of the requested lane. | The guarded replay suite checks each named lane's execution count and skipped campaigns. An unrelated failing fixed campaign must not run before the selected replay. The pre-repair path failed that control; the guarded suite passes 35/35. | +| Includes rollback could appear green without a callback reach check | Final rows alone could miss a transient callback publication, and an empty callback loop could pass. | The includes publication oracle requires callback reach and compares the rollback callback trace with the independent public-row model. Its focused suite passes 93/93. | + +The controlled offline histories do not prove physical power-loss durability, +native browser restart, independent storage writers, or provider idempotency. +A crash after provider completion but before the `deletion-pending` write can +replay an unmarked row. The provider must honor the supplied idempotency key. +Manual removal after a separate provider failure and multi-owner handoff need +their own witnesses before either behavior gains broader coverage. The +[coverage map](../oracle-coverage.md) retains those limits and the open QueryRef +counterexamples. + +At this executable revision, the offline package passed 235 tests across 18 +files, typecheck, source lint, and build. The includes publication and guarded +replay suites passed 128 tests together with no type errors. The merged SQLite +persisted-readiness owner passed 35 runtime tests and 27 type checks. The +earlier browser SQLite and Query DB Collection campaigns remain evidence at +their recorded revisions, not new post-merge results. + +The branch's production diff against `origin/main` is confined to the offline +package and adds a net 48 lines. Tests and contract records carry the larger +change. No universal oracle conformance or exactly-once provider execution is +claimed. diff --git a/docs/contributing/oracle-reviews/2026-09-30-queryref-publication-repair.md b/docs/contributing/oracle-reviews/2026-09-30-queryref-publication-repair.md new file mode 100644 index 0000000000..a57796c11f --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-09-30-queryref-publication-repair.md @@ -0,0 +1,37 @@ +# QueryRef publication repair + +Pre-repair executable revision: `536708458` on PR #1968. +Repair executable revision: `a5fb460d`. +Guide revision: `a1d75726`. +Earlier verdicts remain at their recorded revisions in the +[portfolio follow-up](2026-09-30-guide-portfolio-followup.md) and +[PR preparation follow-up](2026-09-30-prep-pr-followup.md). + +Two public laws were promoted from exact expected-failure guards to ordinary +assertions. Before production edits, the focused run failed two matching +optimizer aggregate cases, one flat aggregate join, and the initial +two-candidate joined `findOne()` case. The nonmatching and rejecting aggregate +controls passed. After the repair, the two owners pass 35 tests with no type +errors. + +| Law | Pre-repair gap | Repair and distinguishing witness | +| --- | --- | --- | +| A computed aggregate projection must join by its published value. | `followRef` treated selected `total` as a source Collection field, so lazy acquisition sought a field that the source rows did not contain. A matching global or flat aggregate QueryRef published no joined row. | Computed projections no longer resolve to a source field. The optimizer owner crosses matching/nonmatching totals with absent, accepting, and rejecting outer predicates and retains a separately materialized aggregate control. The QueryRef owner checks the matching flat aggregate at preload, after the count grows, after a new matching person arrives, and after the count shrinks. | +| A `findOne()` QueryRef must reduce to one candidate before its outer join. | `singleResult` changed the receiving result shape but did not limit the nested stream. Two initial joined candidates reached the outer query. | The compiler applies a one-row top-K inside the QueryRef, and lazy acquisition treats `singleResult` as limited. An ordered joined history checks the first, replacement, empty, restored, and changed-join-key cuts. An unordered history checks both QueryRef positions and key-ordered replacement after deletion and reinsertion. A materialized child control checks another receiving boundary. | + +The pre-repair failures provide hostile controls against the original wrong +designs. The unordered history additionally rejects an ordered-only repair; +source updates reject a repair that changes only initial publication. These +controlled Collection histories cover the stated finite rows, join shapes, +and synchronous public checkpoints. They do not prove all `singleResult` +forms, join forms, provider schedules, or unbounded histories. The +[coverage map](../oracle-coverage.md) assigns those remaining limits to the +QueryRef and optimizer owners. + +At the repair revision, the DB oracle campaign passed 3,712 tests across 55 +files with no type errors. The DB build, edited-file lint, formatting, and +documentation link check passed. The full DB suite passed all 7,861 runtime +tests across 225 files, then exited with four type-check errors in the +unchanged `tests/query/subset-error-matrix.test.ts` fixture. Those type errors +are outside the QueryRef owners and are not included in a green full-suite +claim. diff --git a/docs/contributing/oracle-reviews/2026-09-30-where-predicate-and-join-keys.md b/docs/contributing/oracle-reviews/2026-09-30-where-predicate-and-join-keys.md new file mode 100644 index 0000000000..4fac3e0477 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-09-30-where-predicate-and-join-keys.md @@ -0,0 +1,104 @@ +# WHERE predicate publication and joined result key oracle review + +## Reviewed state and claim + +Base: `49dc79d4`, the head of pull request #1956 before this review. This +record reviews the Git tree that contains it. The final commit or pull request +identifies that immutable tree. The review applied the ORC-001 through ORC-014 +requirements of the oracle guide revision that adds ORC-013 and ORC-014. + +The WHERE predicate publication oracle claims that a filtered live-query +Collection, direct subscriptions with and without initial state, peer +subscriptions routed by one `eq` field, and `currentStateAsChanges` publish +exactly the rows whose predicate is TRUE under SQL three-valued logic. The +claim covers the grammar in the oracle's opening prose. It does not cover +comparison operators other than `eq`, joins, ordering, optimistic updates or +deletes, truncate, failed replay, or generated cleanup and restart histories. + +The joined result key oracle claims that an inner, left, or full two-source +join publishes one row per joined pair and that distinct pairs never share a +result key, for string keys, numeric keys, both infinities, and `NaN`. It does +not cover joins over subqueries, more than two sources, custom `getKey`, or +optimistic mutations. + +Both oracles use `mockSyncCollectionOptions` as a controlled provider. Their +claims are limited to how the Collection and compiler handle the sync +transactions that provider supplies; neither claims a real adapter's behavior. + +## RED and GREEN evidence + +Mutants ran on the reviewed tree through the oracle files alone. Each file was +restored after each run. + +| Mutant | Oracle | Outcome | +| --- | --- | --- | +| `eq` returns FALSE for a nullish operand | WHERE | Assertion failure in the pinned snapshot tests and both campaigns (5 tests). | +| Routing ignores a change's previous value | WHERE | Assertion failure in the change pinned tests and both campaigns (4 tests). | +| Routing continues while stale published rows await reconciliation | WHERE | Assertion failure in the restarted-source witness. | +| Routing treats `or` operands as conjuncts | WHERE | Assertion failure in the change pinned tests and a campaign (2 tests). | +| A dropped insert or update is recorded as sent | WHERE | Survived: equivalent in this oracle's domain. Routing withholds a dropped row from an `eq` subscription before any key is recorded, and unrouted predicates never skip the delete that would expose a stale record. | +| The same sent-key mutant | `collection-subscription.test.ts` page-offset witness | Survived the pre-review `eq` witness. After the review added an unrouted `or` predicate, the two cases beside a matching change fail by assertion (offset 3, expected 2). The unrouted `alone` case survives. | +| Result key joins the source keys with a comma | Join | Key-count invariant error or wrong-row assertion in both pinned delimiter and number histories and both campaigns (4 tests). | +| Result key uses plain `JSON.stringify` | Join | Wrong-row assertion in the pinned infinity history and both campaigns (3 tests). | +| Index compares source-key prefixes with `===` | Join | Key-count invariant error in the pinned `NaN` history and the fixed campaign (2 tests). | +| The same prefix comparison | Index refinement oracle | Assertion failure in three pinned `NaN` histories and both campaigns; the `-0` control passes. | + +The plain-JSON mutant is the pre-review implementation. The review found that +`JSON.stringify` prints `Infinity` and `-Infinity` as `null`, the marker for a +missing outer side. Adding both infinities to the key domain failed the fixed +and random campaigns with a full join over `-Infinity` on both sides. The repair +encodes a non-finite number as an object, which no source key can be. `NaN` +source keys failed before key encoding mattered, with the comma encoding as +well, because the join index compared source-key prefixes with `===`, so a +retracted `NaN`-keyed row never cancelled. The review fixed that comparison in +`@tanstack/db-ivm` and added an Index refinement oracle for it; the join oracle +now includes `NaN` keys and a pinned history in which a `NaN`-keyed pair leaves +and re-forms. + +After the repairs, `test:oracles` at `TANSTACK_DB_ORACLE_RUNS_MULTIPLIER=10` +passed 2,869 tests and the `@tanstack/db` suite passed 7,499 tests. Final +verification receipts belong in the pull request. + +## WHERE predicate publication oracle + +| Requirement | Outcome | +| --- | --- | +| ORC-001 Contract authority and limits | Pass. The evaluator contract in `src/query/compiler/evaluators.ts` states the operand rules. The opening prose lists the omissions and their owners. | +| ORC-002 Independent judgment | Pass. `expectedTruth` and `expectedVisible` evaluate plain objects without production comparison, normalization, prefilter, routing, or virtual-field helpers. | +| ORC-003 Distinguishable responsibilities | Pass. The contract, model, history grammar, production driver, and refinement check are separate marked sections. | +| ORC-004 Generated-history controls | Pass after repair. Reconstruction: the pinned snapshot with `eq($synced, false)` was outside the grammar because `false` was not a literal; the review added it, and every pinned history is now in the grammar. Ablation: removing `not` loses the nullish-comparison history, removing `or` loses the routing history, removing multi-operation transactions loses retraction inside one batch, removing insert reuse loses reinsertion, and removing the index axis loses the index path. Range: depth three, at most six rows, six transactions of three operations, and marginal values `NaN`, a valid Date, the normalization prefix, `null`, and a missing field. Exclusion: an update to an equivalent value and a second update to one key in one transaction are removed from the history. | +| ORC-005 Production path and observation | Pass. Public builder functions, `subscribeChanges`, and `currentStateAsChanges` run against a real Collection. Each consumer's exact key set is compared after the subscriptions attach and after each sync transaction commits. | +| ORC-006 Checker calibration | Pass. The checker test requires the two-valued answer to fail. The mutant table classifies each run. | +| ORC-007 Fixed/random replay | Pass. The fixed seed `44_500_301`, the unseeded campaign, and the replay entry share one property and budget. | +| ORC-008 Stateful-model minimality | Pass. The model keeps a row map across sync transactions. `$synced` and `$origin` separate a pending optimistic insert from a synced row, and the pinned `eq($synced, false)` snapshot distinguishes them. `$key` always equals the row id; it stays because the grammar reads it as an operand. The touched-key set distinguishes a subscription without initial state from one with it. | +| ORC-009 Vocabulary mapping | Pass after repair. The prose now uses live-query Collection and sync transaction. A subscriber is the callback of one subscription; a consumer is the oracle's label for one observed endpoint. | +| ORC-010 Failure fidelity and cleanup | Pass after repair. Cleanup ran in `finally`, so a cleanup failure replaced the check failure. `withOracleCleanup` now runs every cleanup step and keeps the check failure as the `cause` of an `AggregateError` when cleanup also fails. | +| ORC-011 Independent second formulation | Not triggered. No review has named a fault that the Kleene model and production could share. | +| ORC-012 Review evidence | This record. The coverage map links it. | +| ORC-013 Reusable boundary law | Pass. FALSE-for-UNKNOWN is rejected by the negated nullish snapshot. Ignoring the previous value is rejected by generated change histories. Treating `or` operands as conjuncts is rejected by the pinned `or` change history. Routing past stale rows is rejected by the restarted-source witness. | +| ORC-014 Controlled-premise handoff | Not triggered. The claim is limited to the controlled provider's sync transactions. | + +## Joined result key oracle + +| Requirement | Outcome | +| --- | --- | +| ORC-001 Contract authority and limits | Pass after repair. The prose now cites the live-query guide: joins behave like SQL joins, and a join result has a composite key of the parent keys. The guide does not fix the key format. | +| ORC-002 Independent judgment | Pass. `expectedPairs` is a nested loop over plain arrays and does not import the compiler or its key encoding. | +| ORC-003 Distinguishable responsibilities | Pass. The contract, model, history grammar, production driver, and refinement check are separate marked sections. | +| ORC-004 Generated-history controls | Pass after repair. Reconstruction: the pinned number history used right key `x`, which was outside the key domain; it now uses `c`, and all three pinned histories are in the grammar. Ablation: removing delimiter strings, number and string twins, or infinities loses a collision class; removing full joins loses unmatched rows on the right; removing synced changes loses unmatched rows created by a group move. Range: at most five rows a side, groups 0 through 2, and three changes. Exclusion: keys are unique within one side, and a change that keeps a row's group is skipped. | +| ORC-005 Production path and observation | Pass. `createLiveQueryCollection` compiles a real join. Published pairs and the key count are compared after preload and after each synced change. | +| ORC-006 Checker calibration | Pass. The comma and plain-JSON mutants fail, as classified above. | +| ORC-007 Fixed/random replay | Pass. The fixed seed `44_501_962`, the unseeded campaign, and the replay entry share one property and budget. | +| ORC-008 Stateful-model minimality | Not triggered. The model recomputes the pairs from the current rows. | +| ORC-009 Vocabulary mapping | Pass. A pair is one published row of the live-query Collection, described by its two source keys. | +| ORC-010 Failure fidelity and cleanup | Pass after repair, through `withOracleCleanup`. | +| ORC-011 Independent second formulation | Not triggered. No shared-fault hypothesis has been named. | +| ORC-012 Review evidence | This record. The coverage map links it. | +| ORC-013 Reusable boundary law | Pass. The law is that distinct pairs have distinct keys and a retracted pair cancels. The comma encoding is rejected by the delimiter and number pinned histories, plain JSON by the infinity pinned history, and `===` prefixes by the `NaN` pinned history. | +| ORC-014 Controlled-premise handoff | Not triggered. The claim is limited to how the compiler keys rows from the controlled provider. | + +## Open work + +- The sent-key mutant survives the unrouted `alone` page-offset case. +- Generated cleanup and restart histories for filtered subscriptions remain + with the lifecycle publication owner, as the coverage map records. diff --git a/docs/contributing/oracle-reviews/2026-10-01-handler-source-batch.md b/docs/contributing/oracle-reviews/2026-10-01-handler-source-batch.md new file mode 100644 index 0000000000..d66acd47fc --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-01-handler-source-batch.md @@ -0,0 +1,95 @@ +# Source batches written inside a mutation handler + +Pre-repair executable revision: `34095aa6d` (oracle extension on `f2f92c5b2`). +Repair executable revision: `3ab428439`. + +## Law + +A direct `insert`, `update`, or `delete` hands its request to the +Collection's handler. The handler may confirm the request through sync before +it returns. That source batch is the same event as a batch written while the +new request is active: it waits for settlement unless it is immediate or a +truncate, and ordinary publication then retires the accepted snapshot. +Authority: the optimistic-history contract in +`packages/db/tests/optimistic-history-oracle.ts` ("After success it remains +as an accepted local snapshot until source acknowledgement retires it") and +the virtual-property definitions in `docs/guides/live-queries.md`. + +## Gap + +The direct paths in `packages/db/src/collection/mutations.ts` committed the +transaction before they registered it. `commit()` runs the handler +synchronously up to its first `await`, so a synchronous confirmation found no +persisting request and applied at once. It acknowledged nothing. Registration +then installed the optimistic overlay over the confirmed row, and nothing +retired it: + +- the row kept its optimistic value with `$hasPendingWrites: true` after the + transaction completed; +- the next remote write to the key reported `$origin: 'local'`. + +The oracle missed it because its grammar wrote every source batch after the +edit call returned. No generated history reached the handler's synchronous +body. + +The gap was found while classifying survivors of a code-weight mutant run on +`state.ts`. A probe for mutant B4 showed `main` violating the documented +`$hasPendingWrites` contract in this history. + +## Repair and witnesses + +- The grammar lets `edit` and `delete` steps carry a source batch that the + shared handler writes before it returns, about one step in four. The model + applies that batch with `sync` right after `author`, and both views are + allowed publication cuts. The driver asserts that the handler wrote the + batch. +- Six pinned replays confirm an insert, an update, and a delete inside their + own handlers, immediate and not, then settle and receive a later remote + write. +- A sampling witness requires the fixed campaign (seed `86103`) to generate + in-handler batches for edits and deletes, immediate and not, with rows. +- Each direct path now registers the transaction, schedules its cleanup, and + recomputes optimistic state before it commits. The production change is net + −2 lines. + +| Run | Result | +| --- | --- | +| Owner on `34095aa6d` (oracle only) | 7 failed of 95: 5 of 6 pinned cases and both campaigns. The first failure is the batch publishing the server row as synced before the overlay. | +| Owner on `3ab428439` | 95 passed | +| Mutant: one path commits before it registers (insert, update, delete) | assertion failure: 4, 4, and 3 of 95 | +| Mutant: one path registers, commits, then recomputes | assertion failure: 2 of 95 on each path | + +The immediate delete case also passed before the repair: an immediate delete +batch applies at once either way, and the delete leaves no overlay to retire. + +## Limits + +- The handler writes one batch, synchronously, before any `await`. A batch + written after an `await` was already in the grammar as an ordinary sync + step. +- Transactions from `createTransaction` and `createOptimisticAction` register + through `mutate()` before the caller commits, so they never had this gap. + This change adds no witness for them. +- Local-only collections reach the repaired path: their wrapper confirms + synchronously when no user handler is given. Their rows never report + pending writes, so the old order had no visible effect there. + +## Verification + +On `3ab428439`, with the built `dist`: + +- `packages/db` Vitest, typecheck off: 200 files, 7,857 tests. + `tsc --noEmit`: no errors. +- `pnpm --filter @tanstack/db test:dist` (290 tests) and + `pnpm test:minified-db` pass. +- Package unit tests: `query-db-collection` 558, `electric-db-collection` 629, + `offline-transactions` 73. + +## Note added 2026-10-03 + +The settlement contract changed after this record. A transaction's optimistic +state now drops when its mutation function settles. A source batch written +inside the handler is held and publishes with that drop, and it is +`$origin: 'local'` because it was committed while the transaction persisted. +A write committed after settlement is `'remote'`. The evidence above is +historical. diff --git a/docs/contributing/oracle-reviews/2026-10-01-pooled-live-queries.md b/docs/contributing/oracle-reviews/2026-10-01-pooled-live-queries.md new file mode 100644 index 0000000000..ebbfc6117c --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-01-pooled-live-queries.md @@ -0,0 +1,169 @@ +# Pooled live query and flat change tracking oracle review + +## Reviewed state and claim + +Base: `84b828c7e`, merged with `origin/main` at `18abceee4`. This record reviews +the Git tree that contains it; the final commit or pull request identifies that +tree. + +The pooled live query oracle claims that a live query filtered only by +`eq(field, literal)` on one eager, non-persisted source, served from an +equality partition, publishes what its live-query Collection would: the same +rows in key order, values, and status. That holds through sync transactions, +optimistic writes confirmed or rolled back, peer mounts and unmounts, and +source cleanup and restart. The claim excludes on-demand and persisted +sources, `DbClient` hydration, Suspense, and every clause beyond `eq` +conjuncts, which keep the live-query Collection. + +The flat change tracking oracle claims that, for a row whose own fields are +all primitives or functions with a plain or null prototype and no symbol +keys, the flat tracker reports the same change set as the draft proxy and an +independent model, with `0` and `-0` equal. Nested values, Dates, Maps, Sets, +class instances, and symbol keys are outside it, except that they must fall +back to the proxy. + +Both oracles use `mockSyncCollectionOptions` or plain rows; neither claims a +real sync adapter's behavior. + +## RED and GREEN evidence + +Mutants ran through each oracle file alone on the reviewed tree. Each file was +restored after each run. Every outcome below is an assertion failure. + +| Mutant | Oracle | Tests failed | +| --- | --- | --- | +| Partition ignores a row's previous group | Pooled | 6 of 9 | +| Groups keep rows in arrival order | Pooled | 3 of 9 | +| Literals and fields compared without `eq` normalization | Pooled | 4 of 9 | +| Partition skips the source's initial state | Pooled | 8 of 9 | +| View reports the source's status after cleanup | Pooled | 4 of 9 | +| Partition never terminates on cleanup | Pooled | 4 of 9 | +| Partition keys groups by the first field only | Pooled | 5 of 9 | +| Update published as delete then insert | Pooled | 3 of 9 (0 of 7 before payload checks) | +| Update carries a stale `previousValue` | Pooled | 3 of 9 (0 of 7 before) | +| Update carries the old row as its value | Pooled | 3 of 9 (0 of 7 before) | +| Within-group updates dropped | Pooled | 3 of 9 | +| Partition built on a cleaned-up source starts terminal | Pooled | 3 of 9 (0 of 7 before) | +| Frozen peers drop pending optimistic rows | Pooled | 3 of 9 (0 of 7 before) | +| Flat diff with `!==` | Flat | 3 of 17 | +| Flat diff with `Object.is` alone | Flat | 3 of 17 | +| Flat diff without deletions | Flat | 5 of 17 | +| Draft proxy without the added-field revert fix | Flat | pinned history and both campaigns | +| Flat diff ignores whether the row owns a field | Flat | 4 of 17 | +| Flat drafts edit the rows in place | Flat | 9 of 17 | +| Assigned objects not detached | Flat | 1 of 17 (detach witness) | +| Frozen rows sent to the proxy | Flat | 3 of 17 | +| Proxy ignores accessors defined in the callback | Flat | 3 of 17 | +| Proxy reports a field defined back to its own value | Flat | 4 of 17 | +| Proxy accepts a getter-only field's own value | Flat | 3 of 17 | +| Proxy reports a written hidden field's delete | Flat | 3 of 17 | +| Flat compares a hidden object field by identity | Flat | 3 of 17 | + +Every pooled and flat mutant above fails its pinned history and both +campaigns. The pooled grammar delivers initial rows in key order or in +reverse and weights a pending insert most peers see before cleanup; the flat +grammar weights runs that write and delete a hidden field, write an equal +object over a hidden object field, assign a getter-only field its own value, and write the opposite-signed zero +over each zero field. Before that weighting, arrival order and frozen optimistic rows +escaped about one random campaign in three, and the three descriptor +mutants escaped both campaigns. The `Object.is`-alone flat mutant escaped about one random campaign in two +until the zero-flip run. + +The pooled oracle found that a pooled view followed its source's status after +cleanup instead of entering the live query's terminal error; the repair makes +the partition terminate. The flat oracle found that the draft proxy dropped a +field added as `undefined` when another field reverted, on `main` as well; the +repair treats a field the original lacks as changed. The grammar now weights +that run, so both campaigns also kill the unrepaired proxy. + +The loss audit then widened both oracles. The pooled granular observer had +been checked by key membership only, so the payload and lifecycle mutants +marked "0 of 7 before" survived it. The flat grammar gained frozen rows, +non-enumerable fields, accessor and data `defineProperty`, stored drafts, and +throwing callbacks. Four shapes split the trackers, three of them on `main`'s +proxy too. The adopted rule is that defining a field acts as assigning it, +and that a non-enumerable field is row data only once the callback writes +it. The proxy now records accessors and data defines through the assignment +path, rejects a getter-only write of its own value, and ignores the delete of +a hidden field it wrote. The flat tracker now compares a hidden object field +by contents. + +Local-only direct writes are compared with a local-only Collection whose +handlers resolve, across mixed multi-key batches, failing batches, schema +rejection, and every fallback for insert, update, and delete. Mutants that +ignore handler types, drop the pending or persisting check, drop the whole +transaction check, run before the ambient branch, write only the first +mutation, or never complete the transaction fail 4, 4, 4, 8, 12, 1, and 9 +tests. + +Two shared conformance scenarios, `eq-filter-rows` and `eq-filter-peers`, run +the pooled path under React, Vue, Solid, Svelte, and Angular. A partition that +ignores a row's previous group fails both under every adapter. `eq-filter-peers` also checks the +source's public `subscriberCount`: an adapter that declares `pooledEqFilters` +must share one subscription for two queries on the same fields. Disabling pooling or using one partition per query fails it under React; +before this check the first passed. Each adapter's driver declares +`pooledEqFilters`. + +The loss audit found that a partition released its source one second after +its last listener, whatever `gcTime` its views had. A focused release-timing +test, `pooled-live-query-gc.test.ts`, now compares pooled and live-query +Collection release times. A fixed delay failed 5 of 10 cases, a missing 50 ms +floor for unsubscribed queries 1, a last-`gcTime`-wins rule 1, and releasing +at `gcTime` 0 2. Release timing is resource lifetime, so the publication +oracle does not observe it. + +Pooling later admitted residual conjuncts: any `where` conjunct that reads +only the query's own row, beside at least one `eq`, is evaluated per view +with the compiler's evaluator. Peers may add `not(eq(g, literal))`, which the +model evaluates itself, and a pinned history moves rows in and out of a view +within one group. A view that ignores the residual, reports unfiltered +entries, or sends a row entering or leaving it as an update fails the +pinned history and both campaigns. Hiding part of a group makes some +earlier histories rarer, so in one full run the arrival-order, +normalization, delete-plus-insert, and dropped in-group update mutants +escaped the random campaign; the fixed campaign and their pinned histories +still kill each. Peers' literals were weighted toward the normalized values +to keep the normalization mutant in the fixed campaign. + +## Pooled live query oracle + +| Requirement | Outcome | +| --- | --- | +| ORC-001 Contract authority and limits | Pass. The `eq` operand rules come from `src/query/compiler/evaluators.ts`; the pooled boundary and the terminal-error rule come from the live-query architecture document's pooled section and cleanup law. The opening prose lists the omissions. | +| ORC-002 Independent judgment | Pass. `expectedKeys` uses a local `eq` over plain values. Order, values, and status come from a live-query Collection, which compiles a D2 pipeline and does not use the partition. | +| ORC-003 Distinguishable responsibilities | Pass. Contract, model, grammar, driver, and refinement check are separate marked sections. | +| ORC-004 Generated-history controls | Pass. Reconstruction: every pinned history uses only domain values and step kinds. Ablation, run per axis on the campaigns with pinned cases skipped: without Dates, `NaN`, and `-0` the normalization mutant survives; without optimistic steps, frozen peers dropping optimistic rows survives; without mounts and unmounts, the terminal-at-creation mutant survives; without cleanup-restart, the status, termination, and both pending-cleanup mutants survive; without the second conjunct, first-field grouping survives. Range: at most four rows, three peers, eight steps, and ids 0 through 3; field values weight toward the literal so rows update within their group. Exclusion: an optimistic update to an equal value, an insert of an existing id, and an update or delete of a missing id are dropped. | +| ORC-005 Production path and observation | Pass. `createPooledLiveQuery` and `createLiveQueryObserver`, the adapter seam, run in wholesale and granular mode; the conformance scenarios run through React's `useLiveQuery`. Rows, keyed state, status, layout revision, and non-materialization are observed after every step, and granular changes are compared with a reference observer by type, key, value, and previous value, then replayed against the model. | +| ORC-006 Checker calibration | Pass. Thirteen mutants, classified above. | +| ORC-007 Fixed/random replay | Pass. Fixed seed `44_502_001`, an unseeded campaign, and a replay entry share one property and budget; the file is in `test:oracles`. | +| ORC-008 Stateful-model minimality | Pass. The model keeps source rows and, per mounted peer, the frozen keys at cleanup. The pinned cleanup history distinguishes a frozen peer from one mounted after the restart. | +| ORC-009 Vocabulary mapping | Pass. Equality partition, partition group, and pooled live query are glossary terms; a peer is one mounted pooled live query. | +| ORC-010 Failure fidelity and cleanup | Pass. `withOracleCleanup` releases observers, references, and the source and keeps the check failure. | +| ORC-011 Independent second formulation | Pass. The live-query Collection is the second formulation for order, values, and status. | +| ORC-012 Review evidence | This record; the coverage map links it. | +| ORC-013 Reusable boundary law | Pass. Normalization is rejected by the Date history, arrival order by the key-order history, and following the source's status by the cleanup history. | +| ORC-014 Controlled-premise handoff | Not triggered. The claim is limited to the mock source's sync transactions and lifecycle. | + +## Flat change tracking oracle + +| Requirement | Outcome | +| --- | --- | +| ORC-001 Contract authority and limits | Pass. The change-set rules come from `src/proxy.ts`'s `getChanges` contract: changed fields with their final value and deleted fields as `undefined`. | +| ORC-002 Independent judgment | Pass. `expectedChanges` folds the operations over a plain copy and does not import either tracker. | +| ORC-003 Distinguishable responsibilities | Pass. Contract, model, grammar, driver, and refinement check are separate marked sections. | +| ORC-004 Generated-history controls | Pass. Reconstruction: every pinned history uses domain values and operations. Ablation: removing `NaN`, `-0`, reverts, or deletions each loses a mutant. Range: one to three rows, three fields plus one added and one non-enumerable field, frozen or not, up to six operations per row including data and accessor defines, stored drafts, and a throw. Exclusion: none in generation; non-flat rows are rejected by the fallback witness. | +| ORC-005 Production path and observation | Pass. `withFlatChangeTracking`, `withArrayChangeTracking`, and `withChangeTracking` run the same callbacks; the result change sets are observed. `collection.update` selects between them. | +| ORC-006 Checker calibration | Pass. Thirteen mutants, classified above. | +| ORC-007 Fixed/random replay | Pass. Fixed seed `44_502_101`, an unseeded campaign, and a replay entry; the file is in `test:oracles`. | +| ORC-008 Stateful-model minimality | Not triggered. The model recomputes from the operations. | +| ORC-009 Vocabulary mapping | Pass. Draft, change set, and revert follow the proxy's terms. | +| ORC-010 Failure fidelity and cleanup | Not triggered. The oracle holds no resources. | +| ORC-011 Independent second formulation | Pass. The draft proxy is the second formulation. | +| ORC-012 Review evidence | This record; the coverage map links it. | +| ORC-013 Reusable boundary law | Pass. `!==` is rejected by the `NaN` history, `Object.is` by the `-0` history, and missing deletions by the deleted-field history. | +| ORC-014 Controlled-premise handoff | Not triggered. No provider is involved. | + +## Open work + +- Svelte's suite reads `@tanstack/db` from its built `dist`, so a mutant + must type-check and be rebuilt before Svelte can observe it. diff --git a/docs/contributing/oracle-reviews/2026-10-01-pr-1977-residual-convergence.md b/docs/contributing/oracle-reviews/2026-10-01-pr-1977-residual-convergence.md new file mode 100644 index 0000000000..1879a08b29 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-01-pr-1977-residual-convergence.md @@ -0,0 +1,129 @@ +# PR 1977 residual-convergence oracle review + +The reviewed baseline is origin/main at +`18abceee48ebde712e120cbc541289bb83f35d77`. The contributor's supplied +patch corresponds to proposed head `e6a1e2924d4acb5bb41b0478df1bbbe7171498a9`. +The reviewed candidate is that baseline plus the uncommitted changes whose +optimizer file has SHA-256 +`fee8078ebdfc40d9a431a7c48f7061dd1afbc0f7383a95a13f7d87507248bfc3` +and oracle file has SHA-256 +`bd5b06605814d3f1c190455438974f9112a53219930ec7d8b92673917d3dc56c`. +The separate task-local external-feedback ledger preserves all 13 PR and bot +items; this record preserves the oracle and repair evidence. +The loss-audit follow-up reviewed PR head +`0da9cb9a0612e0b905723dffd2791c27978c6ec2` against the guide and +production paths. The code-bearing follow-up head is +`92490bc7245c734ad547746786ae9c0219fe81db`. This review record is an +evidence-only successor to that head. + +## Contract and checked boundary + +The optimizer's predicate-pushdown contract permits a predicate on a +nonnullable outer-join source to enter that source. The joined row still needs +the predicate, so its outer copy is residual and must not be pushed again. +A predicate on a nullable source stays regular. One logical predicate should +enter each eligible source once, including when optimization runs again. +The public-row contract requires the same result as applying predicates after +the join. These rules follow the optimizer's documented pushdown behavior and +the established outer-join tests. + +The finite structural grammar crosses LEFT, RIGHT, INNER, and FULL joins with +separate or AND-combined clauses, both clause orders, and one or two active +predicates. Fixed extensions cover a LEFT/INNER chain with two pushed sources, +same-source OR, cross-source equality, greater-than, nullable-field +`isUndefined`, and an already residual outer clause. The checkpoint is the +returned `optimizeQuery` IR, both initially and after re-optimization. +The oracle counts each predicate in its source and outer WHERE and checks the +outer residual marker and clause count. Plain-array LEFT, RIGHT, and +three-source join models separately check the first public snapshot of a +live-query Collection, with matched and unmatched source rows. + +## RED, GREEN, and code weight + +| Subject | Result at the intended checkpoint | +| --- | --- | +| Original optimizer, full expanded oracle | 21 failed, 28 passed. Source copies multiply and outer residual markers disappear; the new greater-than witness alone fails at source predicate multiplicity. | +| Contributor's supplied patch, full expanded oracle | 1 failed, 48 passed. A later pass with an existing residual leaves three outer WHERE clauses, while the law expects one regular and one residual group. Predicate multiplicities and markers pass that case. | +| Candidate partition-and-combine repair, full expanded oracle | 49 passed. The candidate combines each regular/residual group once inside `applyOptimizations` and removes caller-side residual reassembly. | +| Candidate, optimizer, join, and join-subquery suites | 247 passed after the predicate-kind case was added. | +| Wrong-design controls | Marking regular clauses residual fails on `member.userId`'s marker. Keeping only the first residual fails on the missing `tag.region` predicate. Both are assertion failures at the structural checkpoint, not setup failures or timeouts. | + +At the initial reviewed candidate, the production change added 20 lines and +removed 28, net **minus eight**. The executable oracle added 533 lines. The +loss-audit follow-up has separate code-weight figures below. + +## Oracle guide audit + +| ID | Outcome | +| --- | --- | +| ORC-001 | The contract above states the optimizer and outer-join law, authority, finite grammar, and observations. The coverage map owns omitted expressions, join chains, and publication histories. | +| ORC-002 | The structural expectation is an explicit relational-side table, not the optimizer's nullable-source classifier. The public result models join and filter plain arrays; they import neither optimizer analysis nor compiler evaluation. | +| ORC-003 | The oracle opening states the contract, model, grammar, driver, and checkpoints. The table and array models give the answer; `optimizeQuery` and `createLiveQueryCollection` are the production drivers; assertions are adjacent to each case. | +| ORC-004 | The finite matrix claims only its stated input grammar. Reconstruction: each of 4 join kinds × 2 clause forms × 2 clause orders × 2 predicate counts is constructed and run. Ablation: join kind changes nullable-side eligibility; clause form tests residual-marker retention after combination; order checks order independence; the extra predicate checks same-source multiplicity. Range: one or two active predicates, with the FULL no-push case, are the bounds. Exclusion: unknown source aliases are outside this valid-query grammar; UNION, arbitrary functions, and incremental histories are outside the claim. This records the controls even though the guide distinguishes finite enumeration from an important generated property. | +| ORC-005 | Structural work is observed at the `optimizeQuery` return and after a later pass. The public driver observes exact projected rows at the first synchronous Collection snapshot. The recorder preserves duplicate predicate terms and result rows. | +| ORC-006 | The original implementation and two wrong designs fail at the intended assertions. The contributor's partial fix also fails the later-pass clause-count assertion. | +| ORC-007 | Not applicable: there is no important generated property or random campaign. Every enumerated case runs in the package oracle test file. | +| ORC-008 | Not applicable: the structural table and public array models are stateless; no model state is merged or split. | +| ORC-009 | Source Collection, live-query Collection, public row, public snapshot, and checkpoint use the glossary meanings. The model's `teamPush` and `memberPush` booleans describe expected source placement, not production states. | +| ORC-010 | Not applicable: the fixed tests do not shrink, record mutable traces, or perform cleanup that could replace an assertion failure. | +| ORC-011 | The plain-array public result is a different formulation from the structural predicate-count law. It checks the plausible shared fault that a plan with fewer predicates changed joined rows, including unmatched rows. It does not independently prove optimizer work cost. | +| ORC-012 | This record scores every applicable requirement and identifies non-applicable triggers. The closure claim is limited to the stated grammar, paths, and checkpoints; remaining cells are assigned in the coverage map. | +| ORC-013 | The original ten-copy result and the repaired one-copy result distinguish the once-only law from repeated pushdown. LEFT and RIGHT active-side cases distinguish the relational-side boundary from a LEFT-only rule; FULL is the no-push control. The existing-residual case rejects a plausible partial grouping. | +| ORC-014 | Not applicable to the structural path, which has no controlled provider. The public driver uses synchronous in-memory source Collections and claims only their first snapshot; it does not claim an external-provider handoff. | + +This closes the reported repeated-pushdown class for the finite structural +grammar and initial public-result paths above. Arbitrary expression trees, +more complex join chains, and incremental source changes or publication +histories remain open cells for the optimizer-semantics owner. The current +tests give no wall-clock runtime guarantee. + +## Package-wide validation + +After building the isolated candidate package, the parent audit ran +`pnpm exec tsc --noEmit -p packages/db/tsconfig.json --pretty false` +successfully. The full `packages/db` Vitest suite then passed 229 files and +7,937 tests with no type errors using `--testTimeout=120000`. This resolves +the earlier self-package declaration setup errors. The structural and public +history bounds above remain unchanged. + +## Loss-audit follow-up + +The audit found a real adjacent predicate-preservation defect: with a join, +`groupWhereClauses` silently discarded a clause that touched no source. A +`Value(false)` after an active-side filter therefore vanished, and the first +public live-query Collection snapshot contained three rows instead of none. +The structural predicate-preservation assertion and the public snapshot test +both failed before the repair and passed afterward. The repair retains every +clause without exactly one pushable source in the outer WHERE. Three older +optimizer tests expected source-free or malformed clauses to disappear; their +false-green expectations now assert preservation. + +The structural matrix now checks both `optimizedQuery` and +`sourceWhereClauses`, including nullable-source exclusion. Replacing the latter +with an empty map failed the left-join matrix case at the intended assertion +(`[]` versus `['team']`). Fixed extensions check a namespace-only predicate, +later RIGHT/FULL joins that make earlier aliases nullable, and an ordered +QueryRef that declines pushdown. Each checks the first and later optimizer +pass. The source-free case also checks public rows. These extensions do not +establish public results for the later join chains or all QueryRef refusal +reasons. UNION remains a separate optimizer path. + +The parallel preparation review found that the structural term reader ignored +predicate values. It could not distinguish `team.active=true` from +`team.active=false`. The reader now records literal values and rejects +unexpected compared expressions. A calibration test failed when an equality +literal was deliberately hidden, then passed with the corrected reader. The +main matrix also checks those values in pushed and residual predicates. +The duplicate focused LEFT JOIN test was removed because the matrix receives +its trace and its original-implementation RED evidence. + +The final branch diff against the fetched `origin/main` adds 22 production +lines and removes 31, net **minus nine**. The executable oracle adds 762 lines; +the existing optimizer tests add 23 and remove 13. Tests and review documents +are reported separately from production code. + +At the code-bearing head, the full `packages/db/tests` suite passed 197 files +and 7,675 tests with two Vitest threads and a 120-second test timeout. The +package TypeScript check, focused ESLint, Prettier, and diff check passed. +An earlier sandboxed full run could not write Vitest's temporary cache and +failed its replay calibration; the same suite passed with that cache writable. diff --git a/docs/contributing/oracle-reviews/2026-10-02-absent-key-source-delete.md b/docs/contributing/oracle-reviews/2026-10-02-absent-key-source-delete.md new file mode 100644 index 0000000000..43f0cd8189 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-02-absent-key-source-delete.md @@ -0,0 +1,161 @@ +# Source delete for a key the source never held + +Base revision: `06cab6fc7` (`main` after #2005). Tested revision: +`74e5b80d6`. Production code is unchanged from the base revision. + +## Contract + +A source may delete a key it does not hold. A typical cause: the backend +accepted an optimistic insert, then deleted the row before the source +streamed it. The maintainer decision for this owner is: + +- An accepted optimistic snapshot of the key retires, as it does for any + ordinary source publication. The row disappears. +- An active request for the key stays in place. The delete removes no base + row and acknowledges no request. + +`main` already behaves this way. This review adds the generated histories +and witnesses. It changes no production code. + +## Gap + +The coverage map listed the case as open. The optimistic-history driver +dropped every delete for a key the source did not hold before the model or +the Collection saw it. So no optimistic-history campaign ever wrote such a +delete. + +## Witnesses + +- The driver keeps deletes for keys the source does not hold and counts them. + The fixed campaign must write at least one, beside deletes of held keys. +- Three pinned histories in + `collection-state-retention-oracle.property.test.ts` accept an optimistic + insert and then delete its key from a source that never held it. They cover + a queued delete, an immediate delete, and a delete written inside the + insert handler. +- A fourth pinned history writes an immediate delete while the insert is + still active. The row stays visible through settlement. + +## ORC outcomes + +The record claims a repaired generated-history grammar, so ORC-012 applies. +Each requirement follows. + +- **ORC-001 contract authority and limits: met.** The maintainer decision + above is the authority. The oracle's opening prose states the contract, + and Limits below states what it does not claim. +- **ORC-002 independent judgment: met.** The model applies its existing drain + rule. Every ordinary source publication retires accepted snapshots, and a + source delete acknowledges no request. The model reads no production state + and imports no production classifier. +- **ORC-003 distinguishable responsibilities: met.** + `optimistic-history-oracle.ts` keeps the contract prose, the + `HistoryModel`, the driver `runOptimisticHistory`, and its per-step refinement + check apart. The grammar lives in + `collection-state-retention-oracle.property.test.ts` as `sourceBatch` and + `optimisticHistory`. +- **ORC-004 grammar controls: met, with one gap.** + - Reconstruction: the four pinned histories are legal histories of the + grammar. Edits and source deletes both draw keys from 1 to 3. + - Ablation and range: the fixed-campaign control requires absent-key + deletes, and it requires more deletes in total than absent-key deletes, so + held-key deletes remain. + - Exclusion: the driver no longer filters deletes. The source still writes + `insert` only for a key it lacks. + - Gap: no counter shows that the generator reaches an absent-key delete + while an accepted snapshot of the same key exists. The pinned histories + reach that premise directly. +- **ORC-005 production path and observation: met.** The driver writes through + the real sync `begin`, `write`, and `commit` API of a Collection. After each + step it compares public rows, both subscribers' replicas, downstream query + rows, and request outcomes with the model. +- **ORC-006 checker calibration: met.** A hostile mutant rejects a sync delete + for a key absent from the source projection. With the old grammar, the + optimistic-history campaigns pass under it. Only the separate retained + authoritative state property fails, and that property has no optimistic + layers. With the new grammar, the fixed and random optimistic-history + campaigns and the three accepted-insert histories fail. This ran on the + tree of `af0b8648a`. `74e5b80d6` adds only the fourth pinned history. A + mutant that drops any delete for a key absent from `syncedData` is too broad + to distinguish the change. The old grammar already kills it through queued + deletes of held keys. +- **ORC-007 fixed and random campaigns: met.** The fixed campaign uses seed + 86103. The random campaign runs under the property name + `collection-state.optimistic-history`, which accepts a seed and path for + replay. Both pass on `74e5b80d6`, and so do the 153 tests in the four files + that use the driver. On `af0b8648a`, the `@tanstack/db` suite passed 244 + files and 8,269 tests. +- **ORC-008 stateful-model minimality: not applicable.** The model gains no + state and loses none. An absent-key delete takes the existing delete branch + of the drain. +- **ORC-009 vocabulary mapping: met, with a recorded term.** "Accepted + snapshot" maps to a completed direct mutation that the Collection retains + until source publication. The model's existing `retired` flag means that + the snapshot stopped overlaying the base. That differs from the glossary's + demand retirement. This change keeps the existing model term and does not + rename it. +- **ORC-010 failure fidelity and cleanup: not applicable.** This change alters + no shrinking, capture, or cleanup path. The driver keeps its existing + `withHistoryCleanup`. +- **ORC-011 independent second formulation: not applicable.** No reviewer + named a fault that production and the model could share for this case. +- **ORC-012 review evidence: met by this record.** +- **ORC-013 distinguishing witness: met, with one limit.** The law is + conditional: an accepted snapshot retires, but an active request stays. The + accepted-insert histories reach the first side. The active-insert history is + the nearby witness for the second side. A wrong design that also removed the + active row would differ from the model at the step after the delete. No + mutant demonstrates that rejection. +- **ORC-014 controlled-premise handoff: not applicable.** The oracle uses no + controlled provider or host. The source is the Collection's own sync API. + +## Limits + +An immediate delete that arrives while the insert is active leaves the +accepted row until the next source publication. That follows the existing +rule that a delete acknowledges no request. Immediate sync batches are +scheduled for removal in a separate change, so this record does not revisit +that rule. + +## Update, 2026-10-05: settlement-drop law + +The record above describes the retention law that was current on +`06cab6fc7`. The settlement-drop change replaces that law. See +[the settlement-drop review](2026-10-03-settlement-drop.md). The following +now applies to this owner: + +- A source delete for a key the source never held removes no applied synced + row and acknowledges no request. +- If the source commits the delete while the transaction persists, or inside + its handler, the delete is queued. The completed row is held, and the drop + and the delete publish together. The row is gone after settlement. +- Once the optimistic state has dropped, the delete changes no visible row. +- A persisting transaction keeps its optimistic row. This includes a truncate + that carries the delete: the truncate applies at once, beneath that row. +- The `immediate` dimension is gone, because `begin({ immediate })` was + removed. + +The pinned histories in `collection-state-retention-oracle.property.test.ts` +now cover these cases: + +- a queued delete; +- a delete inside the handler; +- a delete after the drop; +- a persisting insert with a queued delete; +- a persisting insert with a truncate delete. + +The driver and its counters are unchanged. + +Mutants on the merged branch: + +- **Reject (throw on) a delete for a key absent from the source projection:** + killed. The state-retention oracle fails 11 tests, including the fixed and + random campaigns and the pinned histories. +- **Silently drop the same delete:** survived, and is equivalent under the + new law. The key is absent from the applied synced rows, and the + optimistic state drops at settlement whether or not a queued sync + transaction touches the key. So readers see the same rows and the same + publications. Under the retention law, this delete was what retired the + accepted snapshot, so the mutant was observable there. + +The ORC-004 gap and the ORC-013 limit recorded above still apply. diff --git a/docs/contributing/oracle-reviews/2026-10-02-glossary-alignment.json b/docs/contributing/oracle-reviews/2026-10-02-glossary-alignment.json new file mode 100644 index 0000000000..9cccf4a5b0 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-02-glossary-alignment.json @@ -0,0 +1,1159 @@ +{ + "reviewedCommit": "f993daf838da4905d3a327cf6c66d8bdf16d106b", + "implementationBase": "b940a363a7631c49e14dc67e2adeda0a96dc366c", + "auditCommit": "06cab6fc7b808acfbfb3af1eb2fc1fdc3c9f0fa8", + "scope": "Glossary alignment and filename discovery. This is not a full ORC-001–014 conformance audit or a runtime bug-class closure claim.", + "findings": [ + { + "id": "01_collection-1", + "severity": "medium", + "kind": "meaning-conflict", + "term": "private replacement / retirement / publication", + "evidence": "\"Failure retires that replacement. Independent live source work remains public.\" The model instead sets replacement.failed and returns while the publication barrier remains open (lines 299–315); source writes continue into replacement.rows (431–434). The pinned test \"keeps later source changes private after a failed replay\" (1948–1967) demonstrates the opposite boundary, while retirement is tested only after final owner release (1970 onward).", + "glossary": "Runtime terms: private replacement is source/graph state withheld while authoritative replay or repair is incomplete; public snapshot is the last coherent exposed rows; publication makes the coherent result observable. ARCHITECTURE.md normative publication law keeps the prior public result and later partial changes private after failure.", + "recommendation": "State that replay failure keeps the replacement and later source changes private while retaining the last public snapshot. State separately that final logical-demand retirement can retire the replacement barrier without publishing it. Preserve the current model and tests.", + "relatedPaths": [ + "packages/db/tests/collection-subscription-lifecycle-grammar.ts", + "packages/db/tests/collection-subscription-replay-oracle.property.test.ts", + "packages/db/src/query/live/ARCHITECTURE.md" + ], + "auditPath": "packages/db/tests/collection-subscription-lifecycle-publication.property.test.ts", + "auditLine": 30, + "auditEndLine": 32, + "currentPath": "packages/db/tests/collection-subscription-lifecycle-publication-oracle.property.test.ts", + "resolution": "Failed truncate replay retains private replacement and later source changes. Final logical-owner retirement can discard the barrier without publication.", + "status": "resolved within terminology scope" + }, + { + "id": "01_collection-2", + "severity": "medium", + "kind": "meaning-conflict", + "term": "sync run / unsubscribe", + "evidence": "\"`restart`, `cleanup`, and `unsubscribe` change the enclosing sync run.\" In the unsubscribe branch (584–589), the reducer releases this subscription's attempts, clears owners, and sets unsubscribed; it leaves active, syncRunGeneration and collectionStatus untouched. Cleanup and restart are separately allowed after unsubscribe (353–365), proving the sync run outlives that subscription.", + "glossary": "Runtime terms: sync run is one Collection sync invocation and installed resources until cleanup invalidates them; subscription is a consumer with its own demands/lifecycle; cleanup ends the current sync run; restart starts another.", + "recommendation": "Split the sentence: cleanup ends the current sync run; restart starts a new sync run; unsubscribe ends this subscription and retires its logical subset owners/acquisition leases without itself ending the Collection sync run.", + "relatedPaths": [ + "packages/db/tests/collection-subscription-lifecycle-history.property.test.ts", + "packages/db/tests/collection-subscription-lifecycle-publication.property.test.ts" + ], + "auditPath": "packages/db/tests/collection-subscription-lifecycle-grammar.ts", + "auditLine": 17, + "auditEndLine": 18, + "currentPath": "packages/db/tests/collection-subscription-lifecycle-grammar-oracle.ts", + "resolution": "Cleanup ends a sync run, restart starts another, and unsubscribe ends its subscription and ownership claims.", + "status": "resolved within terminology scope" + }, + { + "id": "01_collection-3", + "severity": "medium", + "kind": "missing-mapping", + "term": "PhysicalAcquisitionState / starting", + "evidence": "The contract calls this \"a small physical-acquisition state\" (19), and PhysicalAcquisitionState includes \"starting\". The driver records starting:release/starting:cleanup even for outcome === throw (1143–1161), while targetStarted explicitly excludes throws and cleanup-invalidated startup (1137), and no unload is expected (1168–1169). Thus the state domain also covers tentative acquisition attempts before any physical acquisition or lease exists.", + "glossary": "Demand/pagination terms: acquisition attempt starts at adapter invocation; only acceptance establishes physical acquisition and acquisition lease. Model alignment rules require explicit mappings for combined/split production concepts.", + "recommendation": "Name this domain AcquisitionLifecycleState (or another explicitly model-only abstraction), and document none=no accepted work, starting=tentative acquisition attempt, active=accepted physical acquisition/lease, obsolete=retired work, failed-release=release already attempted. Distinguish attempt cancellation from acquisition release in the interaction labels. This preserves the finite model without copying production machinery.", + "relatedPaths": [ + "packages/db/src/collection/subscription.ts", + "packages/db/tests/collection-subscription-lifecycle-grammar.ts" + ], + "auditPath": "packages/db/tests/collection-subscription-lifecycle-oracle.test.ts", + "auditLine": 185, + "auditEndLine": 199, + "currentPath": "packages/db/tests/collection-subscription-lifecycle-oracle.test.ts", + "resolution": "AcquisitionLifecycleState explicitly combines tentative attempts, accepted acquisitions, and release states. Its retire action maps each case.", + "status": "resolved within terminology scope" + }, + { + "id": "02_optimistic_values-1", + "severity": "medium", + "kind": "meaning-conflict", + "term": "publication / published row", + "evidence": "\"const published = (changes as Partial).items\" and \"published row, ${context}\" label a field returned by withChangeTracking as a publication. The driver at lines 216–218 only extracts changes from a plain draft; it creates no Collection and observes no read/event/downstream publication. The opening contract likewise says the draft \"publishes what the native row now holds\" (11–12). The same conflation occurs for typed arrays (386–391), proxy-iteration extraction-only checks (15, 152–161, 298–325), proxy-detachment's extraction-only alias check (749–760), and proxy-revert's \"before the draft publishes\" checkpoint (33–36).", + "glossary": "Runtime terms: publication is the boundary that makes one coherent result observable to reads, events, and downstream queries. Model alignment rule forbids giving a narrower model-only boundary the name of a stronger production concept.", + "recommendation": "Call these results a detached change set, detached field value, or reconstructed draft row; call the checkpoint change extraction or callback return. Use publication for the existing tests that actually observe a public Collection. Update the small source comment in proxy.ts:273 that uses publication for the same detachment operation, or explicitly map draft extraction as a prerequisite of later Collection publication.", + "relatedPaths": [ + "packages/db/tests/proxy-iteration-contract.test.ts", + "packages/db/tests/proxy-detachment-contract.test.ts", + "packages/db/tests/proxy-revert-oracle.property.test.ts", + "packages/db/src/proxy.ts" + ], + "auditPath": "packages/db/tests/proxy-native-methods.property.test.ts", + "auditLine": 223, + "auditEndLine": 228, + "currentPath": "packages/db/tests/proxy-native-methods-oracle.property.test.ts", + "resolution": "Draft extraction produces detached changes and values. Publication refers to observable Collection state.", + "status": "resolved within terminology scope" + }, + { + "id": "02_optimistic_values-2", + "severity": "low", + "kind": "meaning-conflict", + "term": "fault injection", + "evidence": "\"Fault injection proves those observations can reject wrong keys, partial batches, stale previous values, and transient fields.\" The mechanism explicitly says \"Faults act on a copy of a real callback, never on production state\" (639); for example lines 644–650 change a captured key to String(change.key). These are deliberately wrong observed answers supplied to the checker, not failures, delays, cancellations or malformed provider responses injected into the runtime.", + "glossary": "Oracle and model terms: mutant is a deliberate wrong answer or implementation used to test oracle sensitivity; fault injection is a deliberate runtime failure, delay, cancellation, or malformed response used to test a system boundary.", + "recommendation": "Change the opening claim to \"Wrong-answer mutants calibrate these observations\" or \"Captured-observation corruption proves checker sensitivity.\" Optionally rename the local fault parameter to observationMutant/corruption; retain fault injection for actual runtime boundary failures.", + "relatedPaths": [ + "packages/db/tests/optimistic-history-publication.test.ts", + "packages/db/tests/optimistic-history-outcomes.test.ts", + "packages/db/tests/optimistic-transaction-oracle.property.test.ts" + ], + "auditPath": "packages/db/tests/optimistic-history-oracle.ts", + "auditLine": 37, + "auditEndLine": 39, + "currentPath": "packages/db/tests/optimistic-history-oracle.ts", + "resolution": "Copied callback corruptions are observation mutants. Runtime fault injection remains a separate concept.", + "status": "resolved within terminology scope" + }, + { + "id": "03_includes-1", + "severity": "medium", + "kind": "meaning-conflict", + "term": "startup settlement / resolveSync / rejectSync", + "evidence": "The helper promises 'atomic source batches and explicit startup settlement.' It exports resolveSync/rejectSync (23–24) and directly aliases mockSyncCollectionOptions.utils (64–65). That utility resolves/rejects syncPendingPromise (utils.ts 328–355), which is awaited by onInsert/onUpdate/onDelete (383–393). Default source startup instead commits initialData and calls markReady synchronously (359–374). The includes optimistic driver uses source.resolveSync followed by transaction.isPersisted.promise (includes-optimistic-oracle.property.test.ts 513–514). Therefore these controls settle optimistic mutation persistence; they do not settle startup, end a sync run, or represent a sync transaction's applied receipt.", + "glossary": "Runtime terms distinguish optimistic transaction, sync transaction, sync run and qualified readiness. Demand and pagination terms define settlement as a named promise fulfilling/rejecting and applied receipt as commit()'s visibility receipt. Grammar rules require qualification of the boundary that settled.", + "recommendation": "Replace 'explicit startup settlement' with 'explicit optimistic mutation-handler fulfillment or rejection'. At this helper boundary, expose fulfillMutationPersistence/rejectMutationPersistence (or add an explicit mapping for the inherited resolveSync/rejectSync names) and state that startup publishes initial rows and marks the Collection ready synchronously. Keep source write batches and mutation persistence as separate driver operations.", + "relatedPaths": [ + "packages/db/tests/query/includes-collection-oracle.property.test.ts", + "packages/db/tests/query/includes-optimistic-oracle.property.test.ts", + "packages/db/tests/query/includes-oracle.property.test.ts", + "packages/db/tests/query/includes-publication-oracle.test.ts", + "packages/db/tests/utils.ts" + ], + "mergedFrom": [ + "02_optimistic_values-3" + ], + "additionalEvidence": "\"resolveSync: () => { syncPendingResolve!() }\" and \"rejectSync: (error: Error) => { syncPendingReject!(error) }\" settle the gate consumed by onInsert/onUpdate/onDelete via awaitSync (383–393). They do not complete the sync run, commit a sync transaction, or acknowledge source rows. The no-initial-state helper duplicates the same names (454–485). A concrete consumer in virtual-props-cache.test.ts:84 calls rejectSync solely to reject an optimistic insert's isPersisted promise.", + "auditPath": "packages/db/tests/query/includes-oracle-helpers.ts", + "auditLine": 7, + "auditEndLine": 7, + "currentPath": "packages/db/tests/query/includes-oracle-helpers.ts", + "resolution": "Legacy resolveSync/rejectSync helpers control mutation-handler settlement. Startup rows and Collection readiness occur separately.", + "status": "resolved within terminology scope" + }, + { + "id": "03_includes-2", + "severity": "medium", + "kind": "meaning-conflict", + "term": "route / active bucket / Collection facade", + "evidence": "The contract says 'Parents on the same active route share one facade' and \"Moving a parent route gives it the destination route's facade\". The actual sharing witness constructs parents { id: 1, group: 1 } and { id: 2, group: 1 }, asserts their child handles are identical, then removes each parent independently (1777–1798). These are different materialization cells and hence different route links to one shared bucket. The same opening comment later distinguishes route and bucket state (64–65), so route is not declared as a local synonym for the bucket.", + "glossary": "Live-query materialization terms: route relation = weighted links from bucket keys to materialization cells; materialization cell = one include field on one parent-row occurrence; Collection facade = stable public Collection shared by parents routed to one active bucket. ARCHITECTURE.md 546–560 gives exactly this distinction.", + "recommendation": "Write 'Parents routed to the same active bucket share one facade' and 'Moving a parent route gives it the destination bucket’s facade.' Use 'shared bucket' in the associated contract/grammar descriptions where the intended common identity is the correlation partition; retain route for the individual parent-cell link.", + "relatedPaths": [ + "packages/db/src/query/live/ARCHITECTURE.md", + "packages/db/tests/query/includes-oracle.property.test.ts" + ], + "auditPath": "packages/db/tests/query/includes-collection-oracle.property.test.ts", + "auditLine": 44, + "auditEndLine": 46, + "currentPath": "packages/db/tests/query/includes-collection-oracle.property.test.ts", + "resolution": "Parents routed to one active bucket share a Collection facade. Individual parent links remain routes.", + "status": "resolved within terminology scope" + }, + { + "id": "03_includes-3", + "severity": "low", + "kind": "ambiguous-term", + "term": "demand settlement / initial readiness", + "evidence": "The laws say 'Every reachable child demand must settle before initial readiness' and 'An obsolete demand cannot publish rows or settle a later incarnation.' Yet 51–53 correctly define demand incarnation as an interval of active logical demand that need not map to one acquisition. The matching driver resolves requests[0].deferred, awaits requests[0].outcome, and still expects preload.preloadSettled and live.isReady() false; resolving requests[1] allows preload to finish (1754–1782). Thus acquisition promises settle while logical demand remains active, and the asserted consumer is the live query. The progressive lane repeats 'before demand settles' (2289 and 2719).", + "glossary": "Demand and pagination terms: demand is logical need that may outlive physical work; settlement is a promise becoming fulfilled or rejected. Runtime readiness must be qualified as Collection, subscription or initial-query readiness. Grammar rules: a demand becomes active or retires, and a promise settles.", + "recommendation": "State that current acquisitions covering reachable demand must fulfill with their establishing writes applied before initial-query readiness; retiring demand removes its blocking participation. Say an obsolete acquisition's settlement cannot satisfy current demand or complete the current preload. Retain the useful demand-incarnation mapping and rename the affected test descriptions/comments consistently.", + "relatedPaths": [ + "packages/db/tests/query/includes-temporal-oracle.test.ts" + ], + "auditPath": "packages/db/tests/query/includes-temporal-oracle.test.ts", + "auditLine": 39, + "auditEndLine": 41, + "currentPath": "packages/db/tests/query/includes-temporal-oracle.test.ts", + "resolution": "Initial-query readiness, current acquisition fulfillment, applied writes, and preload settlement have explicit boundaries.", + "status": "resolved within terminology scope" + }, + { + "id": "04_acquisition-1", + "severity": "medium", + "kind": "meaning-conflict", + "term": "applied receipt / acquisition result", + "evidence": "`function requirePendingAppliedReceipt(receipt: LoadSubsetRequestResult)` accepts the subset-load result. At line 566, `const receipt = source._sync.loadSubset({})` feeds it. The asynchronous driver wraps `applyRow()` and awaits its actual commit receipt (lines 539–549), so the observed load promise can include delivery plus application; it is not necessarily the SyncAppliedReceipt returned by commit().", + "glossary": "Demand and pagination: applied receipt is specifically the SyncAppliedReceipt returned by commit(); an acquisition can establish its result through multiple sync transactions and receipts.", + "recommendation": "Rename the helper to requirePendingLoadSubsetResult and its argument/local variables to loadResult or acquisitionResult. Keep appliedReceipt for the result of commit(), and state that this fixture's load result awaits its establishing applied receipt.", + "relatedPaths": [], + "auditPath": "packages/db/tests/query/load-subset-oracle.property.test.ts", + "auditLine": 80, + "auditEndLine": 86, + "currentPath": "packages/db/tests/query/load-subset-oracle.property.test.ts", + "resolution": "loadSubset returns a load result. Only commit() returns the applied receipt.", + "status": "resolved within terminology scope" + }, + { + "id": "04_acquisition-3", + "severity": "medium", + "kind": "missing-mapping", + "term": "before-settlement / after-success / acquisition outcome", + "evidence": "`delivery: 'before-settlement' | 'after-success'` is described as an acquisition control dimension, and line 547 says `Deferred application happens only after a live attempt succeeds.` In the driver, gate.promise settles at lines 336–346, then the after-success branch awaits apply() at line 347 before the actual loadSubset promise returned at lines 333–348 fulfills. Thus after-success is after a controlled response gate, not after successful acquisition settlement.", + "glossary": "Applied settlement requires a successful subset load to wait for every establishing applied receipt; settlement must identify what promise settled. Model alignment rule requires an explicit mapping for a split production concept.", + "recommendation": "Name this controlled gate providerResponse (or modelResponseGate) and define its mapping beside Scenario. Use before-provider-response/after-provider-response or apply-before-response/apply-after-response and say both branches await application before acquisition fulfillment.", + "relatedPaths": [], + "auditPath": "packages/db/tests/query/ordered-lifecycle-oracle.property.test.ts", + "auditLine": 91, + "auditEndLine": 93, + "currentPath": "packages/db/tests/query/ordered-lifecycle-oracle.property.test.ts", + "resolution": "The controlled provider-response gate is distinct from acquisition fulfillment after establishing applied receipts.", + "status": "resolved within terminology scope" + }, + { + "id": "04_acquisition-4", + "severity": "medium", + "kind": "missing-mapping", + "term": "boundary request", + "evidence": "`const isPage = options.orderBy !== undefined` followed by `kind: isPage ? 'page' : 'boundary'` classifies every unordered acquisition as boundary. The custom-collation full-source lane explicitly expects this kind to be boundary (lines 3502–3508), with no cursor, limit or offset. The opening contract correctly says custom collation uses one full-source request, so the recorder gives that distinct concept a continuation-boundary name.", + "glossary": "Demand and pagination: boundary is the last value safely established for ordered continuation; tie group is the equal-order rows at that boundary. Architecture distinguishes tie-boundary acquisition from authoritative full-source acquisition. A combined model abstraction must name and list what it combines.", + "recommendation": "Use neutral ordered/unordered request-shape categories and explicitly map unordered to tie or full-source, or preserve page/prefix/tie-boundary/full-source as separate observations. Update the custom-collation assertion to call its request full-source.", + "relatedPaths": [ + "packages/db/tests/query/ordered-lifecycle-oracle.property.test.ts" + ], + "auditPath": "packages/db/tests/query/ordered-work-oracle.property.test.ts", + "auditLine": 347, + "auditEndLine": 350, + "currentPath": "packages/db/tests/query/ordered-work-oracle.property.test.ts", + "resolution": "with-order and without-order describe request shape. Comments map each category to its actual acquisition scope.", + "status": "resolved within terminology scope" + }, + { + "id": "04_acquisition-2", + "severity": "low", + "kind": "meaning-conflict", + "term": "settle / settled", + "evidence": "The title says `does not settle a window move after its sync run is cleaned up`, but line 4447 requires `await expect(move).rejects.toMatchObject({ name: 'AbortError' })`: rejection is settlement. The same conflict appears in load-subset-oracle.property.test.ts:1787 (`does not settle a demand...`) while line 1078 asserts rejection. In that file, lines 576–584 set `settled` only on fulfillment. Pagination's separate driver field `request.settled = true` at line 1991 is set before writes and before resolving the promise at line 2026; actual settlement is independently recorded in physicallySettled at 1852–1855.", + "glossary": "Demand and pagination and grammar rules: settlement means fulfillment or rejection; a promise settles, while demand becomes active or retires. Use success only for fulfillment.", + "recommendation": "Say the window promise rejects on cleanup and cannot fulfill afterward. Say an aborted load establishes no successful coverage rather than that demand cannot settle. Rename success-only flags to fulfilled; rename the pagination driver's early bookkeeping flag to responseStarted/handled, leaving settled for actual promise settlement.", + "relatedPaths": [ + "packages/db/tests/query/load-subset-oracle.property.test.ts" + ], + "auditPath": "packages/db/tests/query/pagination-oracle.property.test.ts", + "auditLine": 4385, + "auditEndLine": 4385, + "currentPath": "packages/db/tests/query/pagination-oracle.property.test.ts", + "resolution": "Success-only observations use fulfilled. responseStarted records provider response processing through delivery or rejection.", + "status": "resolved within terminology scope" + }, + { + "id": "04_acquisition-5", + "severity": "low", + "kind": "meaning-conflict", + "term": "source reports ready", + "evidence": "The test is named `publishes a replay replacement before its source reports ready`, but it installs `sourceSubscription!.on('status:ready', ...)` at line 645. This observes the source subscription's readiness transition; it does not observe the source Collection reporting ready (the adapter already calls markReady at startup).", + "glossary": "Runtime: Collection status and subscription status are distinct; readiness must be qualified as Collection readiness, subscription readiness or initial-query readiness.", + "recommendation": "Rename the test to publishes a truncate-replay replacement before its source subscription reports ready, and keep that qualification in any summary of this witness.", + "relatedPaths": [], + "auditPath": "packages/db/tests/query/load-subset-replay-refinement-oracle.test.ts", + "auditLine": 596, + "auditEndLine": 596, + "currentPath": "packages/db/tests/query/load-subset-replay-refinement-oracle.test.ts", + "resolution": "The replay witness explicitly observes source subscription readiness.", + "status": "resolved within terminology scope" + }, + { + "id": "05_query-2", + "severity": "medium", + "kind": "glossary-gap", + "term": "equality partition", + "evidence": "Glossary says an equality partition is \"shared by every pooled live query that filters on those fields\". Pooled oracle lines 6–7 instead says sharing requires \"the same `eq` fields and order\". Architecture lines 1256–1258 says \"Queries with different orders use different partitions\", and pooled-live-query.ts:417–418 explicitly adds orderKey to shapeKey. The oracle reflects actual architecture, while the glossary definition implies wider sharing.", + "glossary": "Runtime terms, equality partition: source rows grouped by eq-normalized values of one field set, shared by every pooled live query filtering on those fields.", + "recommendation": "Update the glossary definition to say an equality partition is shared for the same source Collection, equality-field set, and compatible query order (including its comparison/null-order options). Preserve the oracle wording and order-sensitive partition key.", + "relatedPaths": [ + "packages/db/tests/query/pooled-live-query-oracle.property.test.ts", + "packages/db/tests/query/pooled-live-query-gc.test.ts", + "packages/db/src/query/live/ARCHITECTURE.md", + "packages/db/src/query/pooled-live-query.ts" + ], + "auditPath": "docs/contributing/glossary.md", + "auditLine": 23, + "auditEndLine": 23, + "currentPath": "docs/contributing/glossary.md", + "resolution": "The glossary defines equality partitions by source Collection, equality fields, order terms, and resolved comparison options.", + "status": "resolved within terminology scope" + }, + { + "id": "05_query-1", + "severity": "low", + "kind": "meaning-conflict", + "term": "fault injection / mutant", + "evidence": "\"function injectDeliveryFault\" copies actual observations into \"const corrupted = [...actual]\", then removes a peer delivery or inserts a duplicate bootstrap. It is passed to assertHistory at lines 563–568. The opening calls these \"injected faults\" (82). These are deliberate wrong answers after observing production, not runtime failures. The same pattern occurs in identity-output-shape (40–41 and 271–276: \"shared-wrong-output\" replaces both result bags) and cold-join (34–35; 215 alters the observed amount and 219 hides acquisition records).", + "glossary": "Oracle and model terms: mutant = deliberate wrong answer or implementation used to test oracle sensitivity; fault injection = deliberate runtime failure, delay, cancellation, or malformed response used to test a system boundary.", + "recommendation": "Call these result/observation mutants: DeliveryMutant, applyDeliveryMutant, and mutant parameters; use \"wrong-result controls\" or \"mutants\" in the two query contracts. Keep genuinely thrown listener failures and controlled runtime rejection called fault injection.", + "relatedPaths": [ + "packages/db/tests/query/identity-output-shape-oracle.test.ts", + "packages/db/tests/query/cold-join-reconciliation-oracle.test.ts" + ], + "auditPath": "packages/db/tests/live-query-observer-history.property.test.ts", + "auditLine": 348, + "auditEndLine": 363, + "currentPath": "packages/db/tests/live-query-observer-history-oracle.property.test.ts", + "resolution": "Result and observation corruptions use mutant terminology. Runtime listener failures retain fault terminology.", + "status": "resolved within terminology scope" + }, + { + "id": "05_query-3", + "severity": "low", + "kind": "ambiguous-term", + "term": "settle / settlement", + "evidence": "The test is titled \"runs join live queries once after their parent queries settle\". All preloads finish at 1221–1225; the relevant operations are synchronous tx.mutate calls at 1233–1236 and 1243–1250, immediately followed by row and graph-run-count assertions. The transaction has autoCommit:false and is rolled back only at 1256. Thus \"settle\" here means parent graph jobs finish and publish, not a promise fulfillment/rejection. The similarly named test at 507 also only checks synchronous dependent rows after commit throws.", + "glossary": "Demand and pagination terms: settlement is a promise becoming fulfilled or rejected; qualify what settled. Architecture says scheduler dependency order waits on queued jobs in the same context, while source demand/ordered-load settlement barriers are separate.", + "recommendation": "Name the graph cut explicitly, e.g. \"runs the joined graph once after both parent graphs publish in the same scheduler context\"; rename the line-507 test to \"publishes dependent live-query rows before reporting an earlier source-listener failure\". Retain settlement wording where an actual preload or other promise is asserted, such as the test at line 860.", + "relatedPaths": [ + "packages/db/src/query/live/ARCHITECTURE.md" + ], + "auditPath": "packages/db/tests/query/scheduler.test.ts", + "auditLine": 1159, + "auditEndLine": 1159, + "currentPath": "packages/db/tests/query/scheduler-oracle.test.ts", + "resolution": "Scheduler descriptions name graph publication. Actual promise observations retain settlement terminology.", + "status": "resolved within terminology scope" + }, + { + "id": "06_ivm-1", + "severity": "medium", + "kind": "missing-mapping", + "term": "messages / messageCount versus weighted delta", + "evidence": "`addMessage(message: MultiSet) { this.messages.push(...message.getInner()) }` and `messageCount: this.messages.length` count flattened [row, weight] records. One MultiSet containing three weighted deltas counts as three messages, and an empty MultiSet counts as zero. In contrast, d2-work-oracle.test.ts:59 and topKWithFractionalIndex.test.ts:1203-1204 preserve each MultiSet envelope; the latter expects one empty message at line 1219. The same term therefore denotes different work observations across these oracles. KeyedMessageTracker repeats the mapping at lines 147-165.", + "glossary": "Live-query materialization terms: weighted delta = a positive or negative change to a relation row. Runtime terms distinguish a Collection change message. Model alignment rules 2 and 3 require an explicit mapping when a model combines or splits production concepts.", + "recommendation": "Rename the flattened fields to weightedDeltas/deltaCount (and the corresponding work assertions), or explicitly document that these model fields count flattened weighted deltas, not D2 MultiSet message envelopes or Collection change messages. Keep envelope counts separate where empty messages matter.", + "relatedPaths": [ + "packages/db-ivm/tests/operators/topKWithFractionalIndex.test.ts", + "packages/db-ivm/tests/operators/topk-relation-oracle.ts", + "packages/db-ivm/tests/d2-work-oracle.test.ts" + ], + "auditPath": "packages/db-ivm/tests/test-utils.ts", + "auditLine": 123, + "auditEndLine": 136, + "currentPath": "packages/db-ivm/tests/test-utils.ts", + "resolution": "Flattened D2 tracker entries use weightedDeltas and deltaCount. Actual MultiSet envelopes remain messages.", + "status": "resolved within terminology scope" + }, + { + "id": "06_ivm-2", + "severity": "low", + "kind": "ambiguous-term", + "term": "published", + "evidence": "`function published(index: Index, key: string)` only iterates `index.get(key)` and sums returned multiplicities. The driver invokes it immediately after `index.addValue` at lines 194-198. It observes an internal Index and never crosses a Collection publication boundary.", + "glossary": "Runtime terms: publication is the boundary making one coherent result observable to reads, events and downstream queries; public snapshot is the coherent set exposed there. Oracle terms call a recorded fact an observation.", + "recommendation": "Rename the helper to observedMultiplicities or indexContents. Keep the checkpoint wording `after addition`; this owner does not need a publication concept.", + "relatedPaths": [], + "auditPath": "packages/db-ivm/tests/index-refinement-oracle.property.test.ts", + "auditLine": 179, + "auditEndLine": 188, + "currentPath": "packages/db-ivm/tests/index-refinement-oracle.property.test.ts", + "resolution": "The Index oracle names its direct reads observedMultiplicities.", + "status": "resolved within terminology scope" + }, + { + "id": "06_ivm-3", + "severity": "low", + "kind": "missing-mapping", + "term": "settled local-snapshot cut", + "evidence": "The contract says `two public Collection rows at the settled local-snapshot cut`. The actual driver awaits `collection.preload()` at line 144 and reads `collection.currentStateAsChanges(...)` at lines 150-165. The named cut does not identify which promise settled, and local snapshot does not map to the glossary public snapshot despite reading the public Collection rows.", + "glossary": "Runtime terms: public snapshot is the last coherent set of rows exposed to reads/events/downstream queries. Demand terms: settlement is a promise becoming fulfilled or rejected and must be qualified; it is not publication or success. Model alignment requires canonical shared concepts or an explicit abstraction mapping.", + "recommendation": "Say `the ordered public snapshot read after collection.preload() fulfills` and name the currentStateAsChanges return as the comparison cut. Update the nearby local-snapshot comment/test name, or explicitly define that shorthand once.", + "relatedPaths": [], + "auditPath": "packages/db/tests/cursor.property.test.ts", + "auditLine": 25, + "auditEndLine": 28, + "currentPath": "packages/db/tests/cursor-oracle.property.test.ts", + "resolution": "The snapshot owner names the read after preload fulfillment and the currentStateAsChanges observation cut.", + "status": "resolved within terminology scope" + }, + { + "id": "06_ivm-4", + "severity": "low", + "kind": "glossary-gap", + "term": "structural equality / value identity", + "evidence": "The deepEquals owner promises `structural equality`; its lines 942-945 explicitly ignore Map/Set insertion order, RegExp lastIndex and array holes for change-event suppression. The hash-identity owner also calls its concept `structural equality` (line 14), but lines 30-34 retain all three distinctions. Both owners locally state their intended rules, so this is an absent shared vocabulary distinction rather than evidence that either oracle computes the wrong answer.", + "glossary": "The glossary introduction owns terms crossing subsystem boundaries and requires one term for one concept, but contains no entries distinguishing D2 structural value identity, deepEquals change-event equality, or draft equality.", + "recommendation": "Add qualified glossary entries such as D2 value identity and change-event equality, linked to their owners; require qualified headings when discussing both. Preserve the differing product semantics and the independent models. Include draft equality through its owning reviewer if the portfolio consolidates this gap.", + "relatedPaths": [ + "packages/db-ivm/tests/hash-identity-oracle.property.test.ts", + "packages/db-ivm/tests/hash.property.test.ts", + "packages/db-ivm/tests/operators/topk-batch-contract.test.ts", + "packages/db/tests/proxy-revert-oracle.property.test.ts" + ], + "auditPath": "packages/db/tests/utils.property.test.ts", + "auditLine": 7, + "auditEndLine": 10, + "currentPath": "packages/db/tests/utils-oracle.property.test.ts", + "resolution": "The glossary and oracle headings distinguish D2 value identity, change-event equality, and draft equality.", + "status": "resolved within terminology scope" + }, + { + "id": "07_query_adapter-4", + "severity": "medium", + "kind": "missing-mapping", + "term": "query-succeeded / refresh-succeeded / applied", + "evidence": "An applicable query-succeeded action with no deferOn immediately sets phase: terminal and outcome: applied (183–193); refresh-succeeded does the same (162–173). The opening mapping only says applicable combines validation and the ability to materialize (57–60). The empty-result driver submits query-succeeded after awaiting public refetch (3680–3691), so the action represents a compressed successful fetch plus completed result application, rather than Query fetch fulfillment alone.", + "glossary": "Demand graph and grammar distinguish provider completion, settlement, sync application, and publication. Model alignment rule requires an abstraction that combines production concepts to list what it combines.", + "recommendation": "Add an explicit mapping beside ResultSettlementModelAction: for a nondeferred applicable result, these actions combine Query fetch fulfillment, completed Collection application/applied settlement, and the terminal public-refetch checkpoint; deferred/invalid variants instead describe their named alternative outcomes. Alternatively give the compressed action a name that states that stronger boundary. Keep the independent coarse model.", + "relatedPaths": [], + "auditPath": "packages/query-db-collection/tests/ownership-lifecycle.oracle.test.ts", + "auditLine": 183, + "auditEndLine": 193, + "currentPath": "packages/query-db-collection/tests/ownership-lifecycle.oracle.test.ts", + "resolution": "Compressed result-settlement actions explicitly combine Query fetch fulfillment, Collection application, and the terminal refetch checkpoint.", + "status": "resolved within terminology scope" + }, + { + "id": "07_query_adapter-2", + "severity": "low", + "kind": "ambiguous-term", + "term": "acquisition", + "evidence": "“shares acquisitions across readers without letting one abort cancel its peer” drives two createCursorPager readers against a shared QueryClient and fetchPage transport. No Collection, loadSubset attempt, or acquisition lease exists in this history. In the implementation the local acquisition variable is query.promise (src/cursor-pagination.ts:181), whereas the adjacent load-subset lifecycle oracle explicitly models two acquisition leases sharing one Query transport (lines 1060–1140).", + "glossary": "Demand and pagination terms: physical acquisition is request-scoped work accepted by a sync adapter; transport is provider work and need not map one-to-one to a physical acquisition. Model alignment requires explicit mappings for different or combined concepts.", + "recommendation": "Use shared Query fetch consistently in pager-only oracle prose and test names. The README already qualifies this local acquisition concept and excludes cross-owner cancellation leases, so no new reference-model mapping is required.", + "relatedPaths": [ + "packages/query-db-collection/tests/cursor-pagination/README.md", + "packages/query-db-collection/tests/cursor-pagination.oracle.test.ts", + "packages/query-db-collection/tests/cursor-pagination.publication-oracle.test.ts", + "packages/query-db-collection/tests/cursor-pagination.boundary-oracle.test.ts", + "packages/query-db-collection/src/cursor-pagination.ts", + "packages/query-db-collection/tests/load-subset-lifecycle-oracle.test.ts" + ], + "adjudication": "The README already qualifies Query shared acquisition and excludes cross-owner cancellation leases. Standardize its local name, without claiming the conceptual mapping is absent.", + "auditPath": "packages/query-db-collection/tests/cursor-pagination.cache-oracle.test.ts", + "auditLine": 575, + "auditEndLine": 598, + "currentPath": "packages/query-db-collection/tests/cursor-pagination.cache-oracle.test.ts", + "resolution": "Pager owners use Query fetch for work that does not establish the acquisition leases modeled by lifecycle owners.", + "status": "resolved within terminology scope" + }, + { + "id": "07_query_adapter-3", + "severity": "low", + "kind": "ambiguous-term", + "term": "sync generations", + "evidence": "“sync generations order competing results” does not identify its clock. The same-run histories include overlapping defer barriers (4892–5026) and post-write result authority (4067–4182); production advances postWriteRefetchGenerations when a write demands newer authority (src/query.ts:966–983), independently of cleanup/restart. Calling those result-ordering clocks sync generations invites a sync-run interpretation.", + "glossary": "generation must qualify the clock it fences, such as sync-run or post-write-refetch generation. sync run is one sync-function invocation and is not a synonym for generation.", + "recommendation": "Replace the umbrella phrase sync generations with qualified sync-run, post-write-refetch and defer-result authorities as appropriate. Preserve the existing local mappings and independent model.", + "relatedPaths": [ + "packages/query-db-collection/src/query.ts" + ], + "adjudication": "Local defer and post-write clock mappings are present. The umbrella phrase sync generations is the remaining qualification problem.", + "auditPath": "packages/query-db-collection/tests/ownership-lifecycle.oracle.test.ts", + "auditLine": 45, + "auditEndLine": 47, + "currentPath": "packages/query-db-collection/tests/ownership-lifecycle.oracle.test.ts", + "resolution": "Ownership prose qualifies sync-run fences, post-write-refetch generations, and defer-result barriers.", + "status": "resolved within terminology scope" + }, + { + "id": "07_query_adapter-5", + "severity": "low", + "kind": "meaning-conflict", + "term": "settled", + "evidence": "“let settled = false; void refetch.then(() => { settled = true })” tracks fulfillment only; rejection leaves settled false. The cursor integration owner similarly increments settled only in fulfillment handlers (445–450). Both use that value at a pending-work checkpoint, even though glossary settlement includes rejection.", + "glossary": "settlement is a promise becoming fulfilled or rejected; grammar says a promise settles, fulfills, or rejects.", + "recommendation": "Rename these local observations to fulfilled / fulfilledPreloads if they intentionally measure success. If the assertion intends to establish that the promises are still pending, record both fulfillment and rejection with an explicit outcome.", + "relatedPaths": [ + "packages/query-db-collection/tests/cursor-pagination.integration.test.ts" + ], + "auditPath": "packages/query-db-collection/tests/load-subset-lifecycle-oracle.test.ts", + "auditLine": 603, + "auditEndLine": 606, + "currentPath": "packages/query-db-collection/tests/load-subset-lifecycle-oracle.test.ts", + "resolution": "Fulfillment-only flags and preload counters use fulfilled terminology.", + "status": "resolved within terminology scope" + }, + { + "id": "08_providers-1", + "severity": "medium", + "kind": "meaning-conflict", + "term": "session / sync run", + "evidence": "The test says \"discards tag state when the same collection starts a fresh session\", then executes \"await collection.cleanup()\" and \"collection.startSyncImmediate()\". This is exactly termination of one local sync run and restart of the same Collection. ORACLE_MUTATIONS.md:117-118 says \"every sync session\" and :203-205 says \"retire a session\" / \"new-session waiters\" for the same local ownership boundary.", + "glossary": "Runtime terms: sync run is one Collection sync-function invocation and its resources until cleanup invalidates them; provider session is a provider-defined remote lifetime, always provider-qualified, and is not a synonym for sync run.", + "recommendation": "Use \"new sync run\" in the test, \"every sync run\" in the ledger and \"retire a sync run\" / \"replacement-sync-run waiters\" for callback reentry. Preserve Electric provider stream terminology for actual remote/SDK resources.", + "relatedPaths": [ + "packages/electric-db-collection/tests/ORACLE_MUTATIONS.md", + "packages/electric-db-collection/tests/electric-oracle.property.test.ts" + ], + "auditPath": "packages/electric-db-collection/tests/electric-descriptor-isolation.test.ts", + "auditLine": 867, + "auditEndLine": 873, + "currentPath": "packages/electric-db-collection/tests/electric-descriptor-isolation-oracle.test.ts", + "resolution": "Local Collection lifetimes use sync run. Provider session remains provider-qualified.", + "status": "resolved within terminology scope" + }, + { + "id": "08_providers-2", + "severity": "medium", + "kind": "meaning-conflict", + "term": "settled", + "evidence": "\"let settled = false\" becomes true only in \".then(() => { settled = true })\" on applyTransaction. The same fulfillment-only observation occurs at :476-481. A rejected promise is settled by the glossary but leaves these variables false. The file itself uses the genuinely two-outcome \"persistenceState.settled\" at :761-770 with .finally(), so the same name represents two ideas within one oracle.", + "glossary": "Demand and pagination terms / Grammar rules: settlement means a promise becoming fulfilled or rejected; use success only for fulfillment.", + "recommendation": "Rename the two success-only flags to \"persistenceFulfilled\" (and corresponding assertions), or use an explicit pending/fulfilled/rejected outcome observer if the intended observation really is settlement. Leave the .finally()-based settlement flag named settled.", + "relatedPaths": [], + "auditPath": "packages/powersync-db-collection/tests/correctness-oracle.test.ts", + "auditLine": 308, + "auditEndLine": 315, + "currentPath": "packages/powersync-db-collection/tests/correctness-oracle.test.ts", + "resolution": "Fulfillment-only persistence flags use persistenceFulfilled. Both-outcome observations retain settled.", + "status": "resolved within terminology scope" + }, + { + "id": "08_providers-3", + "severity": "medium", + "kind": "ambiguous-term", + "term": "commit", + "evidence": "SchedulerEvent has \"| `commit`\", whose driver creates \"const commit = createDeferred()\" (:2623) and awaits \"commit.promise\" before invoking the persistence adapter applyCommittedTx (:2638-2641). In the same file, designMessage instead maps \"token.operation === `commit`\" to the Electric \"upToDate\" message (:2219-2222). Thus the same grammar action names persistence-write release and provider up-to-date delivery, neither of which alone denotes Collection sync application/publication.", + "glossary": "Runtime terms distinguish sync transaction, publication and provider completion; demand graph distinguishes commit(sync transaction), applied receipt and publication. Model alignment requires naming a differing abstraction and declaring what production concepts it combines or splits.", + "recommendation": "Name the scheduler action \"release-persistence-apply\" (or equally precise persistence-qualified wording) and the design action \"deliver-up-to-date\". Add one short mapping that provider controls may cause sync commit, whose applied receipt and later durability are separate boundaries. Do not change the independent state computations.", + "relatedPaths": [], + "auditPath": "packages/electric-db-collection/tests/electric-oracle.property.test.ts", + "auditLine": 2162, + "auditEndLine": 2167, + "currentPath": "packages/electric-db-collection/tests/electric-oracle.property.test.ts", + "resolution": "Electric actions name deliver-up-to-date, deliver-subset-end, and release-persistence-apply at their actual boundaries.", + "status": "resolved within terminology scope" + }, + { + "id": "09_sqlite_core-2", + "severity": "medium", + "kind": "meaning-conflict", + "term": "restart", + "evidence": "\"Same-schema restarts preserve metadata\" and \"same-schema restart ... restarts on either side\". observeResetResumeHistory instead constructs a new SQLiteCorePersistenceAdapter on the same harness.driver (457–462, repeated at 485–493); it creates no Collection and ends or starts no sync run. The observation is nevertheless named after-persistence-transition-restart (521).", + "glossary": "Runtime terms: restart is starting a new sync run after the prior sync run has ended.", + "recommendation": "Use adapter reopen/reconstruction in the law, test title, local restarted variable, and checkpoint (for example after-persistence-transition-reopen). If a process restart abstraction is intended, explicitly state that adapter reconstruction models only persistence reopening and does not cover the Collection restart boundary.", + "relatedPaths": [], + "auditPath": "packages/db-sqlite-persistence-core/tests/sqlite-core-adapter.test.ts", + "auditLine": 1787, + "auditEndLine": 1795, + "currentPath": "packages/db-sqlite-persistence-core/tests/sqlite-core-adapter-oracle.test.ts", + "resolution": "Reconstruction of a persistence adapter uses adapter reopen, separate from Collection restart.", + "status": "resolved within terminology scope" + }, + { + "id": "09_sqlite_core-1", + "severity": "low", + "kind": "ambiguous-term", + "term": "applied receipt / wrapped sync receipt", + "evidence": "\"Publication precedes durability, but a rejected durability boundary must reject its applied receipt\". The witness at 4112–4123 observes receipt pending while the row is public; 4154–4164 observes that receipt rejected while the same row remains public and is not durable. Production separately awaits core application and persistence before resolveApplied (src/persisted.ts:2736–2758). Thus this receipt combines application and durable persistence rather than denoting the glossary application boundary alone.", + "glossary": "Demand and pagination terms: applied receipt settles when the sync transaction writes/events become visible, or rejects if abandoned; settlement is not application/publication. Model alignment rule requires a declared mapping for combined concepts.", + "recommendation": "Use wrapped sync receipt in the opening where the wrapper waits for both core application and durability. The stronger behavior is already declared and tested. Preserve its API type and assertions; a new glossary term is optional.", + "relatedPaths": [ + "packages/db-sqlite-persistence-core/src/persisted.ts", + "packages/db/src/types.ts", + "docs/contributing/glossary.md" + ], + "adjudication": "The stronger application-plus-durability behavior is explicitly declared and called wrapped sync receipt in its witness. The introduction should use that qualified name; a new glossary entry is not required.", + "auditPath": "packages/db-sqlite-persistence-core/tests/persisted.test.ts", + "auditLine": 63, + "auditEndLine": 65, + "currentPath": "packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts", + "resolution": "The persistence owner calls its durability-qualified result a wrapped sync receipt.", + "status": "resolved within terminology scope" + }, + { + "id": "09_sqlite_core-3", + "severity": "low", + "kind": "ambiguous-term", + "term": "network readiness", + "evidence": "\"Initial rendering prefers network readiness\"; the test \"uses network readiness before the fallback deadline\" (710) implements markNetworkReady = markReady (723–725) and finally asserts persisted.status is ready (748). The actual evidence is Collection readiness signaled by the upstream adapter, not a separate network-readiness state.", + "glossary": "Runtime terms: readiness must name the consumer (Collection, subscription, initial-query); network-first initial render prefers Collection readiness before its fallback condition.", + "recommendation": "Say upstream-signaled Collection readiness in the contract and test title, then use Collection readiness consistently. Rename markNetworkReady to markCollectionReady or add a first-use explicit mapping; retain network failure and the public network-first strategy name.", + "relatedPaths": [], + "auditPath": "packages/db-sqlite-persistence-core/tests/persisted-readiness-oracle.test.ts", + "auditLine": 148, + "auditEndLine": 149, + "currentPath": "packages/db-sqlite-persistence-core/tests/persisted-readiness-oracle.test.ts", + "resolution": "The owner qualifies upstream-signaled Collection readiness and its preload observations.", + "status": "resolved within terminology scope" + }, + { + "id": "10_browser_sqlite-1", + "severity": "medium", + "kind": "missing-mapping", + "term": "same lease / public acquisition", + "evidence": "The test says 'reacquires the same Browser lease after leadership retirement', but explicitly calls releaseInboundRemoteSubsetAcquisitions, observes one unload, ensures again, and expects two loads/two unloads (5588–5597). Electron repeats this at 3135–3164. The composed owner calls the retained operation 'one public acquisition' (1876; 1956–1957), while its ledger says leadership transfer 'releases retired-owner acquisitions and establishes replacements only for surviving demand' (1656–1658). The nested Collection-level claim and owner-level physical acquisitions are not explicitly mapped, so 'same lease' appears to survive a boundary that already fulfilled its release obligation.", + "glossary": "Demand and pagination: demand may outlive/replace physical work; each accepted physical acquisition creates an acquisition lease requiring one release. A lease must name its resource. Model alignment requires explicit mappings for combined/split concepts.", + "recommendation": "Rename the retirement tests to 'establishes a replacement acquisition for retained demand after leadership retirement'. Add one shared explanation distinguishing the still-retained Collection/persisted-adapter claim from the old and replacement remote-owner acquisitions and their separate acquisition leases. Qualify 'public acquisition' with that layer if it intentionally names the outer acquisition.", + "relatedPaths": [ + "packages/electron-db-sqlite-persistence/tests/electron-ipc.test.ts", + "packages/browser-db-sqlite-persistence/tests/per-collection-coordinator-oracle.test.ts" + ], + "auditPath": "packages/browser-db-sqlite-persistence/tests/browser-coordinator.test.ts", + "auditLine": 5569, + "auditEndLine": 5597, + "currentPath": "packages/browser-db-sqlite-persistence/tests/browser-coordinator-oracle.test.ts", + "resolution": "Mappings distinguish outer Collection acquisition, retained coordinator demand, and remote-owner acquisition leases.", + "status": "resolved within terminology scope" + }, + { + "id": "10_browser_sqlite-2", + "severity": "low", + "kind": "ambiguous-term", + "term": "publications", + "evidence": "`let publications = 0` increments when observeBroadcastMessage sees a `tx:committed` transport payload. The same observation recurs at 1614–1620 and 3134–3140. It measures coordinator message emission, without observing Collection reads/events/downstream-query coherence. The literal coordinator.publish API is valid; the unqualified observation name is the ambiguity.", + "glossary": "Runtime: publication is the boundary that makes one coherent result observable to reads, events, and downstream queries; it is not sync commit by itself. Overloaded observations must be qualified.", + "recommendation": "Rename these counters to committedMessagePosts (or coordinatorBroadcasts) and retain publish as the actual API name. If publication is used in prose for the transport API, explicitly say 'coordinator message publication' and distinguish it from Collection publication.", + "relatedPaths": [], + "auditPath": "packages/browser-db-sqlite-persistence/tests/browser-coordinator.test.ts", + "auditLine": 656, + "auditEndLine": 668, + "currentPath": "packages/browser-db-sqlite-persistence/tests/browser-coordinator-oracle.test.ts", + "resolution": "Transport counters use committedMessagePosts for tx:committed broadcasts.", + "status": "resolved within terminology scope" + }, + { + "id": "10_browser_sqlite-3", + "severity": "low", + "kind": "missing-mapping", + "term": "subsetDemandSettled", + "evidence": "`Promise.resolve(collection._sync.loadSubset(subsetDemand)).then(() => collection._sync.unloadSubset(subsetDemand)).catch(...).finally(() => { reach.subsetDemandSettled = true })` marks settlement of a combined load, success-path unload, and error-handling chain. The spec waits on this marker at line 190. A logical demand itself does not settle, and this is a later cut than settlement of the load promise alone.", + "glossary": "Demand is a logical need; demand retirement and acquisition release are separate transitions. Settlement means a named promise becoming fulfilled or rejected. Model-only combined observations must list their components.", + "recommendation": "Name the marker subsetLoadAndReleaseChainSettled and document that it is a fixture-only checkpoint after the load plus success-path unload/error handling. If the desired observation is load-promise settlement alone, record that separately rather than implying the logical demand settles.", + "relatedPaths": [ + "packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle.opfs.spec.ts" + ], + "auditPath": "packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle.opfs.ts", + "auditLine": 317, + "auditEndLine": 325, + "currentPath": "packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle-oracle.opfs.ts", + "resolution": "The browser reach marker names the entire subset-load-and-release promise chain.", + "status": "resolved within terminology scope" + }, + { + "id": "11_offline-1", + "severity": "medium", + "kind": "missing-mapping", + "term": "success / application / caller fulfillment", + "evidence": "The opening declares that the README authorizes 'Success applies its server rows and, after outbox removal, fulfills both promises.' In this driver the mutationFn explicitly calls env.applyMutations (line 184); harness.ts updates serverState and submits a sync transaction. README.md:218–222 instead says successful settlement establishes mutationFn return plus acknowledged deletion, and establishes server confirmation or exposure only if mutationFn waits for that provider observation. The model's coupled application-and-fulfillment premise is not identified as the fixture's stronger premise in the opening law.", + "glossary": "Demand and pagination: settlement is promise fulfillment or rejection, not application. Runtime: publication is not provider return/request settlement. Offline outbox: deletion-pending follows mutation-function fulfillment, not a universal server-application guarantee.", + "recommendation": "State the general boundary as mutationFn fulfillment plus acknowledged outbox deletion. Add an explicit model mapping: in this fixture, successful mutationFn also updates the modeled server rows and submits their sync writes before returning; the row assertions are conditional on that controlled behavior.", + "relatedPaths": [ + "packages/offline-transactions/tests/harness.ts", + "packages/offline-transactions/README.md", + "packages/offline-transactions/src/executor/TransactionExecutor.ts" + ], + "auditPath": "packages/offline-transactions/tests/transaction-settlement.property.test.ts", + "auditLine": 18, + "auditEndLine": 22, + "currentPath": "packages/offline-transactions/tests/transaction-settlement-oracle.property.test.ts", + "resolution": "Caller success requires mutationFn fulfillment and acknowledged outbox deletion. The fixture states its stronger server-application premise.", + "status": "resolved within terminology scope" + }, + { + "id": "11_offline-2", + "severity": "low", + "kind": "meaning-conflict", + "term": "FaultInjection", + "evidence": "'type FaultInjection = { commandIndex: number; apply: (snapshot: Snapshot) => Snapshot }'. At lines 506–509 the scheduler has already run, and the helper replaces its recorded observation immediately before assertSnapshot. Examples set runningCount to 2 or fabricate a next transaction. These are deliberate wrong answers used to calibrate the oracle, not runtime failure/delay/cancellation/malformed-provider-response injection.", + "glossary": "Oracle and model terms: mutant is a deliberate wrong answer or implementation used to test oracle sensitivity; fault injection is a deliberate runtime failure, delay, cancellation, or malformed response testing a system boundary.", + "recommendation": "Rename this helper ObservationMutant (or WrongObservation) and call its cases observation mutants / wrong-result controls. Keep runtime fault injection for failures applied to the actual system boundary.", + "relatedPaths": [], + "auditPath": "packages/offline-transactions/tests/KeyScheduler.property.test.ts", + "auditLine": 383, + "auditEndLine": 390, + "currentPath": "packages/offline-transactions/tests/KeyScheduler-oracle.property.test.ts", + "resolution": "Snapshot substitutions use ObservationMutant and observationMutant.", + "status": "resolved within terminology scope" + }, + { + "id": "11_offline-3", + "severity": "low", + "kind": "glossary-gap", + "term": "offline executor restart / outbox replay", + "evidence": "Both outbox phase definitions say 'A restart...' while the glossary's sole restart entry at line 39 means starting a new Collection sync run. The serializer oracle says 'A restart has different object and collection IDs' (transaction-serializer.property.test.ts:186) and constructs new Collections. The settlement oracle disposes one executor and constructs a fresh test environment with the same storage (transaction-settlement.property.test.ts:1360–1375). Its restored marked rows retry deletion without invoking mutationFn. This is a separate offline lifecycle, not Collection sync restart or oracle seed/path replay.", + "glossary": "Runtime restart is a new sync run after the previous run ends; oracle replay reruns a recorded failure. Offline outbox currently defines phase markers but does not name fresh-executor recovery or persisted-outbox replay.", + "recommendation": "Add qualified offline terms: 'offline executor restart' for constructing a fresh executor over retained durable outbox state, and 'outbox replay' for loading/reconciling that state and resuming permitted work. Use 'offline executor restart' in phase definitions and 'application restart' / 'fresh serializer and Collection reconstruction' where the serialization fixture simulates process recreation. Reserve oracle replay for recorded test reproduction.", + "relatedPaths": [ + "packages/offline-transactions/tests/transaction-serializer.property.test.ts", + "packages/offline-transactions/tests/transaction-settlement.property.test.ts", + "packages/offline-transactions/tests/leadership-replay.property.test.ts", + "packages/offline-transactions/tests/connectivity-replay-oracle.test.ts" + ], + "auditPath": "docs/contributing/glossary.md", + "auditLine": 60, + "auditEndLine": 61, + "currentPath": "docs/contributing/glossary.md", + "resolution": "The glossary and local mappings distinguish offline executor restart, outbox replay, serializer reconstruction, and Collection lifecycle.", + "status": "resolved within terminology scope" + }, + { + "id": "11_offline-4", + "severity": "low", + "kind": "ambiguous-term", + "term": "resolved", + "evidence": "The outcome enum is '`pending` | `resolved` | `rejected`', and only the fulfillment callback of promise.then assigns 'resolved'. This observation means fulfilled, while promise resolution in JavaScript can also adopt an unsettled promise. Other offline owners use fulfilled for the same observed outcome.", + "glossary": "Grammar rules: a promise settles; it fulfills or rejects. Settlement is fulfillment or rejection.", + "recommendation": "Use fulfilled for the local outcome state and matching expectation at line 95. Preserve actual IndexedDB event names onsuccess/oncomplete and public API method names.", + "relatedPaths": [ + "packages/offline-transactions/tests/connectivity-replay-oracle.test.ts", + "packages/offline-transactions/tests/transaction-settlement.property.test.ts" + ], + "auditPath": "packages/offline-transactions/tests/indexeddb-write-settlement.test.ts", + "auditLine": 51, + "auditEndLine": 57, + "currentPath": "packages/offline-transactions/tests/indexeddb-write-settlement-oracle.test.ts", + "resolution": "The IndexedDB promise outcome uses fulfilled instead of resolved.", + "status": "resolved within terminology scope" + }, + { + "id": "12_frameworks-1", + "severity": "low", + "kind": "missing-mapping", + "term": "demand generation", + "evidence": "\"changing the query, collection, or page shape creates a new demand generation.\" This is the shared model description, but no demand-generation token is modeled or mapped. It groups input Collection/query identity replacement with reactive page-option updates; the latter preserves committed page depth (infinite-suite.ts:895-919). The receiving controller fences window operations with windowGeneration (packages/db/src/live-query-window-controller.ts:851).", + "glossary": "Demand and pagination: generation is a monotonic token that rejects obsolete asynchronous work and must name the clock it fences; demand is the logical need for source data. Model alignment rules 2 and 4 require explicit mapping of combined or model-only concepts.", + "recommendation": "Replace \"creates a new demand generation\" with direct observable wording about replacing input identity or requesting the updated window; alternatively declare this a model-only input epoch and map query/Collection replacement separately from window-operation generations and retained page depth.", + "relatedPaths": [ + "packages/db/tests/conformance/infinite-suite.ts", + "packages/react-db/tests/infinite-query-conformance.test.tsx", + "packages/vue-db/tests/infinite-query-conformance.test.ts", + "packages/svelte-db/tests/infinite-query-conformance.svelte.test.ts" + ], + "auditPath": "packages/db/tests/conformance/infinite-contract.ts", + "auditLine": 4, + "auditEndLine": 6, + "currentPath": "packages/db/tests/conformance/infinite-contract-oracle.ts", + "resolution": "The shared infinite-query contract names input identity replacement and window/page changes without inventing a generation clock.", + "status": "resolved within terminology scope" + }, + { + "id": "12_frameworks-2", + "severity": "low", + "kind": "meaning-conflict", + "term": "settled / fulfillment", + "evidence": "\"A settled page extends the committed prefix\" is implemented as \"const pageCount = pageSucceeded ? 2 : 1\"; rejected work also settles but does not advance this prefix. The same file calls a fulfillment-only observation \"settled\" at lines 518-520: \"let settled = false\" followed by a fulfillment-only .then setting it true. The failure-preservation rules and assertions are otherwise explicit, so this is vocabulary drift, not an established behavior defect.", + "glossary": "Demand and pagination: settlement is a Promise becoming fulfilled or rejected, not success. Grammar rules: use settled for either outcome and success only for fulfillment.", + "recommendation": "Say \"A fulfilled page fetch advances the committed prefix\" and rename the local fulfilled-only flag to fetchFulfilled. If the intended observation truly is settlement, record both fulfillment and rejection while retaining the outcome separately.", + "relatedPaths": [], + "auditPath": "packages/db/tests/conformance/infinite-suite.ts", + "auditLine": 33, + "auditEndLine": 45, + "currentPath": "packages/db/tests/conformance/infinite-suite-oracle.ts", + "resolution": "The page-prefix rule names fulfilled page fetches. The historical scenario key remains stable.", + "status": "resolved within terminology scope" + } + ], + "oracleOwnersAndDedicatedCompanions": [ + "packages/angular-db/tests/conformance-oracle.test.ts", + "packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle-oracle.opfs.spec.ts", + "packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle-oracle.opfs.ts", + "packages/browser-db-sqlite-persistence/e2e/electric-immediate-reload-oracle.opfs.spec.ts", + "packages/browser-db-sqlite-persistence/e2e/electric-immediate-reload-oracle.opfs.ts", + "packages/browser-db-sqlite-persistence/e2e/electric-resume-two-tab-oracle.opfs.spec.ts", + "packages/browser-db-sqlite-persistence/e2e/electric-resume-two-tab-oracle.opfs.ts", + "packages/browser-db-sqlite-persistence/e2e/open-timeout-oracle.opfs.spec.ts", + "packages/browser-db-sqlite-persistence/e2e/remote-subset-two-tab-oracle.opfs.spec.ts", + "packages/browser-db-sqlite-persistence/e2e/remote-subset-two-tab-oracle.opfs.ts", + "packages/browser-db-sqlite-persistence/e2e/shared-driver-fairness-oracle.opfs.spec.ts", + "packages/browser-db-sqlite-persistence/e2e/shared-driver-fairness-oracle.opfs.ts", + "packages/browser-db-sqlite-persistence/tests/browser-coordinator-oracle.test.ts", + "packages/browser-db-sqlite-persistence/tests/opfs-page-lifecycle-oracle.test.ts", + "packages/browser-db-sqlite-persistence/tests/opfs-worker-diagnostics-oracle.test.ts", + "packages/browser-db-sqlite-persistence/tests/per-collection-coordinator-oracle.test.ts", + "packages/browser-db-sqlite-persistence/tests/shared-driver-fairness-oracle.test.ts", + "packages/browser-db-sqlite-persistence/tests/shared-driver-fairness-oracle.ts", + "packages/capacitor-db-sqlite-persistence/e2e/capacitor-persisted-collection-conformance-suite-oracle.ts", + "packages/capacitor-db-sqlite-persistence/e2e/shared/register-persisted-collection-conformance-suite-oracle.ts", + "packages/db-collection-e2e/src/fixtures/persisted-conformance-manifest-oracle.ts", + "packages/db-collection-e2e/src/suites/collation-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/deduplication-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/joins-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/live-updates-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/moves-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/mutations-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/pagination-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/predicates-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/progressive-oracle.suite.ts", + "packages/db-collection-e2e/src/utils/assertions-oracle.ts", + "packages/db-collection-e2e/src/utils/notification-laws-oracle.ts", + "packages/db-ivm/tests/d2-work-oracle.test.ts", + "packages/db-ivm/tests/hash-failure-retry-oracle.property.test.ts", + "packages/db-ivm/tests/hash-graph-oracle.property.test.ts", + "packages/db-ivm/tests/hash-identity-oracle.property.test.ts", + "packages/db-ivm/tests/hash-mixed-graph-oracle.property.test.ts", + "packages/db-ivm/tests/hash-oracle.property.test.ts", + "packages/db-ivm/tests/hash-session-oracle.ts", + "packages/db-ivm/tests/hash-session-replay-oracle.test.ts", + "packages/db-ivm/tests/hash-work-oracle.test.ts", + "packages/db-ivm/tests/incrementalization-law-oracle.property.test.ts", + "packages/db-ivm/tests/incrementalization-law-oracle.ts", + "packages/db-ivm/tests/index-refinement-oracle.property.test.ts", + "packages/db-ivm/tests/multiset-consolidate-oracle.property.test.ts", + "packages/db-ivm/tests/operators/topKWithFractionalIndex-oracle.test.ts", + "packages/db-ivm/tests/operators/topKWithIndex-oracle.test.ts", + "packages/db-ivm/tests/operators/topk-batch-contract-oracle.test.ts", + "packages/db-ivm/tests/operators/topk-relation-oracle.test.ts", + "packages/db-ivm/tests/operators/topk-relation-oracle.ts", + "packages/db-ivm/tests/operators/topk-support-window-oracle.test.ts", + "packages/db-ivm/tests/temporal-group-key-oracle.test.ts", + "packages/db-sqlite-persistence-core/tests/contracts/driver-admission-laws-oracle.ts", + "packages/db-sqlite-persistence-core/tests/contracts/persisted-collection-conformance-contract-oracle.ts", + "packages/db-sqlite-persistence-core/tests/contracts/runtime-bridge-e2e-contract-oracle.ts", + "packages/db-sqlite-persistence-core/tests/contracts/runtime-persistence-contract-oracle.ts", + "packages/db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract-oracle.ts", + "packages/db-sqlite-persistence-core/tests/contracts/sqlite-driver-contract-oracle.ts", + "packages/db-sqlite-persistence-core/tests/persisted-options-type-oracle.test-d.ts", + "packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts", + "packages/db-sqlite-persistence-core/tests/persisted-readiness-oracle.test.ts", + "packages/db-sqlite-persistence-core/tests/persisted-real-adapter-lifecycle-oracle.test.ts", + "packages/db-sqlite-persistence-core/tests/runtime-restart-checker-oracle.test.ts", + "packages/db-sqlite-persistence-core/tests/shared-logical-scheduling-oracle.test.ts", + "packages/db-sqlite-persistence-core/tests/sqlite-core-adapter-oracle.test.ts", + "packages/db-sqlite-persistence-core/tests/sqlite-driver-admission-laws-oracle.test.ts", + "packages/db-sqlite-persistence-core/tests/sqlite-resume-snapshot-oracle.test.ts", + "packages/db/tests/SortedMap-oracle.test.ts", + "packages/db/tests/adapter-schema-transform-conformance-oracle.test-d.ts", + "packages/db/tests/btree-map-oracle.test.ts", + "packages/db/tests/change-event-history-oracle.test.ts", + "packages/db/tests/cleanup-queue-oracle.property.test.ts", + "packages/db/tests/collection-cleanup-restart-oracle.test.ts", + "packages/db/tests/collection-gc-clock-oracle.test.ts", + "packages/db/tests/collection-idle-cleanup-oracle.test.ts", + "packages/db/tests/collection-metadata-publication-oracle.property.test.ts", + "packages/db/tests/collection-mutation-startup-oracle.test.ts", + "packages/db/tests/collection-state-retention-oracle.property.test.ts", + "packages/db/tests/collection-subscription-lifecycle-grammar-oracle.ts", + "packages/db/tests/collection-subscription-lifecycle-history-oracle.property.test.ts", + "packages/db/tests/collection-subscription-lifecycle-oracle.test.ts", + "packages/db/tests/collection-subscription-lifecycle-publication-oracle.property.test.ts", + "packages/db/tests/collection-subscription-reentrancy-oracle.test.ts", + "packages/db/tests/collection-subscription-replay-oracle.property.test.ts", + "packages/db/tests/collection-sync-reentrancy-oracle.test.ts", + "packages/db/tests/collection-truncate-ownership-oracle.property.test.ts", + "packages/db/tests/comparison-oracle.property.test.ts", + "packages/db/tests/conformance-disabled-laws-oracle.test.ts", + "packages/db/tests/conformance-infinite-calibration-oracle.test.ts", + "packages/db/tests/conformance-infinite-demand-oracle.test.ts", + "packages/db/tests/conformance-page-laws-oracle.test.ts", + "packages/db/tests/conformance-registration-oracle.test.ts", + "packages/db/tests/conformance-result-laws-oracle.test.ts", + "packages/db/tests/conformance-scenario-lifetime-oracle.test.ts", + "packages/db/tests/conformance-scenario-sources-oracle.test.ts", + "packages/db/tests/conformance-scope-setup-oracle.test.ts", + "packages/db/tests/conformance/contract-oracle.ts", + "packages/db/tests/conformance/disabled-laws-oracle.ts", + "packages/db/tests/conformance/infinite-contract-oracle.ts", + "packages/db/tests/conformance/infinite-on-demand-oracle.ts", + "packages/db/tests/conformance/infinite-suite-oracle.ts", + "packages/db/tests/conformance/page-laws-oracle.ts", + "packages/db/tests/conformance/result-laws-oracle.ts", + "packages/db/tests/conformance/suite-oracle.ts", + "packages/db/tests/cursor-oracle.property.test.ts", + "packages/db/tests/d2-source-reconciliation-oracle.property.test.ts", + "packages/db/tests/db-client-hydration-authority-oracle.test.ts", + "packages/db/tests/db-client-oracle.test-d.ts", + "packages/db/tests/db-client-oracle.test.ts", + "packages/db/tests/effect-disposal-oracle.test.ts", + "packages/db/tests/flat-change-tracking-oracle.property.test.ts", + "packages/db/tests/index-suggestion-oracle.test.ts", + "packages/db/tests/index-update-oracle.property.test.ts", + "packages/db/tests/live-query-observer-history-oracle.property.test.ts", + "packages/db/tests/live-query-observer-oracle.test.ts", + "packages/db/tests/local-only-direct-write-oracle.test.ts", + "packages/db/tests/mutation-handler-type-oracle.test-d.ts", + "packages/db/tests/notification-laws-oracle.test.ts", + "packages/db/tests/observer-cleanup-restart-oracle.test.ts", + "packages/db/tests/optimistic-composition-oracle.test.ts", + "packages/db/tests/optimistic-history-oracle.ts", + "packages/db/tests/optimistic-history-outcomes-oracle.test.ts", + "packages/db/tests/optimistic-history-publication-oracle.test.ts", + "packages/db/tests/optimistic-settlement-boundaries-oracle.test.ts", + "packages/db/tests/optimistic-transaction-oracle.property.test.ts", + "packages/db/tests/oracle-replay.test.ts", + "packages/db/tests/paced-mutations-oracle.test.ts", + "packages/db/tests/proxy-detachment-contract-oracle.test.ts", + "packages/db/tests/proxy-iteration-contract-oracle.test.ts", + "packages/db/tests/proxy-native-methods-oracle.property.test.ts", + "packages/db/tests/proxy-oracle.test.ts", + "packages/db/tests/proxy-revert-oracle.property.test.ts", + "packages/db/tests/query/aggregate-value-contracts-oracle.test-d.ts", + "packages/db/tests/query/builder/ref-proxy-oracle.test.ts", + "packages/db/tests/query/cold-join-reconciliation-oracle.test.ts", + "packages/db/tests/query/compiler/evaluators-oracle.test.ts", + "packages/db/tests/query/compiler/lazy-targets-oracle.test.ts", + "packages/db/tests/query/derived-delete-reconciliation-oracle.test.ts", + "packages/db/tests/query/identity-output-shape-oracle.test.ts", + "packages/db/tests/query/includes-collection-oracle.property.test.ts", + "packages/db/tests/query/includes-context-transport-oracle.test.ts", + "packages/db/tests/query/includes-cross-formulation-oracle.property.test.ts", + "packages/db/tests/query/includes-functional-input-boundary-oracle.test.ts", + "packages/db/tests/query/includes-functional-projection-oracle.test.ts", + "packages/db/tests/query/includes-optimistic-oracle.property.test.ts", + "packages/db/tests/query/includes-oracle-helpers.ts", + "packages/db/tests/query/includes-oracle.property.test.ts", + "packages/db/tests/query/includes-publication-oracle.test.ts", + "packages/db/tests/query/includes-query-shape-oracle.test.ts", + "packages/db/tests/query/includes-scope-identity-oracle.ts", + "packages/db/tests/query/includes-space-oracle.test.ts", + "packages/db/tests/query/includes-temporal-oracle.test.ts", + "packages/db/tests/query/includes-work-counter-oracle.test.ts", + "packages/db/tests/query/index-path-collision-oracle.test.ts", + "packages/db/tests/query/ir-stable-identity-oracle.test.ts", + "packages/db/tests/query/join-result-key-oracle.property.test.ts", + "packages/db/tests/query/load-subset-oracle.property.test.ts", + "packages/db/tests/query/load-subset-replay-refinement-oracle.test.ts", + "packages/db/tests/query/load-subset-source-readiness-refinement-oracle.test.ts", + "packages/db/tests/query/load-subset-transaction-refinement-oracle.test.ts", + "packages/db/tests/query/optimizer-semantics-oracle.test.ts", + "packages/db/tests/query/ordered-default-work-oracle.test.ts", + "packages/db/tests/query/ordered-demand-retirement-oracle.test.ts", + "packages/db/tests/query/ordered-lifecycle-oracle.property.test.ts", + "packages/db/tests/query/ordered-source-loader-oracle.test.ts", + "packages/db/tests/query/ordered-source-loader-state-oracle.test.ts", + "packages/db/tests/query/ordered-work-oracle.property.test.ts", + "packages/db/tests/query/pagination-oracle.property.test.ts", + "packages/db/tests/query/pooled-live-query-gc-oracle.test.ts", + "packages/db/tests/query/pooled-live-query-oracle.property.test.ts", + "packages/db/tests/query/public-container-copy-oracle.test.ts", + "packages/db/tests/query/query-api-type-algebra-oracle.test-d.ts", + "packages/db/tests/query/query-api-type-algebra-oracle.test.ts", + "packages/db/tests/query/replay-failure-boundary-oracle.test.ts", + "packages/db/tests/query/scheduler-oracle.test.ts", + "packages/db/tests/query/subquery-user-value-oracle.test.ts", + "packages/db/tests/query/validate-aliases-oracle.test.ts", + "packages/db/tests/query/virtual-row-fields-oracle.test-d.ts", + "packages/db/tests/query/virtual-row-fields-oracle.test.ts", + "packages/db/tests/query/where-predicate-publication-oracle.property.test.ts", + "packages/db/tests/query/where-prefilter-property-visibility-oracle.test.ts", + "packages/db/tests/reference-expression-oracle.ts", + "packages/db/tests/replay-publication-storage-oracle.test.ts", + "packages/db/tests/single-row-ref-proxy-oracle.test-d.ts", + "packages/db/tests/sync-reused-row-oracle.test.ts", + "packages/db/tests/trace-runner-oracle.ts", + "packages/db/tests/transactions-oracle.test.ts", + "packages/db/tests/utils-oracle.property.test.ts", + "packages/db/tests/virtual-props-cache-oracle.test.ts", + "packages/db/tests/virtual-row-legacy-guard-oracle.test-d.ts", + "packages/electric-db-collection/e2e/sql-predicate-semantics-oracle.e2e.test.ts", + "packages/electric-db-collection/e2e/subset-sql-acceptance-oracle.e2e.test.ts", + "packages/electric-db-collection/tests/ORACLE_MUTATIONS.md", + "packages/electric-db-collection/tests/electric-descriptor-isolation-oracle.test.ts", + "packages/electric-db-collection/tests/electric-oracle-lifecycle.test.ts", + "packages/electric-db-collection/tests/electric-oracle-lifecycle.ts", + "packages/electric-db-collection/tests/electric-oracle.property.test.ts", + "packages/electric-db-collection/tests/electric-recovery-oracle.test.ts", + "packages/electric-db-collection/tests/electric-resume-snapshot-races-oracle.test.ts", + "packages/electric-db-collection/tests/electric-sdk-delivery-oracle.property.test.ts", + "packages/electric-db-collection/tests/electric-sdk-framing-oracle.test.ts", + "packages/electric-db-collection/tests/pg-serializer-oracle.property.test.ts", + "packages/electric-db-collection/tests/schema-transform-conformance-oracle.test-d.ts", + "packages/electron-db-sqlite-persistence/tests/electron-ipc-oracle.test.ts", + "packages/expo-db-sqlite-persistence/e2e/mobile-persisted-collection-conformance-suite-oracle.ts", + "packages/node-db-sqlite-persistence/tests/cached-upstream-history-oracle.test.ts", + "packages/node-db-sqlite-persistence/tests/expression-index-oracle.test.ts", + "packages/node-db-sqlite-persistence/tests/node-driver-oracle.test.ts", + "packages/node-db-sqlite-persistence/tests/node-persistence-oracle.test.ts", + "packages/node-db-sqlite-persistence/tests/node-sqlite-core-adapter-contract-oracle.test.ts", + "packages/offline-transactions/tests/KeyScheduler-oracle.property.test.ts", + "packages/offline-transactions/tests/connectivity-replay-oracle.test.ts", + "packages/offline-transactions/tests/fifo-retry-oracle.property.test.ts", + "packages/offline-transactions/tests/filtered-replay-work-oracle.test.ts", + "packages/offline-transactions/tests/indexeddb-write-settlement-oracle.test.ts", + "packages/offline-transactions/tests/leadership-replay-oracle.property.test.ts", + "packages/offline-transactions/tests/oracle-lifecycle.test.ts", + "packages/offline-transactions/tests/storage-delete-settlement-oracle.test.ts", + "packages/offline-transactions/tests/transaction-serializer-oracle.property.test.ts", + "packages/offline-transactions/tests/transaction-settlement-oracle.property.test.ts", + "packages/powersync-db-collection/tests/correctness-oracle.test.ts", + "packages/powersync-db-collection/tests/schema-transform-conformance-oracle.test-d.ts", + "packages/query-db-collection/tests/cursor-pagination-oracle.integration.test.ts", + "packages/query-db-collection/tests/cursor-pagination-oracle.no-peek.integration.test.ts", + "packages/query-db-collection/tests/cursor-pagination-oracle.no-peek.test.ts", + "packages/query-db-collection/tests/cursor-pagination.boundary-oracle.test.ts", + "packages/query-db-collection/tests/cursor-pagination.cache-oracle.test.ts", + "packages/query-db-collection/tests/cursor-pagination.oracle.test.ts", + "packages/query-db-collection/tests/cursor-pagination.publication-oracle.test.ts", + "packages/query-db-collection/tests/cursor-pagination/model-oracle.ts", + "packages/query-db-collection/tests/cursor-pagination/no-peek-oracle.ts", + "packages/query-db-collection/tests/includes-work-counter-oracle.test.ts", + "packages/query-db-collection/tests/load-subset-lifecycle-oracle.test.ts", + "packages/query-db-collection/tests/ownership-lifecycle.oracle.test.ts", + "packages/query-db-collection/tests/query-oracle.test-d.ts", + "packages/query-db-collection/tests/ssr-dehydration-oracle.test.ts", + "packages/react-db/tests/conformance-oracle.test.tsx", + "packages/react-db/tests/infinite-query-conformance-oracle.test.tsx", + "packages/react-native-db-sqlite-persistence/e2e/mobile-persisted-collection-conformance-suite-oracle.ts", + "packages/react-native-db-sqlite-persistence/tests/op-sqlite-driver-oracle.test.ts", + "packages/react-native-db-sqlite-persistence/tests/react-native-persistence-oracle.test.ts", + "packages/rxdb-db-collection/tests/schema-transform-conformance-oracle.test-d.ts", + "packages/solid-db/tests/conformance-oracle.test.tsx", + "packages/svelte-db/tests/conformance-oracle.svelte.test.ts", + "packages/svelte-db/tests/infinite-query-conformance-oracle.svelte.test.ts", + "packages/tauri-db-sqlite-persistence/e2e/shared/register-persisted-collection-conformance-suite-oracle.ts", + "packages/trailbase-db-collection/tests/ORACLE.md", + "packages/trailbase-db-collection/tests/lifecycle-oracle.property.test.ts", + "packages/vue-db/tests/conformance-oracle.test.ts", + "packages/vue-db/tests/infinite-query-conformance-oracle.test.ts", + "packages/vue-db/tests/useLiveQuery-publication-oracle.test.ts" + ], + "excludedGenericSupport": [ + "packages/db-ivm/tests/test-utils.ts", + "packages/db/tests/utils.ts", + "packages/query-db-collection/tests/cursor-pagination/README.md", + "packages/query-db-collection/tests/cursor-pagination/backend.ts", + "packages/query-db-collection/tests/cursor-pagination/no-peek-transport.ts" + ], + "renames": { + "packages/angular-db/tests/conformance.test.ts": "packages/angular-db/tests/conformance-oracle.test.ts", + "packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle.opfs.spec.ts": "packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle-oracle.opfs.spec.ts", + "packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle.opfs.ts": "packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle-oracle.opfs.ts", + "packages/browser-db-sqlite-persistence/e2e/electric-immediate-reload.opfs.spec.ts": "packages/browser-db-sqlite-persistence/e2e/electric-immediate-reload-oracle.opfs.spec.ts", + "packages/browser-db-sqlite-persistence/e2e/electric-immediate-reload.opfs.ts": "packages/browser-db-sqlite-persistence/e2e/electric-immediate-reload-oracle.opfs.ts", + "packages/browser-db-sqlite-persistence/e2e/electric-resume-two-tab.opfs.spec.ts": "packages/browser-db-sqlite-persistence/e2e/electric-resume-two-tab-oracle.opfs.spec.ts", + "packages/browser-db-sqlite-persistence/e2e/electric-resume-two-tab.opfs.ts": "packages/browser-db-sqlite-persistence/e2e/electric-resume-two-tab-oracle.opfs.ts", + "packages/browser-db-sqlite-persistence/e2e/open-timeout.opfs.spec.ts": "packages/browser-db-sqlite-persistence/e2e/open-timeout-oracle.opfs.spec.ts", + "packages/browser-db-sqlite-persistence/e2e/remote-subset-two-tab.opfs.spec.ts": "packages/browser-db-sqlite-persistence/e2e/remote-subset-two-tab-oracle.opfs.spec.ts", + "packages/browser-db-sqlite-persistence/e2e/remote-subset-two-tab.opfs.ts": "packages/browser-db-sqlite-persistence/e2e/remote-subset-two-tab-oracle.opfs.ts", + "packages/browser-db-sqlite-persistence/e2e/shared-driver-fairness.opfs.spec.ts": "packages/browser-db-sqlite-persistence/e2e/shared-driver-fairness-oracle.opfs.spec.ts", + "packages/browser-db-sqlite-persistence/e2e/shared-driver-fairness.opfs.ts": "packages/browser-db-sqlite-persistence/e2e/shared-driver-fairness-oracle.opfs.ts", + "packages/browser-db-sqlite-persistence/tests/browser-coordinator.test.ts": "packages/browser-db-sqlite-persistence/tests/browser-coordinator-oracle.test.ts", + "packages/capacitor-db-sqlite-persistence/e2e/capacitor-persisted-collection-conformance-suite.ts": "packages/capacitor-db-sqlite-persistence/e2e/capacitor-persisted-collection-conformance-suite-oracle.ts", + "packages/capacitor-db-sqlite-persistence/e2e/shared/register-persisted-collection-conformance-suite.ts": "packages/capacitor-db-sqlite-persistence/e2e/shared/register-persisted-collection-conformance-suite-oracle.ts", + "packages/db-collection-e2e/src/fixtures/persisted-conformance-manifest.ts": "packages/db-collection-e2e/src/fixtures/persisted-conformance-manifest-oracle.ts", + "packages/db-collection-e2e/src/suites/collation.suite.ts": "packages/db-collection-e2e/src/suites/collation-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/deduplication.suite.ts": "packages/db-collection-e2e/src/suites/deduplication-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/joins.suite.ts": "packages/db-collection-e2e/src/suites/joins-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/live-updates.suite.ts": "packages/db-collection-e2e/src/suites/live-updates-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/moves.suite.ts": "packages/db-collection-e2e/src/suites/moves-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/mutations.suite.ts": "packages/db-collection-e2e/src/suites/mutations-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/pagination.suite.ts": "packages/db-collection-e2e/src/suites/pagination-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/predicates.suite.ts": "packages/db-collection-e2e/src/suites/predicates-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/progressive.suite.ts": "packages/db-collection-e2e/src/suites/progressive-oracle.suite.ts", + "packages/db-collection-e2e/src/utils/assertions.ts": "packages/db-collection-e2e/src/utils/assertions-oracle.ts", + "packages/db-collection-e2e/src/utils/notification-laws.ts": "packages/db-collection-e2e/src/utils/notification-laws-oracle.ts", + "packages/db-ivm/tests/hash-failure-retry.property.test.ts": "packages/db-ivm/tests/hash-failure-retry-oracle.property.test.ts", + "packages/db-ivm/tests/hash-graph.property.test.ts": "packages/db-ivm/tests/hash-graph-oracle.property.test.ts", + "packages/db-ivm/tests/hash-mixed-graph.property.test.ts": "packages/db-ivm/tests/hash-mixed-graph-oracle.property.test.ts", + "packages/db-ivm/tests/hash-session-replay.test.ts": "packages/db-ivm/tests/hash-session-replay-oracle.test.ts", + "packages/db-ivm/tests/hash-session.ts": "packages/db-ivm/tests/hash-session-oracle.ts", + "packages/db-ivm/tests/hash-work.test.ts": "packages/db-ivm/tests/hash-work-oracle.test.ts", + "packages/db-ivm/tests/hash.property.test.ts": "packages/db-ivm/tests/hash-oracle.property.test.ts", + "packages/db-ivm/tests/incrementalization-law.property.test.ts": "packages/db-ivm/tests/incrementalization-law-oracle.property.test.ts", + "packages/db-ivm/tests/incrementalization-law.ts": "packages/db-ivm/tests/incrementalization-law-oracle.ts", + "packages/db-ivm/tests/operators/topKWithFractionalIndex.test.ts": "packages/db-ivm/tests/operators/topKWithFractionalIndex-oracle.test.ts", + "packages/db-ivm/tests/operators/topKWithIndex.test.ts": "packages/db-ivm/tests/operators/topKWithIndex-oracle.test.ts", + "packages/db-ivm/tests/operators/topk-batch-contract.test.ts": "packages/db-ivm/tests/operators/topk-batch-contract-oracle.test.ts", + "packages/db-sqlite-persistence-core/tests/contracts/driver-admission-laws.ts": "packages/db-sqlite-persistence-core/tests/contracts/driver-admission-laws-oracle.ts", + "packages/db-sqlite-persistence-core/tests/contracts/persisted-collection-conformance-contract.ts": "packages/db-sqlite-persistence-core/tests/contracts/persisted-collection-conformance-contract-oracle.ts", + "packages/db-sqlite-persistence-core/tests/contracts/runtime-bridge-e2e-contract.ts": "packages/db-sqlite-persistence-core/tests/contracts/runtime-bridge-e2e-contract-oracle.ts", + "packages/db-sqlite-persistence-core/tests/contracts/runtime-persistence-contract.ts": "packages/db-sqlite-persistence-core/tests/contracts/runtime-persistence-contract-oracle.ts", + "packages/db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract.ts": "packages/db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract-oracle.ts", + "packages/db-sqlite-persistence-core/tests/contracts/sqlite-driver-contract.ts": "packages/db-sqlite-persistence-core/tests/contracts/sqlite-driver-contract-oracle.ts", + "packages/db-sqlite-persistence-core/tests/persisted-real-adapter-lifecycle.test.ts": "packages/db-sqlite-persistence-core/tests/persisted-real-adapter-lifecycle-oracle.test.ts", + "packages/db-sqlite-persistence-core/tests/persisted.test.ts": "packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts", + "packages/db-sqlite-persistence-core/tests/runtime-restart-checker.test.ts": "packages/db-sqlite-persistence-core/tests/runtime-restart-checker-oracle.test.ts", + "packages/db-sqlite-persistence-core/tests/shared-logical-scheduling.test.ts": "packages/db-sqlite-persistence-core/tests/shared-logical-scheduling-oracle.test.ts", + "packages/db-sqlite-persistence-core/tests/sqlite-core-adapter.test.ts": "packages/db-sqlite-persistence-core/tests/sqlite-core-adapter-oracle.test.ts", + "packages/db-sqlite-persistence-core/tests/sqlite-driver-admission-laws.test.ts": "packages/db-sqlite-persistence-core/tests/sqlite-driver-admission-laws-oracle.test.ts", + "packages/db-sqlite-persistence-core/tests/sqlite-resume-snapshot.test.ts": "packages/db-sqlite-persistence-core/tests/sqlite-resume-snapshot-oracle.test.ts", + "packages/db/tests/SortedMap.test.ts": "packages/db/tests/SortedMap-oracle.test.ts", + "packages/db/tests/adapter-schema-transform-conformance.test-d.ts": "packages/db/tests/adapter-schema-transform-conformance-oracle.test-d.ts", + "packages/db/tests/cleanup-queue.property.test.ts": "packages/db/tests/cleanup-queue-oracle.property.test.ts", + "packages/db/tests/collection-gc-clock.test.ts": "packages/db/tests/collection-gc-clock-oracle.test.ts", + "packages/db/tests/collection-idle-cleanup.test.ts": "packages/db/tests/collection-idle-cleanup-oracle.test.ts", + "packages/db/tests/collection-subscription-lifecycle-grammar.ts": "packages/db/tests/collection-subscription-lifecycle-grammar-oracle.ts", + "packages/db/tests/collection-subscription-lifecycle-history.property.test.ts": "packages/db/tests/collection-subscription-lifecycle-history-oracle.property.test.ts", + "packages/db/tests/collection-subscription-lifecycle-publication.property.test.ts": "packages/db/tests/collection-subscription-lifecycle-publication-oracle.property.test.ts", + "packages/db/tests/collection-sync-reentrancy.test.ts": "packages/db/tests/collection-sync-reentrancy-oracle.test.ts", + "packages/db/tests/comparison.property.test.ts": "packages/db/tests/comparison-oracle.property.test.ts", + "packages/db/tests/conformance-disabled-laws.test.ts": "packages/db/tests/conformance-disabled-laws-oracle.test.ts", + "packages/db/tests/conformance-infinite-calibration.test.ts": "packages/db/tests/conformance-infinite-calibration-oracle.test.ts", + "packages/db/tests/conformance-infinite-demand.test.ts": "packages/db/tests/conformance-infinite-demand-oracle.test.ts", + "packages/db/tests/conformance-page-laws.test.ts": "packages/db/tests/conformance-page-laws-oracle.test.ts", + "packages/db/tests/conformance-registration.test.ts": "packages/db/tests/conformance-registration-oracle.test.ts", + "packages/db/tests/conformance-result-laws.test.ts": "packages/db/tests/conformance-result-laws-oracle.test.ts", + "packages/db/tests/conformance-scenario-lifetime.test.ts": "packages/db/tests/conformance-scenario-lifetime-oracle.test.ts", + "packages/db/tests/conformance-scenario-sources.test.ts": "packages/db/tests/conformance-scenario-sources-oracle.test.ts", + "packages/db/tests/conformance-scope-setup.test.ts": "packages/db/tests/conformance-scope-setup-oracle.test.ts", + "packages/db/tests/conformance/contract.ts": "packages/db/tests/conformance/contract-oracle.ts", + "packages/db/tests/conformance/disabled-laws.ts": "packages/db/tests/conformance/disabled-laws-oracle.ts", + "packages/db/tests/conformance/infinite-contract.ts": "packages/db/tests/conformance/infinite-contract-oracle.ts", + "packages/db/tests/conformance/infinite-on-demand.ts": "packages/db/tests/conformance/infinite-on-demand-oracle.ts", + "packages/db/tests/conformance/infinite-suite.ts": "packages/db/tests/conformance/infinite-suite-oracle.ts", + "packages/db/tests/conformance/page-laws.ts": "packages/db/tests/conformance/page-laws-oracle.ts", + "packages/db/tests/conformance/result-laws.ts": "packages/db/tests/conformance/result-laws-oracle.ts", + "packages/db/tests/conformance/suite.ts": "packages/db/tests/conformance/suite-oracle.ts", + "packages/db/tests/cursor.property.test.ts": "packages/db/tests/cursor-oracle.property.test.ts", + "packages/db/tests/db-client.test-d.ts": "packages/db/tests/db-client-oracle.test-d.ts", + "packages/db/tests/db-client.test.ts": "packages/db/tests/db-client-oracle.test.ts", + "packages/db/tests/index-update.property.test.ts": "packages/db/tests/index-update-oracle.property.test.ts", + "packages/db/tests/live-query-observer-history.property.test.ts": "packages/db/tests/live-query-observer-history-oracle.property.test.ts", + "packages/db/tests/live-query-observer.test.ts": "packages/db/tests/live-query-observer-oracle.test.ts", + "packages/db/tests/local-only-direct-write.test.ts": "packages/db/tests/local-only-direct-write-oracle.test.ts", + "packages/db/tests/notification-laws.test.ts": "packages/db/tests/notification-laws-oracle.test.ts", + "packages/db/tests/observer-cleanup-restart.test.ts": "packages/db/tests/observer-cleanup-restart-oracle.test.ts", + "packages/db/tests/optimistic-composition.test.ts": "packages/db/tests/optimistic-composition-oracle.test.ts", + "packages/db/tests/optimistic-history-outcomes.test.ts": "packages/db/tests/optimistic-history-outcomes-oracle.test.ts", + "packages/db/tests/optimistic-history-publication.test.ts": "packages/db/tests/optimistic-history-publication-oracle.test.ts", + "packages/db/tests/optimistic-settlement-boundaries.test.ts": "packages/db/tests/optimistic-settlement-boundaries-oracle.test.ts", + "packages/db/tests/proxy-detachment-contract.test.ts": "packages/db/tests/proxy-detachment-contract-oracle.test.ts", + "packages/db/tests/proxy-iteration-contract.test.ts": "packages/db/tests/proxy-iteration-contract-oracle.test.ts", + "packages/db/tests/proxy-native-methods.property.test.ts": "packages/db/tests/proxy-native-methods-oracle.property.test.ts", + "packages/db/tests/proxy.test.ts": "packages/db/tests/proxy-oracle.test.ts", + "packages/db/tests/query/aggregate-value-contracts.test-d.ts": "packages/db/tests/query/aggregate-value-contracts-oracle.test-d.ts", + "packages/db/tests/query/builder/ref-proxy.test.ts": "packages/db/tests/query/builder/ref-proxy-oracle.test.ts", + "packages/db/tests/query/compiler/evaluators.test.ts": "packages/db/tests/query/compiler/evaluators-oracle.test.ts", + "packages/db/tests/query/compiler/lazy-targets.test.ts": "packages/db/tests/query/compiler/lazy-targets-oracle.test.ts", + "packages/db/tests/query/derived-delete-reconciliation.test.ts": "packages/db/tests/query/derived-delete-reconciliation-oracle.test.ts", + "packages/db/tests/query/includes-functional-input-boundary.test.ts": "packages/db/tests/query/includes-functional-input-boundary-oracle.test.ts", + "packages/db/tests/query/ir-stable-identity.test.ts": "packages/db/tests/query/ir-stable-identity-oracle.test.ts", + "packages/db/tests/query/ordered-default-work.test.ts": "packages/db/tests/query/ordered-default-work-oracle.test.ts", + "packages/db/tests/query/ordered-demand-retirement.test.ts": "packages/db/tests/query/ordered-demand-retirement-oracle.test.ts", + "packages/db/tests/query/ordered-source-loader-state.test.ts": "packages/db/tests/query/ordered-source-loader-state-oracle.test.ts", + "packages/db/tests/query/ordered-source-loader.test.ts": "packages/db/tests/query/ordered-source-loader-oracle.test.ts", + "packages/db/tests/query/pooled-live-query-gc.test.ts": "packages/db/tests/query/pooled-live-query-gc-oracle.test.ts", + "packages/db/tests/query/public-container-copy.test.ts": "packages/db/tests/query/public-container-copy-oracle.test.ts", + "packages/db/tests/query/query-api-type-algebra.test-d.ts": "packages/db/tests/query/query-api-type-algebra-oracle.test-d.ts", + "packages/db/tests/query/query-api-type-algebra.test.ts": "packages/db/tests/query/query-api-type-algebra-oracle.test.ts", + "packages/db/tests/query/replay-failure-boundary.test.ts": "packages/db/tests/query/replay-failure-boundary-oracle.test.ts", + "packages/db/tests/query/scheduler.test.ts": "packages/db/tests/query/scheduler-oracle.test.ts", + "packages/db/tests/query/validate-aliases.test.ts": "packages/db/tests/query/validate-aliases-oracle.test.ts", + "packages/db/tests/query/where-prefilter-property-visibility.test.ts": "packages/db/tests/query/where-prefilter-property-visibility-oracle.test.ts", + "packages/db/tests/reference-expression.ts": "packages/db/tests/reference-expression-oracle.ts", + "packages/db/tests/replay-publication-storage.test.ts": "packages/db/tests/replay-publication-storage-oracle.test.ts", + "packages/db/tests/single-row-ref-proxy.test-d.ts": "packages/db/tests/single-row-ref-proxy-oracle.test-d.ts", + "packages/db/tests/sync-reused-row.test.ts": "packages/db/tests/sync-reused-row-oracle.test.ts", + "packages/db/tests/trace-runner.ts": "packages/db/tests/trace-runner-oracle.ts", + "packages/db/tests/transactions.test.ts": "packages/db/tests/transactions-oracle.test.ts", + "packages/db/tests/utils.property.test.ts": "packages/db/tests/utils-oracle.property.test.ts", + "packages/db/tests/virtual-props-cache.test.ts": "packages/db/tests/virtual-props-cache-oracle.test.ts", + "packages/electric-db-collection/e2e/sql-predicate-semantics.e2e.test.ts": "packages/electric-db-collection/e2e/sql-predicate-semantics-oracle.e2e.test.ts", + "packages/electric-db-collection/e2e/subset-sql-acceptance.e2e.test.ts": "packages/electric-db-collection/e2e/subset-sql-acceptance-oracle.e2e.test.ts", + "packages/electric-db-collection/tests/electric-descriptor-isolation.test.ts": "packages/electric-db-collection/tests/electric-descriptor-isolation-oracle.test.ts", + "packages/electric-db-collection/tests/electric-resume-snapshot-races.test.ts": "packages/electric-db-collection/tests/electric-resume-snapshot-races-oracle.test.ts", + "packages/electric-db-collection/tests/electric-sdk-delivery.property.test.ts": "packages/electric-db-collection/tests/electric-sdk-delivery-oracle.property.test.ts", + "packages/electric-db-collection/tests/electric-sdk-framing.test.ts": "packages/electric-db-collection/tests/electric-sdk-framing-oracle.test.ts", + "packages/electric-db-collection/tests/pg-serializer.property.test.ts": "packages/electric-db-collection/tests/pg-serializer-oracle.property.test.ts", + "packages/electric-db-collection/tests/schema-transform-conformance.test-d.ts": "packages/electric-db-collection/tests/schema-transform-conformance-oracle.test-d.ts", + "packages/electron-db-sqlite-persistence/tests/electron-ipc.test.ts": "packages/electron-db-sqlite-persistence/tests/electron-ipc-oracle.test.ts", + "packages/expo-db-sqlite-persistence/e2e/mobile-persisted-collection-conformance-suite.ts": "packages/expo-db-sqlite-persistence/e2e/mobile-persisted-collection-conformance-suite-oracle.ts", + "packages/node-db-sqlite-persistence/tests/cached-upstream-history.test.ts": "packages/node-db-sqlite-persistence/tests/cached-upstream-history-oracle.test.ts", + "packages/node-db-sqlite-persistence/tests/node-driver.test.ts": "packages/node-db-sqlite-persistence/tests/node-driver-oracle.test.ts", + "packages/node-db-sqlite-persistence/tests/node-persistence.test.ts": "packages/node-db-sqlite-persistence/tests/node-persistence-oracle.test.ts", + "packages/node-db-sqlite-persistence/tests/node-sqlite-core-adapter-contract.test.ts": "packages/node-db-sqlite-persistence/tests/node-sqlite-core-adapter-contract-oracle.test.ts", + "packages/offline-transactions/tests/KeyScheduler.property.test.ts": "packages/offline-transactions/tests/KeyScheduler-oracle.property.test.ts", + "packages/offline-transactions/tests/fifo-retry.property.test.ts": "packages/offline-transactions/tests/fifo-retry-oracle.property.test.ts", + "packages/offline-transactions/tests/filtered-replay-work.test.ts": "packages/offline-transactions/tests/filtered-replay-work-oracle.test.ts", + "packages/offline-transactions/tests/indexeddb-write-settlement.test.ts": "packages/offline-transactions/tests/indexeddb-write-settlement-oracle.test.ts", + "packages/offline-transactions/tests/leadership-replay.property.test.ts": "packages/offline-transactions/tests/leadership-replay-oracle.property.test.ts", + "packages/offline-transactions/tests/storage-delete-settlement.test.ts": "packages/offline-transactions/tests/storage-delete-settlement-oracle.test.ts", + "packages/offline-transactions/tests/transaction-serializer.property.test.ts": "packages/offline-transactions/tests/transaction-serializer-oracle.property.test.ts", + "packages/offline-transactions/tests/transaction-settlement.property.test.ts": "packages/offline-transactions/tests/transaction-settlement-oracle.property.test.ts", + "packages/powersync-db-collection/tests/schema-transform-conformance.test-d.ts": "packages/powersync-db-collection/tests/schema-transform-conformance-oracle.test-d.ts", + "packages/query-db-collection/tests/cursor-pagination.integration.test.ts": "packages/query-db-collection/tests/cursor-pagination-oracle.integration.test.ts", + "packages/query-db-collection/tests/cursor-pagination.no-peek.integration.test.ts": "packages/query-db-collection/tests/cursor-pagination-oracle.no-peek.integration.test.ts", + "packages/query-db-collection/tests/cursor-pagination.no-peek.test.ts": "packages/query-db-collection/tests/cursor-pagination-oracle.no-peek.test.ts", + "packages/query-db-collection/tests/cursor-pagination/model.ts": "packages/query-db-collection/tests/cursor-pagination/model-oracle.ts", + "packages/query-db-collection/tests/cursor-pagination/no-peek.ts": "packages/query-db-collection/tests/cursor-pagination/no-peek-oracle.ts", + "packages/query-db-collection/tests/query.test-d.ts": "packages/query-db-collection/tests/query-oracle.test-d.ts", + "packages/react-db/tests/conformance.test.tsx": "packages/react-db/tests/conformance-oracle.test.tsx", + "packages/react-db/tests/infinite-query-conformance.test.tsx": "packages/react-db/tests/infinite-query-conformance-oracle.test.tsx", + "packages/react-native-db-sqlite-persistence/e2e/mobile-persisted-collection-conformance-suite.ts": "packages/react-native-db-sqlite-persistence/e2e/mobile-persisted-collection-conformance-suite-oracle.ts", + "packages/react-native-db-sqlite-persistence/tests/op-sqlite-driver.test.ts": "packages/react-native-db-sqlite-persistence/tests/op-sqlite-driver-oracle.test.ts", + "packages/react-native-db-sqlite-persistence/tests/react-native-persistence.test.ts": "packages/react-native-db-sqlite-persistence/tests/react-native-persistence-oracle.test.ts", + "packages/rxdb-db-collection/tests/schema-transform-conformance.test-d.ts": "packages/rxdb-db-collection/tests/schema-transform-conformance-oracle.test-d.ts", + "packages/solid-db/tests/conformance.test.tsx": "packages/solid-db/tests/conformance-oracle.test.tsx", + "packages/svelte-db/tests/conformance.svelte.test.ts": "packages/svelte-db/tests/conformance-oracle.svelte.test.ts", + "packages/svelte-db/tests/infinite-query-conformance.svelte.test.ts": "packages/svelte-db/tests/infinite-query-conformance-oracle.svelte.test.ts", + "packages/tauri-db-sqlite-persistence/e2e/shared/register-persisted-collection-conformance-suite.ts": "packages/tauri-db-sqlite-persistence/e2e/shared/register-persisted-collection-conformance-suite-oracle.ts", + "packages/vue-db/tests/conformance.test.ts": "packages/vue-db/tests/conformance-oracle.test.ts", + "packages/vue-db/tests/infinite-query-conformance.test.ts": "packages/vue-db/tests/infinite-query-conformance-oracle.test.ts" + }, + "namingInventoryRetraction": {"record": "2026-10-03-oracle-naming-scope.md", "reason": "The original owner/companion inventory mixed oracle mechanisms with ordinary tests and support. Use the corrected classification and path map.", "retainedPrRenames": 63, "reversedPrRenames": 101} +} diff --git a/docs/contributing/oracle-reviews/2026-10-02-glossary-alignment.md b/docs/contributing/oracle-reviews/2026-10-02-glossary-alignment.md new file mode 100644 index 0000000000..01dd4ce4fc --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-02-glossary-alignment.md @@ -0,0 +1,123 @@ +# Oracle glossary alignment — 2026-10-02 + +Reviewed implementation: [f993daf838da4905d3a327cf6c66d8bdf16d106b](https://github.com/TanStack/db/commit/f993daf838da4905d3a327cf6c66d8bdf16d106b). +Implementation base: `b940a363a7631c49e14dc67e2adeda0a96dc366c`. Original glossary audit: `06cab6fc7b808acfbfb3af1eb2fc1fdc3c9f0fa8`. + +This review resolves 39 retained terminology findings: 18 substantive and 21 minor. +The inventory contains 251 executable oracle owners and dedicated companions. +All filenames contain `oracle`, after 164 moves. Generic fixtures and utilities retain their existing names. + +The [companion ledger](2026-10-02-glossary-alignment.json) preserves every retained finding, its original evidence, +its resolution, the complete owner inventory, and the old-to-new path map. +Original line numbers refer to the audit revision. Current paths refer to the reviewed implementation. +Historical review records and immutable revision links remain unchanged. +Later changes require a new record or an append-only entry tied to their own revision. + +## Scope and result + +Shared terms now name the same boundaries in model declarations, driver observations, and glossary prose. +Model abstractions explicitly describe combined or split production concepts. +The changes preserve model rules, assertions, grammar domains, and campaign budgets. +They also update imports, test commands, replay selectors, coverage links, and browser entry modules after the moves. +Production runtime behavior is unchanged. The only production TypeScript edit corrects a draft-extraction comment. + +This is an ORC-009 vocabulary review and a filename-discovery change. +It does not reassess full ORC-001–014 conformance or close any runtime bug class. +Earlier coverage gaps and review findings outside terminology retain their prior status. +Two stack-checkpoint regexes now match their renamed comparison and Electric SDK oracle filenames. +Their existing checkpoint and shrinking tests verify those path adjustments. + +## Resolution map + +| Domain | Findings | Canonical distinctions | +| --- | ---: | --- | +| Collection lifecycle | 3 | Failed replay versus publication, unsubscribe versus sync run, tentative versus accepted acquisition | +| Optimistic state and values | 2 | Draft extraction versus publication, observation mutant versus runtime fault | +| Includes | 3 | Mutation-handler gate versus startup, bucket versus route, acquisition fulfillment versus initial-query readiness | +| Acquisition and pagination | 5 | Load result versus applied receipt, response processing versus fulfillment, request shape, subscription readiness | +| Core query | 3 | Mutants, equality-partition identity, graph publication versus promise settlement | +| D2 and comparison | 4 | Weighted deltas versus messages, index observations, snapshot cut, comparison domains | +| Query DB | 4 | Compressed model actions, Query fetch versus acquisition, qualified generations, fulfillment-only observations | +| Providers | 3 | Sync run versus provider session, persistence fulfillment, provider control and persistence boundaries | +| SQLite core | 3 | Wrapped sync receipt, adapter reopen, Collection readiness | +| Browser and Electron | 3 | Layered acquisition ownership, committed-message posts, composite promise chain | +| Offline transactions | 4 | Caller success boundary, observation mutants, executor restart and outbox replay, promise fulfillment | +| Frameworks | 2 | Input/window changes and fulfilled page fetches | + +## Validation + +These are unique runtime results from package runs, except the selected framework receiving suites. +The final DB run includes its renamed replay, checkpoint, and oracle files. + +| Package or receiving suites | Passing tests | Files | Result | +| --- | ---: | ---: | --- | +| DB | 7,962 | 210 | Passed | +| DB IVM | 704 | 44 | Passed | +| Offline transactions | 242 | 18 | Passed | +| SQLite persistence core | 375 | 9 | Passed, one todo | +| Node SQLite persistence | 109 | 5 | Passed | +| Browser SQLite persistence | 242 | 9 | Passed | +| Electron SQLite persistence | 97 | 3 | Passed | +| Electric | 629 | 13 | Passed | +| TrailBase | 67 | 3 | Passed | +| PowerSync | 176 | 9 | Passed with local native SQLite | +| Query DB | 557 | 16 | One intermittent baseline failure | +| React, Vue, Solid, Svelte, Angular receiving suites | 290 | 9 | Passed | + +The final supplementary no-peek model rename also passed its 18 affected tests across two files. +The Electric SDK checkpoint follow-up passed all nine tests in that file. +These focused repeats are not additional unique test counts. + +Builds passed for DB, IVM, Offline, Query DB, SQLite core, Node, Browser, Electron, Electric, TrailBase, and PowerSync. +Standalone TypeScript checks passed for IVM, Offline, and Query DB. +Selected framework runs reported no type errors. They were not full framework-package campaigns. +ESLint checked the changed TypeScript files directly in package groups. +Scoped formatting and `git diff --check` passed. +Static checks covered all renamed paths, relative imports, explicit commands, replay targets, and browser script entries. + +### Commands and environment + +Validation used the installed Node, Vite, Vitest, TypeScript, and ESLint entrypoints directly. +The shell hook invokes pnpm, which attempted dependency installation with the reused dependency tree. +The equivalent scoped ESLint check ran directly before commit instead. +Runtime reruns used the following arguments from each package directory: + +```sh +node /vitest.mjs run --coverage.enabled=false --typecheck.enabled=false --maxWorkers=1 --reporter=dot +node /bin/vite.js build +node /bin/tsc --noEmit +``` + +DB used two workers. IVM, Offline, and selected framework runs retained their normal integrated typecheck setting. +The environment used Node 24.19.0, Vitest 3.2.4, TypeScript 5.9.3, and Vite 7.3.2. +The package mirror refused some locked dependencies, and the public registry was unavailable. +Validation reused installed dependencies with workspace links directed at the isolated implementation checkout. +For example, installed Seroval was 1.5.0 while the lockfile requires 1.6.8. The lockfile is unchanged. + +### Limits and baseline evidence + +Default integrated Vitest typecheck lanes are not green under the available dependencies. +They report cross-package `rootDir` diagnostics against unchanged source/configuration boundaries. +Some persistence lanes also report existing generic-cast diagnostics in `persisted.ts` at lines 4976 and 5098. +Those sources remain unchanged. No full baseline typecheck campaign establishes their root cause. +Runtime-only passes do not establish type correctness for those lanes. + +Query DB's unchanged `awaits persisted server responses in update handlers` test intermittently fails its terminal-row assertion. +It expects revision 3 and `$synced: true`, but observes revision 2 and `$synced: false`. +The complete original test file reproduces the same failure under the installed dependencies. +A full original-package control passes 558 tests, and the isolated original test also passes. +The changed-package repeat passes 557 tests with that same failure. All Query DB oracle suites pass. +The test executes before the changed oracle files, and the original-file control fails independently of those files. +This supports an intermittent baseline classification. Its root cause remains unestablished. +The baseline copy preserves original sources and filenames, with four external helper imports adapted to their renamed paths. + +No external-provider, browser-host, Electron-process, or native-device E2E campaign ran. +Local SQLite paths and controlled runtime fixtures executed within the package suites. +Static entrypoint checks establish path consistency only. + +## Retraction of naming inventory — 2026-10-03 + +The 251-file owner/companion classification above is withdrawn. It included ordinary examples, fixtures, harness tests, and documentation. +The [corrected naming review](2026-10-03-oracle-naming-scope.md) classifies each file from executable evidence and supersedes that inventory. +It restores 101 original filenames and retains 63 justified PR renames. The original 39 terminology resolutions remain intact. +The earlier validation results describe the exact implementation revision stated above; they are not a current owner-count claim. diff --git a/docs/contributing/oracle-reviews/2026-10-02-insert-for-absent-key.md b/docs/contributing/oracle-reviews/2026-10-02-insert-for-absent-key.md new file mode 100644 index 0000000000..1d2e7578d5 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-02-insert-for-absent-key.md @@ -0,0 +1,66 @@ +# Insert, not update, for a key subscribers saw removed + +Pre-repair executable revision: `721a607e8` (oracle extension on `ef1e6a4aa`). +Repair executable revision: `182b99262`. + +## Law + +Each change message must be valid for a consumer that has applied every +earlier message: an insert names an absent key, and an update or delete names +a present one. Authority: the change-message contract in +`packages/db/tests/change-event-history-oracle.test.ts` and issue #1901, which +require a consumer that applies every message to agree with the Collection. + +## Gap + +When a sync commit made a previously absent key visible, `state.ts` checked +for a completed optimistic request on that key. If one existed, it emitted an +`update` whose `previousValue` was that request's value. Subscribers had last +seen the key removed, usually by a completed optimistic delete, so they got an +update for a row they did not hold. + +The change-event oracle missed it because its mirror applied inserts and +updates with the same `Map#set`. The message type was never checked. + +A mutation round on the state stack found it. The mutant that limited this +branch to updates (E3) survived the whole suite. With a strict mirror, the +oracle's histories showed 78 invalid updates on `main` and none under E3. + +## Repair and witnesses + +- The oracle's mirror records each message that is invalid for its state, and + every history asserts that the list is empty. The deferred-sync lane already + compared exact per-key traces. +- A previously absent key now always gets an `insert`. When a buffered delete + for the key is still batched, `composeBatchedChange` turns the pair into an + update, as before. +- The completed-request map becomes a key set, the only part that + virtual-property derivation reads. The `state.ts` module is 194 minified and + 39 gzip bytes smaller. + +| Run | Result | +| --- | --- | +| Owner on `721a607e8` (oracle only) | 26 of 378 fail, each on an update for a key the mirror lacks | +| Owner on `182b99262` | 378 pass | +| Mutant: the old update branch | the 26 failures above | +| Mutant: no event for a previously absent key | 92 of 538 owner tests fail | + +## Limits + +- The strict mirror runs in the change-event oracle's local-only histories. + Other owners that build mirrors may still accept an update for an absent key. +- Survivor: building the completed-key set empty changes no test result. The + set only feeds the fallback for previous `$synced`/`$origin` when the + pre-sync capture lacks the key. This repair keeps that use unchanged; the + next mutation round owns it. + +## Verification + +On `182b99262`, with the built `dist`: + +- `packages/db` Vitest, typecheck off: 200 files, 7,857 tests. + `tsc --noEmit`: no errors. +- `pnpm --filter @tanstack/db test:dist` (290 tests) and + `pnpm test:minified-db` pass. +- Package unit tests: `query-db-collection` 558, `electric-db-collection` 629, + `offline-transactions` 242. diff --git a/docs/contributing/oracle-reviews/2026-10-02-state-mutation-round-2.md b/docs/contributing/oracle-reviews/2026-10-02-state-mutation-round-2.md new file mode 100644 index 0000000000..276d0f25a5 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-02-state-mutation-round-2.md @@ -0,0 +1,53 @@ +# State mutation round 2 + +Reviewed executable revision: `76484a092`, the test commit that precedes +this record (base `4ce1f9b87`). No production code changes. + +## Round + +After the round-1 fixes merged (#1986, #1995, #1996), 40 new source mutants +modeled plausible mistakes in the state.ts sync commit (12), change batching +and subscription delivery (14), and mutations.ts and transactions.ts (14). +Each was applied to unchanged `main`; the full db suite ran with bail. The +round-1 survivors were rerun first: every round-1 test gap is now caught. + +28 of the 40 were caught. Each of the 12 survivors was classified: + +| Mutants | Verdict | Evidence | +| --- | --- | --- | +| M8 | **Test gap**, closed here | A delete then a reinsert of the same key in one transaction, merged as an update, lost its optimistic flag. The row showed its old value until persistence. The same-key lane never authored a delete followed by an insert | +| M1, M2, M3 | Equivalent; dead code | `markPendingLocalChanges` covered a handler that syncs before its transaction is registered. Since #1986, each direct path registers the transaction and recomputes, which rebuilds `pendingLocalChanges`, before `commit()` | +| S5, S6, S12 | Equivalent; dead code | The truncate reapply search for an earlier insert never finds one (round-1 A2 to A4) | +| S7, M10 | Equivalent | Recompute's stale cleanup removes the leftover layer; `isPersisted` awaiters resume after the synchronous state change | +| S2 | Equivalent on reached histories | The completed-key fallback decides 219 times, all in `collection-metadata-publication-oracle`, which asserts the exact published batches and passes. Another publication delivers the settlement update | +| C1, C2 | Equivalent on reached histories | Batch composition ran 228 times in the suite; insert-first pairs never ran, including in a targeted history | +| C3 | Minor | A composed delete keeps a `previousValue` beside the correct `value`; reached once | + +## Witness + +The same-key lane of `optimistic-transaction-oracle.property.test.ts` adds +`delete-insert`. The `applyMutations` truth table gives its net request: a +reinsert that restores the original row cancels the pair; otherwise it is an +update from the original row whose `changes` hold exactly the differing +fields. The lane checks both authored prefixes, so the reinserted row must be +visible optimistically. Two pinned cases cover a restoring reinsert, which +generation never produces. + +| Mutant on `transactions.ts` | Owner result | +| --- | --- | +| M8: the merged update loses `optimistic` | 3 of 66 fail | +| X1: the reinsert is passed through as an insert | 13 of 66 fail | +| X2: `changes` hold the whole reinserted row | 12 of 66 fail | +| X3: a restoring reinsert is not canceled | 4 of 68 fail, including both pinned cases | + +## Open + +- The completed-key fallback (S2) and the insert-first batch compositions + (C1, C2) may be unreachable. Proving that would let the state-stack port + delete them. +- The port can delete `markPendingLocalChanges`, the truncate reapply search, + and the recompute delete filter. + +## Verification + +`packages/db` Vitest, typecheck off: 201 files, 7,865 tests. diff --git a/docs/contributing/oracle-reviews/2026-10-02-state-stack-mutant-gaps.md b/docs/contributing/oracle-reviews/2026-10-02-state-stack-mutant-gaps.md new file mode 100644 index 0000000000..75ed74f026 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-02-state-stack-mutant-gaps.md @@ -0,0 +1,81 @@ +# State-stack mutation round: test gaps + +Reviewed executable revision: `f1a40e58d` (base +`ef1e6a4aa`). No production code changes. + +## How the gaps were found + +A planned code-weight refactor of `state.ts`, `sync.ts`, and `lifecycle.ts` +has nine prototype commits. Before porting them, 22 plausible porting mistakes +were applied to unchanged `main` as source mutants. Each survivor of the db +suite was then classified with a probe that passes on `main` and fails on the +mutant, or with evidence that the mutant changes no public observation. Two +survivors exposed bugs on `main`, which have separate repairs: a direct +mutation confirmed inside its handler (#1986) and an update sent for an +absent key (#1995). This change closes five survivors that were test gaps. + +| Mutant | Wrong behavior that passed the suite | New witness | +| --- | --- | --- | +| B4 | A sync insert did not clear the key's pending local layers. After a confirmed insert, the next remote update reported `$origin: 'local'`. | Optimistic-history grammar: source rows are written as `insert` for a key the source lacks. | +| B2 | A sync delete did not clear the key's pending local layers. A later source reinsert reported `$origin: 'local'`. | Optimistic-history grammar: source deletes. Pinned replay of the random-campaign counterexample. | +| F2 | A sync `write()` after `commit()`, while the batch waited for persistence, joined the queued batch. | Retention oracle: a write to a committed, waiting batch throws `SyncTransactionAlreadyCommittedWriteError`. | +| G4 | Cleanup with a pending subset load emitted a `start` transition. | Cleanup/restart oracle: one `end` transition at cleanup. | +| H3 | Idle cleanup that rescheduled itself could not be canceled by a returning subscriber, so the Collection cleaned up early. | New `collection-idle-cleanup.test.ts`, with a controllable browser idle scheduler. | + +## Grammar change + +Every optimistic-history source batch wrote `update` messages, which act as +upserts. The production `insert` and `delete` apply paths never ran in those +histories. The driver now tracks which keys the source holds, including +queued batches, in write order: + +- A row for an absent key is written as `insert`. Later copies are updates. +- A batch may carry `deletes`. Only keys the source holds apply. +- The model receives the same resolved batch and removes deleted keys from + its base. A source delete acknowledges no request. + +A runtime witness requires 40 fixed-campaign histories to write source inserts +and deletes. The extended grammar passes on `main`. + +| Run | Result | +| --- | --- | +| Retention owner, B4 applied | 3 of 97 fail | +| Retention owner, B2 applied | the pinned replay fails; the fixed campaign does not reach it | +| Retention owner, F2 applied | 1 of 97 fail | +| Cleanup/restart owner, G4 applied | 1 of 41 fail | +| Idle-cleanup owner, H3 applied | 1 of 1 fail | + +## Survivors that change no observation + +These were classified with evidence and need no witness. The port can delete +the code under the last two. + +- A2 to A4: the truncate reapply search and filter are dead. +- G1, G3, H2, I3: stale state that every reader ignores, or a cancellation that + is a no-op on every reached history. +- D1, D3: the recompute delete filter's inner branch ran 0 times in 7,871 + tests and in targeted histories. Recompute keeps optimistic layers for keys + with pending sync operations, so no such delete forms. + +## Limits + +- H3 depends on a browser idle scheduler. The suite's Node polyfill always + reports a timed-out deadline. +- Source deletes run only after a batch's rows. A delete before an insert of + the same key in one batch is outside the grammar. +- The driver drops a source delete for a key the source lacks, so the grammar + never sends one. On a plain Collection, such a delete is a silent no-op. Its + effect on an optimistic layer for the same key is unverified. The + optimistic-history oracle owns that open cell; it needs the contract for the + absent-key delete and a witness with an active and an accepted request on + the key. +- The idle-cleanup test needs per-file module isolation, the Vitest default. + Without it, the binding check fails with an explicit error. +- The fallback that derives previous `$synced`/`$origin` from completed + request keys still has a survivor (see the + [absent-key insert review](2026-10-02-insert-for-absent-key.md)). + +## Verification + +`packages/db` Vitest, typecheck off: 201 files, 7,862 tests. `tsc --noEmit` +reports only the `tests/conformance` errors that need a built `dist`. diff --git a/docs/contributing/oracle-reviews/2026-10-02-truncate-raw-subscriber.md b/docs/contributing/oracle-reviews/2026-10-02-truncate-raw-subscriber.md new file mode 100644 index 0000000000..e68c8e64df --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-02-truncate-raw-subscriber.md @@ -0,0 +1,79 @@ +# One insert per key after a truncate + +Pre-repair executable revision: `da4e6b7cd` (oracle extension on `201220b51`). +Repair executable revisions: `67b5f73a1`, then `64da25f00`. + +## Law + +Each change message must be valid for a consumer that has applied every +earlier message: an insert names an absent key, and an update or delete names +a present one. Authority: the change-message contract in +`packages/db/tests/change-event-history-oracle.test.ts` and issue #1901. A +truncate batch is no exception. After its delete prefix, it must insert each +visible key at most once. + +## Gap + +A truncate commit re-applies the optimistic upserts and publishes each one as +an insert. The reapply loop in `state.ts` meant to replace the server's insert +for the same key. It ran before the changed-key loop that pushes that insert, +so its search never matched. When a key was both re-applied and changed by +the commit, the batch inserted it two times. + +The optimistic-history oracle missed it because its only subscriber used +`includeInitialState: true`. That subscription filters keys it already sent, +so the second insert never reached the replica. The change-event oracle has a +strict mirror but uses a local-only Collection, which never truncates. + +CodeRabbit found it on PR #2004. The state-stack mutation round 2 had already +listed the redundant insert push as reached in 72 cases on `main`, but no +observation showed it there. + +## Repair and witnesses + +- The optimistic-history driver adds a raw subscriber that starts from the + visible rows. Its batches pass the same event-semantics check as the main + subscriber, and its replica must equal the model at each checkpoint. +- The changed-key loop skips keys that the truncate already published. +- `collection-sync-reentrancy.test.ts` pinned the duplicate in its trace + `[1, 2, 3, 1, 3, 1, 2]`. The trace is now `[1, 2, 3, 1, 3, 2]`. + +## Follow-up from review: `64da25f00` + +**Ready callback during a truncate.** A truncate marks the Collection ready +before its changed-key loop runs. A ready callback that edits a replaced key +adds an optimistic upsert that the batch has not published. The first repair +read the live upsert map, so it skipped that key's insert, and the subscriber +lost the row. The loop now skips only the keys that the reapply loop +published, frozen before `markReady`. CodeRabbit found this on #2005. The +optimistic-history grammar has no ready callbacks, so it could not reach this +history. A witness in `collection-sync-reentrancy.test.ts` covers +`onFirstReady` and `status:change` callbacks. It fails on `67b5f73a1` and +passes on `main` and `64da25f00`. + +**Accepted delete under an active insert.** A random campaign of the extended +oracle found a second invalid insert on `main`, with no truncate. A completed +direct delete retires on the next sync commit. When an active optimistic +insert covered the key, retirement added the key to the changed keys without +the row that subscribers last saw, so the commit inserted it again. Upsert +retirement already recorded that row. Both loops now share one helper. A +pinned replay in `optimistic-history-publication-oracle.test.ts` fails on `main` for +an immediate commit. A non-immediate commit waits for the active insert, so +it passes on both. + +## ORC outcomes + +- ORC-002 independent judgment: the replica check uses the model's visible rows + and the protocol rule, not production event code. +- ORC-006 checker calibration: on `da4e6b7cd` the extended oracle fails 7 cases + in the retention oracle, the settlement replays, and both random campaigns. + A wrong repair that skips every changed key during a truncate fails 2 cases. +- ORC-007 fixed and random campaigns: the fixed and random campaigns pass on + `64da25f00`, including three runs at 20 times the usual count. The + `@tanstack/db` suite passes 236 files and 8,178 tests. + +## Limits + +The raw subscriber has no `where` clause and no ordering. Filtered and limited +raw subscriptions through a truncate remain with the WHERE predicate +publication owner, which lists truncate as outside its scope. diff --git a/docs/contributing/oracle-reviews/2026-10-03-oracle-naming-scope.json b/docs/contributing/oracle-reviews/2026-10-03-oracle-naming-scope.json new file mode 100644 index 0000000000..a981dfb858 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-03-oracle-naming-scope.json @@ -0,0 +1,5189 @@ +{ + "reviewedImplementation": "fe40ab59191d046bda12cc9563cdc4a82b5fd97c", + "classificationEvidenceRevision": "134b7e8bacce5999ab7e06da96d5dbe9989820f5", + "supersedesNamingInventory": "2026-10-02-glossary-alignment.json", + "scope": "Conservative naming classification of the 251 previously inventoried files. Counts are files, not distinct oracle mechanisms. This is not a repository-wide registry or a new guide-conformance audit.", + "counts": { + "driver-or-fixture": 34, + "example-test": 67, + "oracle-owner": 121, + "oracle-definition": 10, + "harness-test": 17, + "documentation": 2 + }, + "retainedPrRenames": { + "packages/browser-db-sqlite-persistence/e2e/shared-driver-fairness.opfs.spec.ts": "packages/browser-db-sqlite-persistence/e2e/shared-driver-fairness-oracle.opfs.spec.ts", + "packages/browser-db-sqlite-persistence/tests/browser-coordinator.test.ts": "packages/browser-db-sqlite-persistence/tests/browser-coordinator-oracle.test.ts", + "packages/db-collection-e2e/src/suites/joins.suite.ts": "packages/db-collection-e2e/src/suites/joins-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/live-updates.suite.ts": "packages/db-collection-e2e/src/suites/live-updates-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/moves.suite.ts": "packages/db-collection-e2e/src/suites/moves-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/pagination.suite.ts": "packages/db-collection-e2e/src/suites/pagination-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/predicates.suite.ts": "packages/db-collection-e2e/src/suites/predicates-oracle.suite.ts", + "packages/db-collection-e2e/src/utils/notification-laws.ts": "packages/db-collection-e2e/src/utils/notification-laws-oracle.ts", + "packages/db-ivm/tests/hash-failure-retry.property.test.ts": "packages/db-ivm/tests/hash-failure-retry-oracle.property.test.ts", + "packages/db-ivm/tests/hash-graph.property.test.ts": "packages/db-ivm/tests/hash-graph-oracle.property.test.ts", + "packages/db-ivm/tests/hash-mixed-graph.property.test.ts": "packages/db-ivm/tests/hash-mixed-graph-oracle.property.test.ts", + "packages/db-ivm/tests/hash.property.test.ts": "packages/db-ivm/tests/hash-oracle.property.test.ts", + "packages/db-ivm/tests/incrementalization-law.property.test.ts": "packages/db-ivm/tests/incrementalization-law-oracle.property.test.ts", + "packages/db-ivm/tests/incrementalization-law.ts": "packages/db-ivm/tests/incrementalization-law-oracle.ts", + "packages/db-ivm/tests/operators/topKWithFractionalIndex.test.ts": "packages/db-ivm/tests/operators/topKWithFractionalIndex-oracle.test.ts", + "packages/db-ivm/tests/operators/topk-batch-contract.test.ts": "packages/db-ivm/tests/operators/topk-batch-contract-oracle.test.ts", + "packages/db-sqlite-persistence-core/tests/persisted.test.ts": "packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts", + "packages/db-sqlite-persistence-core/tests/shared-logical-scheduling.test.ts": "packages/db-sqlite-persistence-core/tests/shared-logical-scheduling-oracle.test.ts", + "packages/db-sqlite-persistence-core/tests/sqlite-core-adapter.test.ts": "packages/db-sqlite-persistence-core/tests/sqlite-core-adapter-oracle.test.ts", + "packages/db-sqlite-persistence-core/tests/sqlite-resume-snapshot.test.ts": "packages/db-sqlite-persistence-core/tests/sqlite-resume-snapshot-oracle.test.ts", + "packages/db/tests/SortedMap.test.ts": "packages/db/tests/SortedMap-oracle.test.ts", + "packages/db/tests/cleanup-queue.property.test.ts": "packages/db/tests/cleanup-queue-oracle.property.test.ts", + "packages/db/tests/collection-subscription-lifecycle-grammar.ts": "packages/db/tests/collection-subscription-lifecycle-grammar-oracle.ts", + "packages/db/tests/collection-subscription-lifecycle-history.property.test.ts": "packages/db/tests/collection-subscription-lifecycle-history-oracle.property.test.ts", + "packages/db/tests/collection-subscription-lifecycle-publication.property.test.ts": "packages/db/tests/collection-subscription-lifecycle-publication-oracle.property.test.ts", + "packages/db/tests/collection-sync-reentrancy.test.ts": "packages/db/tests/collection-sync-reentrancy-oracle.test.ts", + "packages/db/tests/comparison.property.test.ts": "packages/db/tests/comparison-oracle.property.test.ts", + "packages/db/tests/conformance/infinite-suite.ts": "packages/db/tests/conformance/infinite-suite-oracle.ts", + "packages/db/tests/conformance/page-laws.ts": "packages/db/tests/conformance/page-laws-oracle.ts", + "packages/db/tests/cursor.property.test.ts": "packages/db/tests/cursor-oracle.property.test.ts", + "packages/db/tests/index-update.property.test.ts": "packages/db/tests/index-update-oracle.property.test.ts", + "packages/db/tests/live-query-observer-history.property.test.ts": "packages/db/tests/live-query-observer-history-oracle.property.test.ts", + "packages/db/tests/local-only-direct-write.test.ts": "packages/db/tests/local-only-direct-write-oracle.test.ts", + "packages/db/tests/notification-laws.test.ts": "packages/db/tests/notification-laws-oracle.test.ts", + "packages/db/tests/optimistic-composition.test.ts": "packages/db/tests/optimistic-composition-oracle.test.ts", + "packages/db/tests/optimistic-history-outcomes.test.ts": "packages/db/tests/optimistic-history-outcomes-oracle.test.ts", + "packages/db/tests/optimistic-history-publication.test.ts": "packages/db/tests/optimistic-history-publication-oracle.test.ts", + "packages/db/tests/optimistic-settlement-boundaries.test.ts": "packages/db/tests/optimistic-settlement-boundaries-oracle.test.ts", + "packages/db/tests/proxy-detachment-contract.test.ts": "packages/db/tests/proxy-detachment-contract-oracle.test.ts", + "packages/db/tests/proxy-iteration-contract.test.ts": "packages/db/tests/proxy-iteration-contract-oracle.test.ts", + "packages/db/tests/proxy-native-methods.property.test.ts": "packages/db/tests/proxy-native-methods-oracle.property.test.ts", + "packages/db/tests/proxy.test.ts": "packages/db/tests/proxy-oracle.test.ts", + "packages/db/tests/query/compiler/evaluators.test.ts": "packages/db/tests/query/compiler/evaluators-oracle.test.ts", + "packages/db/tests/query/derived-delete-reconciliation.test.ts": "packages/db/tests/query/derived-delete-reconciliation-oracle.test.ts", + "packages/db/tests/query/ir-stable-identity.test.ts": "packages/db/tests/query/ir-stable-identity-oracle.test.ts", + "packages/db/tests/query/ordered-source-loader.test.ts": "packages/db/tests/query/ordered-source-loader-oracle.test.ts", + "packages/db/tests/query/ordered-source-loader-state.test.ts": "packages/db/tests/query/ordered-source-loader-state-oracle.test.ts", + "packages/db/tests/query/pooled-live-query-gc.test.ts": "packages/db/tests/query/pooled-live-query-gc-oracle.test.ts", + "packages/db/tests/reference-expression.ts": "packages/db/tests/reference-expression-oracle.ts", + "packages/db/tests/utils.property.test.ts": "packages/db/tests/utils-oracle.property.test.ts", + "packages/electric-db-collection/e2e/sql-predicate-semantics.e2e.test.ts": "packages/electric-db-collection/e2e/sql-predicate-semantics-oracle.e2e.test.ts", + "packages/electric-db-collection/tests/electric-descriptor-isolation.test.ts": "packages/electric-db-collection/tests/electric-descriptor-isolation-oracle.test.ts", + "packages/electric-db-collection/tests/electric-sdk-delivery.property.test.ts": "packages/electric-db-collection/tests/electric-sdk-delivery-oracle.property.test.ts", + "packages/electric-db-collection/tests/pg-serializer.property.test.ts": "packages/electric-db-collection/tests/pg-serializer-oracle.property.test.ts", + "packages/offline-transactions/tests/KeyScheduler.property.test.ts": "packages/offline-transactions/tests/KeyScheduler-oracle.property.test.ts", + "packages/offline-transactions/tests/fifo-retry.property.test.ts": "packages/offline-transactions/tests/fifo-retry-oracle.property.test.ts", + "packages/offline-transactions/tests/leadership-replay.property.test.ts": "packages/offline-transactions/tests/leadership-replay-oracle.property.test.ts", + "packages/offline-transactions/tests/transaction-serializer.property.test.ts": "packages/offline-transactions/tests/transaction-serializer-oracle.property.test.ts", + "packages/offline-transactions/tests/transaction-settlement.property.test.ts": "packages/offline-transactions/tests/transaction-settlement-oracle.property.test.ts", + "packages/query-db-collection/tests/cursor-pagination.integration.test.ts": "packages/query-db-collection/tests/cursor-pagination-oracle.integration.test.ts", + "packages/query-db-collection/tests/cursor-pagination.no-peek.integration.test.ts": "packages/query-db-collection/tests/cursor-pagination-oracle.no-peek.integration.test.ts", + "packages/query-db-collection/tests/cursor-pagination/model.ts": "packages/query-db-collection/tests/cursor-pagination/model-oracle.ts", + "packages/react-native-db-sqlite-persistence/tests/op-sqlite-driver.test.ts": "packages/react-native-db-sqlite-persistence/tests/op-sqlite-driver-oracle.test.ts" + }, + "reversedPrRenames": { + "packages/angular-db/tests/conformance-oracle.test.ts": "packages/angular-db/tests/conformance.test.ts", + "packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle-oracle.opfs.spec.ts": "packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle.opfs.spec.ts", + "packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle-oracle.opfs.ts": "packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle.opfs.ts", + "packages/browser-db-sqlite-persistence/e2e/electric-immediate-reload-oracle.opfs.spec.ts": "packages/browser-db-sqlite-persistence/e2e/electric-immediate-reload.opfs.spec.ts", + "packages/browser-db-sqlite-persistence/e2e/electric-immediate-reload-oracle.opfs.ts": "packages/browser-db-sqlite-persistence/e2e/electric-immediate-reload.opfs.ts", + "packages/browser-db-sqlite-persistence/e2e/electric-resume-two-tab-oracle.opfs.spec.ts": "packages/browser-db-sqlite-persistence/e2e/electric-resume-two-tab.opfs.spec.ts", + "packages/browser-db-sqlite-persistence/e2e/electric-resume-two-tab-oracle.opfs.ts": "packages/browser-db-sqlite-persistence/e2e/electric-resume-two-tab.opfs.ts", + "packages/browser-db-sqlite-persistence/e2e/open-timeout-oracle.opfs.spec.ts": "packages/browser-db-sqlite-persistence/e2e/open-timeout.opfs.spec.ts", + "packages/browser-db-sqlite-persistence/e2e/remote-subset-two-tab-oracle.opfs.spec.ts": "packages/browser-db-sqlite-persistence/e2e/remote-subset-two-tab.opfs.spec.ts", + "packages/browser-db-sqlite-persistence/e2e/remote-subset-two-tab-oracle.opfs.ts": "packages/browser-db-sqlite-persistence/e2e/remote-subset-two-tab.opfs.ts", + "packages/browser-db-sqlite-persistence/e2e/shared-driver-fairness-oracle.opfs.ts": "packages/browser-db-sqlite-persistence/e2e/shared-driver-fairness.opfs.ts", + "packages/capacitor-db-sqlite-persistence/e2e/capacitor-persisted-collection-conformance-suite-oracle.ts": "packages/capacitor-db-sqlite-persistence/e2e/capacitor-persisted-collection-conformance-suite.ts", + "packages/capacitor-db-sqlite-persistence/e2e/shared/register-persisted-collection-conformance-suite-oracle.ts": "packages/capacitor-db-sqlite-persistence/e2e/shared/register-persisted-collection-conformance-suite.ts", + "packages/db-collection-e2e/src/fixtures/persisted-conformance-manifest-oracle.ts": "packages/db-collection-e2e/src/fixtures/persisted-conformance-manifest.ts", + "packages/db-collection-e2e/src/suites/collation-oracle.suite.ts": "packages/db-collection-e2e/src/suites/collation.suite.ts", + "packages/db-collection-e2e/src/suites/deduplication-oracle.suite.ts": "packages/db-collection-e2e/src/suites/deduplication.suite.ts", + "packages/db-collection-e2e/src/suites/mutations-oracle.suite.ts": "packages/db-collection-e2e/src/suites/mutations.suite.ts", + "packages/db-collection-e2e/src/suites/progressive-oracle.suite.ts": "packages/db-collection-e2e/src/suites/progressive.suite.ts", + "packages/db-collection-e2e/src/utils/assertions-oracle.ts": "packages/db-collection-e2e/src/utils/assertions.ts", + "packages/db-ivm/tests/hash-session-oracle.ts": "packages/db-ivm/tests/hash-session.ts", + "packages/db-ivm/tests/hash-session-replay-oracle.test.ts": "packages/db-ivm/tests/hash-session-replay.test.ts", + "packages/db-ivm/tests/hash-work-oracle.test.ts": "packages/db-ivm/tests/hash-work.test.ts", + "packages/db-ivm/tests/operators/topKWithIndex-oracle.test.ts": "packages/db-ivm/tests/operators/topKWithIndex.test.ts", + "packages/db-sqlite-persistence-core/tests/contracts/driver-admission-laws-oracle.ts": "packages/db-sqlite-persistence-core/tests/contracts/driver-admission-laws.ts", + "packages/db-sqlite-persistence-core/tests/contracts/persisted-collection-conformance-contract-oracle.ts": "packages/db-sqlite-persistence-core/tests/contracts/persisted-collection-conformance-contract.ts", + "packages/db-sqlite-persistence-core/tests/contracts/runtime-bridge-e2e-contract-oracle.ts": "packages/db-sqlite-persistence-core/tests/contracts/runtime-bridge-e2e-contract.ts", + "packages/db-sqlite-persistence-core/tests/contracts/runtime-persistence-contract-oracle.ts": "packages/db-sqlite-persistence-core/tests/contracts/runtime-persistence-contract.ts", + "packages/db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract-oracle.ts": "packages/db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract.ts", + "packages/db-sqlite-persistence-core/tests/contracts/sqlite-driver-contract-oracle.ts": "packages/db-sqlite-persistence-core/tests/contracts/sqlite-driver-contract.ts", + "packages/db-sqlite-persistence-core/tests/persisted-real-adapter-lifecycle-oracle.test.ts": "packages/db-sqlite-persistence-core/tests/persisted-real-adapter-lifecycle.test.ts", + "packages/db-sqlite-persistence-core/tests/runtime-restart-checker-oracle.test.ts": "packages/db-sqlite-persistence-core/tests/runtime-restart-checker.test.ts", + "packages/db-sqlite-persistence-core/tests/sqlite-driver-admission-laws-oracle.test.ts": "packages/db-sqlite-persistence-core/tests/sqlite-driver-admission-laws.test.ts", + "packages/db/tests/adapter-schema-transform-conformance-oracle.test-d.ts": "packages/db/tests/adapter-schema-transform-conformance.test-d.ts", + "packages/db/tests/collection-gc-clock-oracle.test.ts": "packages/db/tests/collection-gc-clock.test.ts", + "packages/db/tests/collection-idle-cleanup-oracle.test.ts": "packages/db/tests/collection-idle-cleanup.test.ts", + "packages/db/tests/conformance-disabled-laws-oracle.test.ts": "packages/db/tests/conformance-disabled-laws.test.ts", + "packages/db/tests/conformance-infinite-calibration-oracle.test.ts": "packages/db/tests/conformance-infinite-calibration.test.ts", + "packages/db/tests/conformance-infinite-demand-oracle.test.ts": "packages/db/tests/conformance-infinite-demand.test.ts", + "packages/db/tests/conformance-page-laws-oracle.test.ts": "packages/db/tests/conformance-page-laws.test.ts", + "packages/db/tests/conformance-registration-oracle.test.ts": "packages/db/tests/conformance-registration.test.ts", + "packages/db/tests/conformance-result-laws-oracle.test.ts": "packages/db/tests/conformance-result-laws.test.ts", + "packages/db/tests/conformance-scenario-lifetime-oracle.test.ts": "packages/db/tests/conformance-scenario-lifetime.test.ts", + "packages/db/tests/conformance-scenario-sources-oracle.test.ts": "packages/db/tests/conformance-scenario-sources.test.ts", + "packages/db/tests/conformance-scope-setup-oracle.test.ts": "packages/db/tests/conformance-scope-setup.test.ts", + "packages/db/tests/conformance/contract-oracle.ts": "packages/db/tests/conformance/contract.ts", + "packages/db/tests/conformance/disabled-laws-oracle.ts": "packages/db/tests/conformance/disabled-laws.ts", + "packages/db/tests/conformance/infinite-contract-oracle.ts": "packages/db/tests/conformance/infinite-contract.ts", + "packages/db/tests/conformance/infinite-on-demand-oracle.ts": "packages/db/tests/conformance/infinite-on-demand.ts", + "packages/db/tests/conformance/result-laws-oracle.ts": "packages/db/tests/conformance/result-laws.ts", + "packages/db/tests/conformance/suite-oracle.ts": "packages/db/tests/conformance/suite.ts", + "packages/db/tests/db-client-oracle.test-d.ts": "packages/db/tests/db-client.test-d.ts", + "packages/db/tests/db-client-oracle.test.ts": "packages/db/tests/db-client.test.ts", + "packages/db/tests/live-query-observer-oracle.test.ts": "packages/db/tests/live-query-observer.test.ts", + "packages/db/tests/observer-cleanup-restart-oracle.test.ts": "packages/db/tests/observer-cleanup-restart.test.ts", + "packages/db/tests/query/aggregate-value-contracts-oracle.test-d.ts": "packages/db/tests/query/aggregate-value-contracts.test-d.ts", + "packages/db/tests/query/builder/ref-proxy-oracle.test.ts": "packages/db/tests/query/builder/ref-proxy.test.ts", + "packages/db/tests/query/compiler/lazy-targets-oracle.test.ts": "packages/db/tests/query/compiler/lazy-targets.test.ts", + "packages/db/tests/query/includes-functional-input-boundary-oracle.test.ts": "packages/db/tests/query/includes-functional-input-boundary.test.ts", + "packages/db/tests/query/ordered-default-work-oracle.test.ts": "packages/db/tests/query/ordered-default-work.test.ts", + "packages/db/tests/query/ordered-demand-retirement-oracle.test.ts": "packages/db/tests/query/ordered-demand-retirement.test.ts", + "packages/db/tests/query/public-container-copy-oracle.test.ts": "packages/db/tests/query/public-container-copy.test.ts", + "packages/db/tests/query/query-api-type-algebra-oracle.test-d.ts": "packages/db/tests/query/query-api-type-algebra.test-d.ts", + "packages/db/tests/query/query-api-type-algebra-oracle.test.ts": "packages/db/tests/query/query-api-type-algebra.test.ts", + "packages/db/tests/query/replay-failure-boundary-oracle.test.ts": "packages/db/tests/query/replay-failure-boundary.test.ts", + "packages/db/tests/query/scheduler-oracle.test.ts": "packages/db/tests/query/scheduler.test.ts", + "packages/db/tests/query/validate-aliases-oracle.test.ts": "packages/db/tests/query/validate-aliases.test.ts", + "packages/db/tests/query/where-prefilter-property-visibility-oracle.test.ts": "packages/db/tests/query/where-prefilter-property-visibility.test.ts", + "packages/db/tests/replay-publication-storage-oracle.test.ts": "packages/db/tests/replay-publication-storage.test.ts", + "packages/db/tests/single-row-ref-proxy-oracle.test-d.ts": "packages/db/tests/single-row-ref-proxy.test-d.ts", + "packages/db/tests/sync-reused-row-oracle.test.ts": "packages/db/tests/sync-reused-row.test.ts", + "packages/db/tests/trace-runner-oracle.ts": "packages/db/tests/trace-runner.ts", + "packages/db/tests/transactions-oracle.test.ts": "packages/db/tests/transactions.test.ts", + "packages/db/tests/virtual-props-cache-oracle.test.ts": "packages/db/tests/virtual-props-cache.test.ts", + "packages/electric-db-collection/e2e/subset-sql-acceptance-oracle.e2e.test.ts": "packages/electric-db-collection/e2e/subset-sql-acceptance.e2e.test.ts", + "packages/electric-db-collection/tests/electric-resume-snapshot-races-oracle.test.ts": "packages/electric-db-collection/tests/electric-resume-snapshot-races.test.ts", + "packages/electric-db-collection/tests/electric-sdk-framing-oracle.test.ts": "packages/electric-db-collection/tests/electric-sdk-framing.test.ts", + "packages/electric-db-collection/tests/schema-transform-conformance-oracle.test-d.ts": "packages/electric-db-collection/tests/schema-transform-conformance.test-d.ts", + "packages/electron-db-sqlite-persistence/tests/electron-ipc-oracle.test.ts": "packages/electron-db-sqlite-persistence/tests/electron-ipc.test.ts", + "packages/expo-db-sqlite-persistence/e2e/mobile-persisted-collection-conformance-suite-oracle.ts": "packages/expo-db-sqlite-persistence/e2e/mobile-persisted-collection-conformance-suite.ts", + "packages/node-db-sqlite-persistence/tests/cached-upstream-history-oracle.test.ts": "packages/node-db-sqlite-persistence/tests/cached-upstream-history.test.ts", + "packages/node-db-sqlite-persistence/tests/node-driver-oracle.test.ts": "packages/node-db-sqlite-persistence/tests/node-driver.test.ts", + "packages/node-db-sqlite-persistence/tests/node-persistence-oracle.test.ts": "packages/node-db-sqlite-persistence/tests/node-persistence.test.ts", + "packages/node-db-sqlite-persistence/tests/node-sqlite-core-adapter-contract-oracle.test.ts": "packages/node-db-sqlite-persistence/tests/node-sqlite-core-adapter-contract.test.ts", + "packages/offline-transactions/tests/filtered-replay-work-oracle.test.ts": "packages/offline-transactions/tests/filtered-replay-work.test.ts", + "packages/offline-transactions/tests/indexeddb-write-settlement-oracle.test.ts": "packages/offline-transactions/tests/indexeddb-write-settlement.test.ts", + "packages/offline-transactions/tests/storage-delete-settlement-oracle.test.ts": "packages/offline-transactions/tests/storage-delete-settlement.test.ts", + "packages/powersync-db-collection/tests/schema-transform-conformance-oracle.test-d.ts": "packages/powersync-db-collection/tests/schema-transform-conformance.test-d.ts", + "packages/query-db-collection/tests/cursor-pagination-oracle.no-peek.test.ts": "packages/query-db-collection/tests/cursor-pagination.no-peek.test.ts", + "packages/query-db-collection/tests/cursor-pagination/no-peek-oracle.ts": "packages/query-db-collection/tests/cursor-pagination/no-peek.ts", + "packages/query-db-collection/tests/query-oracle.test-d.ts": "packages/query-db-collection/tests/query.test-d.ts", + "packages/react-db/tests/conformance-oracle.test.tsx": "packages/react-db/tests/conformance.test.tsx", + "packages/react-db/tests/infinite-query-conformance-oracle.test.tsx": "packages/react-db/tests/infinite-query-conformance.test.tsx", + "packages/react-native-db-sqlite-persistence/e2e/mobile-persisted-collection-conformance-suite-oracle.ts": "packages/react-native-db-sqlite-persistence/e2e/mobile-persisted-collection-conformance-suite.ts", + "packages/react-native-db-sqlite-persistence/tests/react-native-persistence-oracle.test.ts": "packages/react-native-db-sqlite-persistence/tests/react-native-persistence.test.ts", + "packages/rxdb-db-collection/tests/schema-transform-conformance-oracle.test-d.ts": "packages/rxdb-db-collection/tests/schema-transform-conformance.test-d.ts", + "packages/solid-db/tests/conformance-oracle.test.tsx": "packages/solid-db/tests/conformance.test.tsx", + "packages/svelte-db/tests/conformance-oracle.svelte.test.ts": "packages/svelte-db/tests/conformance.svelte.test.ts", + "packages/svelte-db/tests/infinite-query-conformance-oracle.svelte.test.ts": "packages/svelte-db/tests/infinite-query-conformance.svelte.test.ts", + "packages/tauri-db-sqlite-persistence/e2e/shared/register-persisted-collection-conformance-suite-oracle.ts": "packages/tauri-db-sqlite-persistence/e2e/shared/register-persisted-collection-conformance-suite.ts", + "packages/vue-db/tests/conformance-oracle.test.ts": "packages/vue-db/tests/conformance.test.ts", + "packages/vue-db/tests/infinite-query-conformance-oracle.test.ts": "packages/vue-db/tests/infinite-query-conformance.test.ts" + }, + "currentTerminologyFindingPaths": { + "01_collection-1": "packages/db/tests/collection-subscription-lifecycle-publication-oracle.property.test.ts", + "01_collection-2": "packages/db/tests/collection-subscription-lifecycle-grammar-oracle.ts", + "01_collection-3": "packages/db/tests/collection-subscription-lifecycle-oracle.test.ts", + "02_optimistic_values-1": "packages/db/tests/proxy-native-methods-oracle.property.test.ts", + "02_optimistic_values-2": "packages/db/tests/optimistic-history-oracle.ts", + "03_includes-1": "packages/db/tests/query/includes-oracle-helpers.ts", + "03_includes-2": "packages/db/tests/query/includes-collection-oracle.property.test.ts", + "03_includes-3": "packages/db/tests/query/includes-temporal-oracle.test.ts", + "04_acquisition-1": "packages/db/tests/query/load-subset-oracle.property.test.ts", + "04_acquisition-3": "packages/db/tests/query/ordered-lifecycle-oracle.property.test.ts", + "04_acquisition-4": "packages/db/tests/query/ordered-work-oracle.property.test.ts", + "04_acquisition-2": "packages/db/tests/query/pagination-oracle.property.test.ts", + "04_acquisition-5": "packages/db/tests/query/load-subset-replay-refinement-oracle.test.ts", + "05_query-2": "docs/contributing/glossary.md", + "05_query-1": "packages/db/tests/live-query-observer-history-oracle.property.test.ts", + "05_query-3": "packages/db/tests/query/scheduler.test.ts", + "06_ivm-1": "packages/db-ivm/tests/test-utils.ts", + "06_ivm-2": "packages/db-ivm/tests/index-refinement-oracle.property.test.ts", + "06_ivm-3": "packages/db/tests/cursor-oracle.property.test.ts", + "06_ivm-4": "packages/db/tests/utils-oracle.property.test.ts", + "07_query_adapter-4": "packages/query-db-collection/tests/ownership-lifecycle.oracle.test.ts", + "07_query_adapter-2": "packages/query-db-collection/tests/cursor-pagination.cache-oracle.test.ts", + "07_query_adapter-3": "packages/query-db-collection/tests/ownership-lifecycle.oracle.test.ts", + "07_query_adapter-5": "packages/query-db-collection/tests/load-subset-lifecycle-oracle.test.ts", + "08_providers-1": "packages/electric-db-collection/tests/electric-descriptor-isolation-oracle.test.ts", + "08_providers-2": "packages/powersync-db-collection/tests/correctness-oracle.test.ts", + "08_providers-3": "packages/electric-db-collection/tests/electric-oracle.property.test.ts", + "09_sqlite_core-2": "packages/db-sqlite-persistence-core/tests/sqlite-core-adapter-oracle.test.ts", + "09_sqlite_core-1": "packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts", + "09_sqlite_core-3": "packages/db-sqlite-persistence-core/tests/persisted-readiness-oracle.test.ts", + "10_browser_sqlite-1": "packages/browser-db-sqlite-persistence/tests/browser-coordinator-oracle.test.ts", + "10_browser_sqlite-2": "packages/browser-db-sqlite-persistence/tests/browser-coordinator-oracle.test.ts", + "10_browser_sqlite-3": "packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle.opfs.ts", + "11_offline-1": "packages/offline-transactions/tests/transaction-settlement-oracle.property.test.ts", + "11_offline-2": "packages/offline-transactions/tests/KeyScheduler-oracle.property.test.ts", + "11_offline-3": "docs/contributing/glossary.md", + "11_offline-4": "packages/offline-transactions/tests/indexeddb-write-settlement.test.ts", + "12_frameworks-1": "packages/db/tests/conformance/infinite-contract.ts", + "12_frameworks-2": "packages/db/tests/conformance/infinite-suite-oracle.ts" + }, + "files": [ + { + "path": "packages/angular-db/tests/conformance.test.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/angular-db/tests/conformance-oracle.test.ts", + "evidenceLines": [ + 62, + 144, + 183, + 224, + 283 + ], + "mechanism": "Angular source/mount/current/cleanup adapter passes a LiveQueryDriver to runSuite in packages/db/tests/conformance/suite-oracle.ts. Additional checks are fixed publication examples or driver/scope calibration; this file does not compute independent product expectations.", + "confidence": "high" + }, + { + "path": "packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle.opfs.spec.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle-oracle.opfs.spec.ts", + "evidenceLines": [ + 91, + 108, + 122, + 132, + 158, + 183, + 188, + 216 + ], + "mechanism": "One hardcoded PostgreSQL update history: initialRow and updatedRow are supplied literals, and expectExactRow merely wraps those literals into public/durable expected observations. The remaining tests check diagnostic helpers. There is no independent history reducer or generalized reference rule here.", + "confidence": "high" + }, + { + "path": "packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle.opfs.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle-oracle.opfs.ts", + "evidenceLines": [ + 122, + 188, + 207, + 221, + 285, + 290, + 318 + ], + "mechanism": "Holds production sync begin/commit and hydration return, records applied-receipt reach and public/durable rows, and exposes a browser probe. It does not compute an independently expected result.", + "ownerPaths": [ + "packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle.opfs.spec.ts" + ], + "confidence": "high" + }, + { + "path": "packages/browser-db-sqlite-persistence/e2e/electric-immediate-reload.opfs.spec.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/browser-db-sqlite-persistence/e2e/electric-immediate-reload-oracle.opfs.spec.ts", + "evidenceLines": [ + 88, + 101, + 151, + 158, + 217, + 229, + 239, + 252, + 263 + ], + "mechanism": "Single insert/hard-close/reopen regression with a literal row and manually enumerated phase/public/durable expectations. expectedPublicObservation only projects the supplied expected record; the PostgreSQL SELECT verifies the same literal, rather than supplying a differential reference computation. Additional cases test polling/diagnostic helpers.", + "confidence": "high" + }, + { + "path": "packages/browser-db-sqlite-persistence/e2e/electric-immediate-reload.opfs.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/browser-db-sqlite-persistence/e2e/electric-immediate-reload-oracle.opfs.ts", + "evidenceLines": [ + 103, + 178, + 184, + 195, + 201, + 205 + ], + "mechanism": "Creates the Electric/persisted Collection, parks its durable adapter write, records real public/durable rows, and exposes insert/recovery controls. Expected-result judgments live in the spec.", + "ownerPaths": [ + "packages/browser-db-sqlite-persistence/e2e/electric-immediate-reload.opfs.spec.ts" + ], + "confidence": "high" + }, + { + "path": "packages/browser-db-sqlite-persistence/e2e/electric-resume-two-tab.opfs.spec.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/browser-db-sqlite-persistence/e2e/electric-resume-two-tab-oracle.opfs.spec.ts", + "evidenceLines": [ + 72, + 80, + 137, + 156, + 251, + 261, + 312 + ], + "mechanism": "Two named legacy-loss examples run the same fixed seed/add-row/handoff/reopen trace. expectedRows only sorts the explicitly supplied fixture rows; rowsAfter is a case-table expectation. No independent transition model or reusable query-result computation is exercised.", + "confidence": "high" + }, + { + "path": "packages/browser-db-sqlite-persistence/e2e/electric-resume-two-tab.opfs.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/browser-db-sqlite-persistence/e2e/electric-resume-two-tab-oracle.opfs.ts", + "evidenceLines": [ + 126, + 175, + 302, + 311, + 320, + 346 + ], + "mechanism": "Creates real host Collections, edits durable storage to establish legacy-loss input, and transports actual Collection/resume observations. Poisoning checks confirm fixture setup, not an independent expected-result law.", + "ownerPaths": [ + "packages/browser-db-sqlite-persistence/e2e/electric-resume-two-tab.opfs.spec.ts" + ], + "confidence": "high" + }, + { + "path": "packages/browser-db-sqlite-persistence/e2e/open-timeout.opfs.spec.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/browser-db-sqlite-persistence/e2e/open-timeout-oracle.opfs.spec.ts", + "evidenceLines": [ + 36, + 41, + 101, + 115, + 121, + 125, + 139 + ], + "mechanism": "One real-Web-Lock receiving witness with a fixed 15-second deadline and hardcoded TimeoutError, removed pending lock, and SELECT 1 expectations. Important host regression, but no independent generalized deadline/state oracle is invoked.", + "confidence": "high" + }, + { + "path": "packages/browser-db-sqlite-persistence/e2e/remote-subset-two-tab.opfs.spec.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/browser-db-sqlite-persistence/e2e/remote-subset-two-tab-oracle.opfs.spec.ts", + "evidenceLines": [ + 24, + 28, + 40, + 43, + 59, + 69, + 77, + 85 + ], + "mechanism": "One fixed item ID and two-tab load, followed by one invalid-wire value. Expected row, route flags, counts, and error path are literal example assertions, without an independent reference computation.", + "confidence": "high" + }, + { + "path": "packages/browser-db-sqlite-persistence/e2e/remote-subset-two-tab.opfs.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/browser-db-sqlite-persistence/e2e/remote-subset-two-tab-oracle.opfs.ts", + "evidenceLines": [ + 81, + 138, + 141, + 186, + 203, + 210, + 215 + ], + "mechanism": "Supplies a controlled QueryClient-backed provider, records local request fields and wire posts, starts a real filtered live query, and exposes its actual rows. Parsing the request in the provider supplies test data, not expected-result judgment.", + "ownerPaths": [ + "packages/browser-db-sqlite-persistence/e2e/remote-subset-two-tab.opfs.spec.ts" + ], + "confidence": "high" + }, + { + "path": "packages/browser-db-sqlite-persistence/e2e/shared-driver-fairness-oracle.opfs.spec.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/browser-db-sqlite-persistence/e2e/shared-driver-fairness-oracle.opfs.spec.ts", + "evidenceLines": [ + 81, + 84, + 115, + 128 + ], + "mechanism": "The fixed native storm executes the independently defined K=1 fairness rule through its page companion and rejects any returned violation. This is a narrow receiving oracle, not merely the fixed row assertions: the shared checker derives permitted persist IDs/counts from the scenario and compares every hydrate checkpoint. Fixed native scenarios do not make that generalized independent rule a hardcoded trace. Borderline ownership is split across the page and spec, but the spec owns pass/fail at the semantic checkpoint.", + "ownerPaths": [ + "packages/browser-db-sqlite-persistence/tests/shared-driver-fairness-oracle.ts", + "packages/browser-db-sqlite-persistence/e2e/shared-driver-fairness.opfs.ts" + ], + "confidence": "medium" + }, + { + "path": "packages/browser-db-sqlite-persistence/e2e/shared-driver-fairness.opfs.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/browser-db-sqlite-persistence/e2e/shared-driver-fairness-oracle.opfs.ts", + "evidenceLines": [ + 77, + 112, + 120, + 125, + 140 + ], + "mechanism": "Chooses two fixed host scenarios, runs the shared production driver with real OPFS, invokes the imported fairness checker, and transports its violation result. It defines neither the independent fairness rule nor test acceptance; the receiving spec rejects that result. Dedicated oracle support remains a fixture.", + "ownerPaths": [ + "packages/browser-db-sqlite-persistence/e2e/shared-driver-fairness-oracle.opfs.spec.ts", + "packages/browser-db-sqlite-persistence/tests/shared-driver-fairness-oracle.ts" + ], + "confidence": "high" + }, + { + "path": "packages/browser-db-sqlite-persistence/tests/browser-coordinator-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/browser-db-sqlite-persistence/tests/browser-coordinator-oracle.test.ts", + "evidenceLines": [ + 6419, + 6423, + 6472, + 6506, + 6513, + 6788, + 6843, + 6851 + ], + "mechanism": "Although mostly example tests, it also contains genuine generalized independent routing and retention laws. Generated collection counts/registration permutations/target IDs drive real coordinator RPC; expected call counts are computed solely from target identity (index === targetIndex ? 1 : 0). A separate generated release-order ledger decrements independent remaining count and compares both retained maps after each action.", + "confidence": "high" + }, + { + "path": "packages/browser-db-sqlite-persistence/tests/opfs-page-lifecycle-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/browser-db-sqlite-persistence/tests/opfs-page-lifecycle-oracle.test.ts", + "evidenceLines": [ + 371, + 478, + 486, + 547, + 585, + 588, + 605, + 1145, + 1163 + ], + "mechanism": "Computes the expected raw settlement ledger from delivery stage, request count, release direction, and completed prefix. For mixed histories, the independent index predicate determines fulfilled versus rejected outcomes before comparison with actual production opener/query settlements. Existing pre-PR oracle name remains untouched.", + "confidence": "high" + }, + { + "path": "packages/browser-db-sqlite-persistence/tests/opfs-worker-diagnostics-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/browser-db-sqlite-persistence/tests/opfs-worker-diagnostics-oracle.test.ts", + "evidenceLines": [ + 61, + 66, + 211, + 216, + 279 + ], + "mechanism": "referenceMessage independently concatenates primary/cause fields from scalar input; generated error classes/messages invoke the production worker init handler and compare the complete response to that computation. Existing pre-PR name is untouched.", + "confidence": "high" + }, + { + "path": "packages/browser-db-sqlite-persistence/tests/per-collection-coordinator-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/browser-db-sqlite-persistence/tests/per-collection-coordinator-oracle.test.ts", + "evidenceLines": [ + 1664, + 1668, + 1670, + 1709, + 2125, + 2247, + 2778, + 2790, + 3391 + ], + "mechanism": "Contains explicit independent reusable models: remoteLeaseHistoryViolations computes expected loads/unloads/remaining acquisitions from the history, and PerCollectionRouteOracle maps owner plus Collection identity to expected adapter. Generated production histories compare their captured traces against those models. Existing pre-PR name is untouched.", + "confidence": "high" + }, + { + "path": "packages/browser-db-sqlite-persistence/tests/shared-driver-fairness-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/browser-db-sqlite-persistence/tests/shared-driver-fairness-oracle.test.ts", + "evidenceLines": [ + 235, + 256, + 260, + 270, + 653, + 702 + ], + "mechanism": "For generated logical admission histories, independently derives expected hydrate rows and persist FIFO order and applies findSharedDriverFairnessViolation to actual shared-driver observations. Fixed/random campaigns execute this comparison; the wrong-scheduler control checks sensitivity. Existing pre-PR name is untouched.", + "confidence": "high" + }, + { + "path": "packages/browser-db-sqlite-persistence/tests/shared-driver-fairness-oracle.ts", + "role": "oracle-definition", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/browser-db-sqlite-persistence/tests/shared-driver-fairness-oracle.ts", + "evidenceLines": [ + 438, + 449, + 454, + 463, + 471, + 475, + 486 + ], + "mechanism": "Defines createFairnessReference: the expected bound is min(persistCount, 1 + hydrateIndex * K), with FIFO permitted persist IDs. findSharedDriverFairnessViolation compares real checkpoint identities/counts/remaining work to that rule. Also contains production-driver mechanics, but its independent reusable checker establishes definition ownership. Existing pre-PR name is untouched.", + "ownerPaths": [ + "packages/browser-db-sqlite-persistence/tests/shared-driver-fairness-oracle.test.ts", + "packages/browser-db-sqlite-persistence/e2e/shared-driver-fairness-oracle.opfs.spec.ts" + ], + "confidence": "high" + }, + { + "path": "packages/capacitor-db-sqlite-persistence/e2e/capacitor-persisted-collection-conformance-suite.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/capacitor-db-sqlite-persistence/e2e/capacitor-persisted-collection-conformance-suite-oracle.ts", + "evidenceLines": [ + 13, + 17, + 25, + 29 + ], + "mechanism": "Creates a temporary SQLite database and platform harness, then registers the shared suite. No expected-result computation or product assertion is defined.", + "ownerPaths": [ + "packages/db-sqlite-persistence-core/tests/contracts/persisted-collection-conformance-contract.ts" + ], + "confidence": "high" + }, + { + "path": "packages/capacitor-db-sqlite-persistence/e2e/shared/register-persisted-collection-conformance-suite.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/capacitor-db-sqlite-persistence/e2e/shared/register-persisted-collection-conformance-suite-oracle.ts", + "evidenceLines": [ + 18, + 24, + 30, + 36, + 44 + ], + "mechanism": "Only lifecycle hooks, harness lookup, and invocation of the shared conformance registration function. It imports judgment; it does not own it.", + "ownerPaths": [ + "packages/db-sqlite-persistence-core/tests/contracts/persisted-collection-conformance-contract.ts" + ], + "confidence": "high" + }, + { + "path": "packages/db-collection-e2e/src/fixtures/persisted-conformance-manifest.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/db-collection-e2e/src/fixtures/persisted-conformance-manifest-oracle.ts", + "evidenceLines": [ + 13, + 129 + ], + "mechanism": "Literal persisted-conformance test-name manifest and name-formatting function support registration/native result collection. They neither run production nor judge a modeled result.", + "confidence": "high" + }, + { + "path": "packages/db-collection-e2e/src/suites/collation.suite.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db-collection-e2e/src/suites/collation-oracle.suite.ts", + "evidenceLines": [ + 160, + 161, + 173, + 188, + 218, + 252 + ], + "mechanism": "A fixed lexical witness inserts the five authored names A10, A2, Z, a, ä in a fixed permutation and compares real rows to that literal sequence. The local expectOrderedRows closure is exact equality against this example, not an independently computed collation rule; negative controls do not change its role.", + "confidence": "high" + }, + { + "path": "packages/db-collection-e2e/src/suites/deduplication.suite.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db-collection-e2e/src/suites/deduplication-oracle.suite.ts", + "evidenceLines": [ + 278, + 307, + 313, + 324, + 338, + 342 + ], + "mechanism": "Fixed concurrent-query examples assert sizes, nonemptiness, differing offsets, and equal result counts among identical queries. The 20-query burst compares every count to the first production count; it provides no independently computed deduplication/load model. This weak same-input peer comparison is borderline, so conservatively restore the example-suite name.", + "confidence": "medium" + }, + { + "path": "packages/db-collection-e2e/src/suites/joins-oracle.suite.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db-collection-e2e/src/suites/joins-oracle.suite.ts", + "evidenceLines": [ + 29, + 35, + 36, + 42, + 45, + 48, + 87, + 108, + 125 + ], + "mechanism": "Independent userPostPairs and withComments loops compute left-join pairs/triples from captured fixture rows, including unmatched children. Real query projections are compared against those reference rows through createJoinOracle, with independent fixture ordering for ordered cases.", + "confidence": "high" + }, + { + "path": "packages/db-collection-e2e/src/suites/live-updates-oracle.suite.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db-collection-e2e/src/suites/live-updates-oracle.suite.ts", + "evidenceLines": [ + 144, + 159, + 161, + 163, + 216 + ], + "mechanism": "Captures every real subscribeChanges batch alongside callback-visible complete rows and executes expectNotificationHistory on that production history. The imported checker independently reconstructs row state from each insert/update/delete and checks every callback and final row set.", + "confidence": "high" + }, + { + "path": "packages/db-collection-e2e/src/suites/moves-oracle.suite.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db-collection-e2e/src/suites/moves-oracle.suite.ts", + "evidenceLines": [ + 192, + 193, + 221, + 226, + 231, + 236, + 239, + 244, + 249, + 254, + 319 + ], + "mechanism": "Maintains independent activeUsers/posts maps from authored commands. check computes expected visible posts by filtering modeled posts through modeled user activity, compares complete production entries, and also applies notification-history reconstruction to real callback observations.", + "confidence": "high" + }, + { + "path": "packages/db-collection-e2e/src/suites/mutations.suite.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db-collection-e2e/src/suites/mutations-oracle.suite.ts", + "evidenceLines": [ + 188, + 192, + 194, + 200, + 376, + 383, + 389, + 394 + ], + "mechanism": "Authored insertion/deletion/update histories compare observed peers plus a particular inserted row, and a fixed eleven-row ordered witness checks literal slices before/after one age change. Generic equality/capture helpers do not independently evaluate a query or maintain a generalized mutation model. The insert-then-delete frame check has a metamorphic flavor; conservatively restore the example-suite name.", + "confidence": "medium" + }, + { + "path": "packages/db-collection-e2e/src/suites/pagination-oracle.suite.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db-collection-e2e/src/suites/pagination-oracle.suite.ts", + "evidenceLines": [ + 47, + 52, + 55, + 62, + 64, + 69, + 72, + 78, + 85, + 86 + ], + "mechanism": "createWindowOracle clones independent fixture rows, sorts with a test-side comparator, and computes ranked.slice(offset, offset + limit). It checks real query rows for exact cardinality, unique identities, unchanged values, and legal rank ties across window scenarios.", + "confidence": "high" + }, + { + "path": "packages/db-collection-e2e/src/suites/predicates-oracle.suite.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db-collection-e2e/src/suites/predicates-oracle.suite.ts", + "evidenceLines": [ + 76, + 78, + 79, + 90, + 103, + 104, + 179, + 200, + 218 + ], + "mechanism": "Computes expected rows by native fixture filtering (and independent ordering for orderedAliceRows), then captures real query values and compares their complete selected rows. The reference predicate evaluation is separate from the production query builder.", + "confidence": "high" + }, + { + "path": "packages/db-collection-e2e/src/suites/progressive.suite.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db-collection-e2e/src/suites/progressive-oracle.suite.ts", + "evidenceLines": [ + 350, + 355, + 360, + 368, + 372, + 391, + 396, + 399, + 403 + ], + "mechanism": "Fixed progressive-load smoke/regression cases check readiness, limits, literal predicates, and peer-query membership. The snapshot deduplication case runs three identical age=30 queries and compares them to the first result; there is no independent progressive/publication state model. Treat the limited peer comparison conservatively as an example.", + "confidence": "medium" + }, + { + "path": "packages/db-collection-e2e/src/utils/assertions.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/db-collection-e2e/src/utils/assertions-oracle.ts", + "evidenceLines": [ + 8, + 13, + 15, + 78, + 89, + 108, + 140, + 162 + ], + "mechanism": "Generic assertion utilities compare caller-supplied IDs/counts, apply a caller-supplied predicate, or check adjacent ordering. They do not derive complete product expectations or own a domain reference computation; the retained pagination/predicate suites own their reference fixture computations.", + "ownerPaths": [ + "packages/db-collection-e2e/src/suites/pagination-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/predicates-oracle.suite.ts" + ], + "confidence": "medium" + }, + { + "path": "packages/db-collection-e2e/src/utils/notification-laws-oracle.ts", + "role": "oracle-definition", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db-collection-e2e/src/utils/notification-laws-oracle.ts", + "evidenceLines": [ + 16, + 22, + 24, + 32, + 36, + 42, + 46 + ], + "mechanism": "expectNotificationHistory independently reconstructs a Map from notification events, checks event admission/previous values, and requires each callback-visible row set and final rows to equal that reconstructed state. Named suites execute it against real production callbacks.", + "ownerPaths": [ + "packages/db-collection-e2e/src/suites/live-updates-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/moves-oracle.suite.ts" + ], + "confidence": "high" + }, + { + "path": "packages/db-ivm/tests/d2-work-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db-ivm/tests/d2-work-oracle.test.ts", + "evidenceLines": [ + 47, + 53, + 61, + 62, + 66, + 73, + 74 + ], + "mechanism": "A bounded per-branch input ledger computes the expected invocation vector from the selected active branch and next branch, then compares every real D2 operator call count after each graph.run(). This is a small generalized work rule across branch positions; the separate reversed-registration case is a fixed example.", + "confidence": "medium" + }, + { + "path": "packages/db-ivm/tests/hash-failure-retry-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db-ivm/tests/hash-failure-retry-oracle.property.test.ts", + "evidenceLines": [ + 38, + 76, + 90, + 101, + 105, + 108, + 135 + ], + "mechanism": "expectRetryHistory derives getter-read counts from the generated rejecting position, failed-attempt count, and untouched suffix. It checks the thrown error and all read counts after each real hash attempt, compares the successful result with a separately built structural copy, and checks subsequent cache reuse.", + "confidence": "high" + }, + { + "path": "packages/db-ivm/tests/hash-graph-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db-ivm/tests/hash-graph-oracle.property.test.ts", + "evidenceLines": [ + 50, + 69, + 87, + 98, + 108, + 112, + 181 + ], + "mechanism": "An independent reachable-indegree/Kahn model predicts cycle rejection. For acyclic generated graphs, the checker unfolds shared nodes into fresh trees and requires real hash agreement; for reachable cycles it requires repeated rejection.", + "confidence": "high" + }, + { + "path": "packages/db-ivm/tests/hash-identity-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db-ivm/tests/hash-identity-oracle.property.test.ts", + "evidenceLines": [ + 135, + 182, + 233, + 752, + 758, + 765, + 775, + 834 + ], + "mechanism": "A spec-level canonical identity encoding derives expected equality independently of the production hasher. Generated specs are realized into runtime values and checked in both comparison directions and through hash compatibility, including a second module with forced collisions and separately constructed cyclic pairs.", + "confidence": "high" + }, + { + "path": "packages/db-ivm/tests/hash-mixed-graph-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db-ivm/tests/hash-mixed-graph-oracle.property.test.ts", + "evidenceLines": [ + 100, + 118, + 135, + 151, + 159, + 165, + 174 + ], + "mechanism": "The checker computes reachable acyclicity with an indegree model over a separate graph specification. Realizations mix object, array, Map, Set, and symbol edge carriers; cyclic results must reject and acyclic shared graphs must hash like their independent unfoldings.", + "confidence": "high" + }, + { + "path": "packages/db-ivm/tests/hash-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db-ivm/tests/hash-oracle.property.test.ts", + "evidenceLines": [ + 95, + 113, + 148, + 239, + 248, + 253, + 303, + 319 + ], + "mechanism": "Generated metamorphic laws compare hash results for independently constructed clones and reordered object properties. The concrete judgment includes insertion-order independence, equal byte/date contents, and declared type-marker distinctions; it is not only deterministic-output examples.", + "confidence": "high" + }, + { + "path": "packages/db-ivm/tests/hash-session.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/db-ivm/tests/hash-session-oracle.ts", + "evidenceLines": [ + 24, + 38, + 42, + 47, + 58, + 61, + 78 + ], + "mechanism": "captureHashSession reloads the production module while recording/replaying Math.random initialization draws and attaching environment metadata. HashReplayError preserves failure context. Tape validation judges capture/replay mechanics, not hash semantics; there is no independent product-result model here.", + "ownerPaths": [ + "packages/db-ivm/tests/hash-oracle.property.test.ts", + "packages/db-ivm/tests/hash-identity-oracle.property.test.ts" + ], + "confidence": "high" + }, + { + "path": "packages/db-ivm/tests/hash-session-replay.test.ts", + "role": "harness-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db-ivm/tests/hash-session-replay-oracle.test.ts", + "evidenceLines": [ + 14, + 40, + 51, + 63, + 66, + 78, + 87, + 97 + ], + "mechanism": "The main comparisons verify that captureHashSession reproduces the same module initialization and encounter order, including generated permutations, and rejects incomplete/excessive tapes. expectSessionLaws supplies fixed hash sanity examples across a few tapes; it does not make this replay-harness calibration an independent product owner.", + "ownerPaths": [ + "packages/db-ivm/tests/hash-oracle.property.test.ts", + "packages/db-ivm/tests/hash-identity-oracle.property.test.ts" + ], + "confidence": "high" + }, + { + "path": "packages/db-ivm/tests/hash-work.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db-ivm/tests/hash-work-oracle.test.ts", + "evidenceLines": [ + 5, + 30, + 83, + 100, + 105, + 128, + 137, + 143, + 148, + 164 + ], + "mechanism": "The allocation counter observes production work, but the tests judge fixed constructions with literal expectations: a depth-200 shared DAG must read 400 getters, one repeated Map pair must copy entries twice, and a table supplies hand-written maximum sizes for seven shapes. The two-attempt retry examples and single opaque-handle mutation example do not provide a reusable expected-work model. Conservatively restore the original name.", + "confidence": "medium" + }, + { + "path": "packages/db-ivm/tests/incrementalization-law-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db-ivm/tests/incrementalization-law-oracle.property.test.ts", + "evidenceLines": [ + 176, + 184, + 264, + 289, + 336, + 341, + 348, + 354, + 378 + ], + "mechanism": "Plain Map/loop/full-sort evaluators compute whole relations for consolidate, reduce, joins, grouped aggregates, and top-K. Generated input-world histories are sent through real D2 operators and judged by the companion incrementalization checker at each logical batch, including compatible split delivery.", + "confidence": "high" + }, + { + "path": "packages/db-ivm/tests/incrementalization-law-oracle.ts", + "role": "oracle-definition", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db-ivm/tests/incrementalization-law-oracle.ts", + "evidenceLines": [ + 109, + 136, + 168, + 309, + 326, + 338, + 347, + 355, + 371, + 435, + 457, + 465 + ], + "mechanism": "Owns the reusable weighted-relation model, signed integration and difference, legal partition grammar, and unary/binary refinement checks. The checks compare real emitted deltas and cumulative output with independently supplied full-recomputation results and compare atomic versus split delivery. This owns judgment as well as driver mechanics.", + "ownerPaths": [ + "packages/db-ivm/tests/incrementalization-law-oracle.property.test.ts" + ], + "confidence": "high" + }, + { + "path": "packages/db-ivm/tests/index-refinement-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db-ivm/tests/index-refinement-oracle.property.test.ts", + "evidenceLines": [ + 58, + 71, + 179, + 191, + 195, + 198, + 201, + 246 + ], + "mechanism": "expectedIndex independently folds each history prefix into nested native Maps using a declared type-and-value identity and signed multiplicities. After every real Index.addValue, the checker compares complete observed multiplicities and has() with that prefix model.", + "confidence": "high" + }, + { + "path": "packages/db-ivm/tests/multiset-consolidate-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db-ivm/tests/multiset-consolidate-oracle.property.test.ts", + "evidenceLines": [ + 106, + 154, + 163, + 408, + 412, + 414, + 428, + 437, + 544 + ], + "mechanism": "expectedConsolidation independently classifies the supported identity domain, groups records by direct primitive/reference or canonical structural equality, sums multiplicities, and retains the first record. Generated real MultiSet.consolidate output is checked for each exact group, weight, retained reference, omissions, duplicates, and unchanged inputs.", + "confidence": "high" + }, + { + "path": "packages/db-ivm/tests/operators/topKWithFractionalIndex-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db-ivm/tests/operators/topKWithFractionalIndex-oracle.test.ts", + "evidenceLines": [ + 86, + 95, + 125, + 138, + 148, + 168, + 196, + 239, + 243, + 279, + 311 + ], + "mechanism": "Despite many fixed examples later in the file, the generated sections maintain independent source Maps, recompute full ordered windows with sort/slice, and compare them with the cumulative signed TopKRelation after every real graph turn. The grammar includes replacements, transient cancellation, outer joins, and multiple production top-K variants.", + "confidence": "high" + }, + { + "path": "packages/db-ivm/tests/operators/topKWithIndex.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db-ivm/tests/operators/topKWithIndex-oracle.test.ts", + "evidenceLines": [ + 49, + 96, + 146, + 167, + 191, + 224, + 291, + 359, + 409, + 425, + 438 + ], + "mechanism": "The file supplies hand-written inputs and expected output arrays for initial windows and individual row changes. Its one TopKMessageTracker/TopKRelation use checks a fixed remove-b example against the literal rows a,c,d; the local helpers only sort observations. It contains no independent window recomputation or generalized history law of its own. Importing the shared checker is the borderline point; conservatively restore this filename.", + "confidence": "medium" + }, + { + "path": "packages/db-ivm/tests/operators/topk-batch-contract-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db-ivm/tests/operators/topk-batch-contract-oracle.test.ts", + "evidenceLines": [ + 16, + 22, + 26, + 129, + 161, + 177, + 182, + 202, + 203, + 213, + 228, + 230 + ], + "mechanism": "Parameterized value-pair constructors define equivalence relations independently of hashing, then generated payloads and delivery order cross those relations with collision and grouping modes. The cancellation rule judges real topKBatch output, and a real ordered D2 graph must retain exactly the equivalent old row or distinct replacement with unit weight.", + "confidence": "high" + }, + { + "path": "packages/db-ivm/tests/operators/topk-relation-oracle.test.ts", + "role": "harness-test", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db-ivm/tests/operators/topk-relation-oracle.test.ts", + "evidenceLines": [ + 3, + 19, + 20, + 41, + 44, + 77, + 84, + 98 + ], + "mechanism": "Only constructs TopKRelation checker instances and feeds them valid or deliberately corrupted observations to calibrate duplicate weight, retraction, index, payload, capture, and replay behavior. No real top-K production operator is exercised. The existing pre-PR oracle filename is outside the rename correction.", + "ownerPaths": [ + "packages/db-ivm/tests/operators/topKWithFractionalIndex-oracle.test.ts", + "packages/db-ivm/tests/operators/topk-support-window-oracle.test.ts" + ], + "confidence": "high" + }, + { + "path": "packages/db-ivm/tests/operators/topk-relation-oracle.ts", + "role": "oracle-definition", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db-ivm/tests/operators/topk-relation-oracle.ts", + "evidenceLines": [ + 27, + 33, + 38, + 49, + 51, + 59, + 64, + 67, + 75 + ], + "mechanism": "TopKRelation owns independent cumulative signed integration keyed by full modeled row/index identity. Its checker rejects nonunit residue, compares complete rows, derives numeric indexes from offset and position, or checks strict fractional-index order. This is reusable semantic judgment used by generated production owners; TopKMessageTracker additionally supplies capture.", + "ownerPaths": [ + "packages/db-ivm/tests/operators/topKWithFractionalIndex-oracle.test.ts", + "packages/db-ivm/tests/operators/topk-support-window-oracle.test.ts" + ], + "confidence": "high" + }, + { + "path": "packages/db-ivm/tests/operators/topk-support-window-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db-ivm/tests/operators/topk-support-window-oracle.test.ts", + "evidenceLines": [ + 37, + 124, + 128, + 154, + 169, + 196, + 229, + 236, + 247, + 252, + 258 + ], + "mechanism": "An independent native Map counts typed-key support. At each generated input or window-move checkpoint, a fixed independent key order is filtered by positive support and sliced by offset/limit, then compared with the accumulated real fractional-index output and reported window size. Ordinary top-K also checks preserved multiplicity against its running input count.", + "confidence": "high" + }, + { + "path": "packages/db-ivm/tests/temporal-group-key-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db-ivm/tests/temporal-group-key-oracle.test.ts", + "evidenceLines": [ + 27, + 70, + 78, + 86, + 101, + 103, + 109, + 113, + 122, + 127 + ], + "mechanism": "expectedGroupCounts independently counts fixture-declared identity classes, and observedGroupCounts executes real D2 groupBy and records group cardinalities, unit weights, and distinct output keys. The bounded Temporal-kind cross product is judged by that class-counting rule rather than output arrays calculated separately per kind. Its oracle is narrow and based on fixed equivalence-class assignments.", + "confidence": "medium" + }, + { + "path": "packages/db-sqlite-persistence-core/tests/contracts/driver-admission-laws.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/db-sqlite-persistence-core/tests/contracts/driver-admission-laws-oracle.ts", + "evidenceLines": [ + 4, + 8 + ], + "mechanism": "expectAdmissionHistory only maps rows to value and compares them to a caller-supplied expected string array. This is a generic equality wrapper, not an independent admission state model or reference rule.", + "confidence": "high" + }, + { + "path": "packages/db-sqlite-persistence-core/tests/contracts/persisted-collection-conformance-contract.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/db-sqlite-persistence-core/tests/contracts/persisted-collection-conformance-contract-oracle.ts", + "evidenceLines": [ + 34, + 39, + 47, + 49, + 52, + 55, + 58, + 61, + 64, + 67 + ], + "mechanism": "Only registers imported predicate/pagination/join/deduplication/collation/mutation/live-update suites according to capability flags. It computes no expected result and performs no independent comparison.", + "ownerPaths": [ + "packages/db-collection-e2e/src/suites/predicates-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/pagination-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/joins-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/deduplication.suite.ts", + "packages/db-collection-e2e/src/suites/collation.suite.ts", + "packages/db-collection-e2e/src/suites/mutations.suite.ts", + "packages/db-collection-e2e/src/suites/live-updates-oracle.suite.ts" + ], + "confidence": "high" + }, + { + "path": "packages/db-sqlite-persistence-core/tests/contracts/runtime-bridge-e2e-contract.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db-sqlite-persistence-core/tests/contracts/runtime-bridge-e2e-contract-oracle.ts", + "evidenceLines": [ + 31, + 34, + 39, + 48, + 50, + 51, + 52, + 81, + 92, + 118, + 124 + ], + "mechanism": "assertRuntimeRestartHistory encapsulates one fixed two-row write/restart/write example with literal rows and literal expected snapshots. The other exported suite cases are one fixed round-trip and fixed missing-collection error assertions. Sharing this scenario across hosts does not add an independent reference model or generalized checker.", + "confidence": "high" + }, + { + "path": "packages/db-sqlite-persistence-core/tests/contracts/runtime-persistence-contract.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db-sqlite-persistence-core/tests/contracts/runtime-persistence-contract-oracle.ts", + "evidenceLines": [ + 44, + 49, + 57, + 81, + 95, + 112, + 121, + 128, + 136 + ], + "mechanism": "Three hardcoded shared examples: write one fixed todo and query it, persist one fixed Collection insert and inspect its key/title through a second adapter, and assert coordinator identity. Factory injection supplies host coverage but no independently computed domain-wide result.", + "confidence": "high" + }, + { + "path": "packages/db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract-oracle.ts", + "evidenceLines": [ + 1, + 2, + 3, + 4 + ], + "mechanism": "Four-line re-export only; it neither owns nor executes independent judgment. The model and registered property are in sqlite-core-adapter-oracle.test.ts.", + "ownerPaths": [ + "packages/db-sqlite-persistence-core/tests/sqlite-core-adapter-oracle.test.ts" + ], + "confidence": "high" + }, + { + "path": "packages/db-sqlite-persistence-core/tests/contracts/sqlite-driver-contract.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db-sqlite-persistence-core/tests/contracts/sqlite-driver-contract-oracle.ts", + "evidenceLines": [ + 65, + 70, + 91, + 181, + 200, + 262, + 264, + 268, + 273, + 307, + 311, + 320, + 353 + ], + "mechanism": "Reusable host suite is a set of fixed SQL examples with literal expected rows/orders: [1,2,3] for one held transaction, [3] after one rollback, [1,3] after one nested savepoint rollback. expectAdmissionHistory receives those literal arrays; it does not derive admission from an independent schedule model.", + "confidence": "high" + }, + { + "path": "packages/db-sqlite-persistence-core/tests/persisted-options-type-oracle.test-d.ts", + "role": "example-test", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db-sqlite-persistence-core/tests/persisted-options-type-oracle.test-d.ts", + "evidenceLines": [ + 48, + 58, + 60, + 63, + 79, + 84, + 91, + 103, + 115, + 136 + ], + "mechanism": "Ordinary positive and negative TypeScript examples for one concrete RowInput/RowOutput schema, string keys, and own-key sync overloads. No generalized independent type relation or type-level algebra. Existing pre-PR oracle filename must remain unchanged despite this classification.", + "confidence": "high" + }, + { + "path": "packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts", + "evidenceLines": [ + 668, + 679, + 687, + 697, + 698, + 703, + 8880, + 8881, + 8902, + 9009, + 9195 + ], + "mechanism": "Mixed large file contains a genuine independent append-only durability reference: foldDurabilityLedger interprets begin/write/abort/commit events into committedRows and commitOrder without production queues. Real persisted-wrapper histories are checked against that fold at durable receipt and reopen observations; this specific mechanism, not the many fixed regressions, warrants the name.", + "confidence": "high" + }, + { + "path": "packages/db-sqlite-persistence-core/tests/persisted-readiness-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db-sqlite-persistence-core/tests/persisted-readiness-oracle.test.ts", + "evidenceLines": [ + 22, + 23, + 26, + 29, + 210, + 216, + 259, + 265, + 267, + 279, + 283 + ], + "mechanism": "expectedPersistedSnapshot independently computes opt-in availability, error precedence, and all-source readiness from source outcomes. A finite cross-product drives real Collections/live-query observer and compares persistedStatus, isPersistedReady, and exact error to that computation. Existing pre-PR name remains unchanged.", + "confidence": "high" + }, + { + "path": "packages/db-sqlite-persistence-core/tests/persisted-real-adapter-lifecycle.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db-sqlite-persistence-core/tests/persisted-real-adapter-lifecycle-oracle.test.ts", + "evidenceLines": [ + 165, + 188, + 213, + 218, + 220, + 234, + 256, + 259, + 267, + 270, + 280, + 282, + 285 + ], + "mechanism": "observeRestartOrder runs one fixed two-start cleanup/restart schedule and records counts. The generated variant only selects scheduled versus unscheduled and an incidental schemaVersion; assertions choose the fixed 1/2/4 counts for those same two schedules. No independent lifecycle model or scalable/generalized schedule judgment is present. Borderline because it uses fc.asyncProperty, but restore the original filename conservatively.", + "confidence": "medium" + }, + { + "path": "packages/db-sqlite-persistence-core/tests/runtime-restart-checker.test.ts", + "role": "harness-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db-sqlite-persistence-core/tests/runtime-restart-checker-oracle.test.ts", + "evidenceLines": [ + 9, + 12, + 15, + 28, + 31, + 47, + 48, + 51, + 54, + 56 + ], + "mechanism": "Runs assertRuntimeRestartHistory against an in-memory fake and deliberately changes score/id/extra-row/drop-next-write to ensure the checker throws. It tests checker sensitivity, not production persistence.", + "confidence": "high" + }, + { + "path": "packages/db-sqlite-persistence-core/tests/shared-logical-scheduling-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db-sqlite-persistence-core/tests/shared-logical-scheduling-oracle.test.ts", + "evidenceLines": [ + 302, + 304, + 306, + 307, + 311, + 316, + 318, + 325, + 328, + 332, + 334, + 335 + ], + "mechanism": "Contains a generalized partial admission/cardinality law: generated hydrateQueries and peerQueries control real adapter work; while the hydrate is held, admission count must not advance, and after release metadata query count must equal independently computed hydrateQueries + peerQueries. It judges variable-size production histories without consulting the scheduler; fixed cases elsewhere are not the basis for retention.", + "confidence": "high" + }, + { + "path": "packages/db-sqlite-persistence-core/tests/sqlite-core-adapter-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db-sqlite-persistence-core/tests/sqlite-core-adapter-oracle.test.ts", + "evidenceLines": [ + 251, + 258, + 261, + 265, + 335, + 349, + 378, + 584, + 1824, + 1852, + 1856, + 1882, + 1884, + 1895 + ], + "mechanism": "Contains generated reset/resume lineage oracle: expectedResetResumeMetadata derives exact retained metadata keys and resume kind from the declared destructive/non-destructive transition and arbitrary metadata set. observeResetResumeHistory drives real SQLite adapter transitions, then expectResetResumeLaw compares observed metadata with the independent result. The property generates rows, metadata, versions, and reopen counts.", + "confidence": "high" + }, + { + "path": "packages/db-sqlite-persistence-core/tests/sqlite-driver-admission-laws.test.ts", + "role": "harness-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db-sqlite-persistence-core/tests/sqlite-driver-admission-laws-oracle.test.ts", + "evidenceLines": [ + 4, + 5, + 11, + 12, + 18, + 27, + 29, + 30, + 34 + ], + "mechanism": "Feeds fixed good and deliberately interleaved/missing/extra arrays to expectAdmissionHistory and asserts acceptance or throw. No production driver is used; this is checker calibration.", + "confidence": "high" + }, + { + "path": "packages/db-sqlite-persistence-core/tests/sqlite-resume-snapshot-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db-sqlite-persistence-core/tests/sqlite-resume-snapshot-oracle.test.ts", + "evidenceLines": [ + 257, + 272, + 286, + 351, + 365, + 379, + 383, + 386, + 391, + 398, + 437, + 442, + 445, + 450 + ], + "mechanism": "Mixed file contains startup reference recomputation across generated baseline sizes and managed/no-write/raw-loss transitions. Expected public rows are independently built from original baseline plus managed insert, or empty for raw loss, then compared with real Collection and durable snapshot observations. It runs the same computation in fixed and random campaigns.", + "confidence": "high" + }, + { + "path": "packages/db/tests/SortedMap-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/SortedMap-oracle.test.ts", + "evidenceLines": [ + 53, + 59, + 64, + 78, + 90, + 109, + 111, + 125, + 133 + ], + "mechanism": "Generated set/delete/clear histories update both production SortedMap and a native Map. expectMap derives expected ordering by a fresh full sort and compares every public iterator, size, membership, and value observation, including deferred-order checkpoints and three ordering modes.", + "confidence": "high" + }, + { + "path": "packages/db/tests/adapter-schema-transform-conformance.test-d.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/adapter-schema-transform-conformance-oracle.test-d.ts", + "evidenceLines": [ + 9, + 23, + 42, + 74, + 78, + 96, + 104, + 114, + 130 + ], + "mechanism": "Two concrete schema-transform fixtures assert RowInput/RowOutput and individual callback/parameter types with expectTypeOf and @ts-expect-error. The ItemOf extractor is ordinary type plumbing; there is no generalized independent type algebra that computes expected adapter types. Restore the PR-added oracle name.", + "confidence": "high" + }, + { + "path": "packages/db/tests/btree-map-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/btree-map-oracle.test.ts", + "evidenceLines": [ + 118, + 143, + 171, + 177, + 208, + 216, + 229, + 234, + 257, + 273 + ], + "mechanism": "A native Map step model independently computes operation returns and preserves both payload references and original values. Generated BTree histories are compared after each action for point reads, size/extrema, sorted full/range scans, bounds, and neighboring pairs using filtering over independently sorted model keys.", + "confidence": "high" + }, + { + "path": "packages/db/tests/change-event-history-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/change-event-history-oracle.test.ts", + "evidenceLines": [ + 139, + 149, + 187, + 443, + 453, + 461 + ], + "mechanism": "An independent keyed Map reducer applyModelOp/expectedRowsAfter computes each legal operation prefix; expectedEventTrace derives causal insert/update/delete payloads. runHistory compares actual Collection rows, callback replicas, and index observations against those computations.", + "confidence": "high" + }, + { + "path": "packages/db/tests/cleanup-queue-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/cleanup-queue-oracle.property.test.ts", + "evidenceLines": [ + 61, + 77, + 88, + 109, + 128, + 143, + 153, + 247 + ], + "mechanism": "stepModel independently reduces schedule/cancel/elapsed-time actions into appointments, due deliveries and errors. runHistory drives the production CleanupQueue and compares delivery/error traces after each step; generated action histories execute that rule.", + "confidence": "high" + }, + { + "path": "packages/db/tests/collection-cleanup-restart-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/collection-cleanup-restart-oracle.test.ts", + "evidenceLines": [ + 88, + 92, + 100, + 108, + 334, + 347, + 356 + ], + "mechanism": "expectedCleanupBoundary is an independent three-cut function of prior Collection status and cleanup boundary. Real cleanup/restart tests compare restart admission, public status, dependent termination, and settlement observations with its calculated boundary state; the file also contains ordinary regressions.", + "confidence": "high" + }, + { + "path": "packages/db/tests/collection-gc-clock.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/collection-gc-clock-oracle.test.ts", + "evidenceLines": [ + 11, + 24, + 31, + 35, + 38 + ], + "mechanism": "One fixed fake-timer history sets gcTime to 1, steps the wall clock backward, advances elapsed time by 10, and asserts subscriber count 1 then 0. It contains no appointment reference computation or reusable independent judgment.", + "confidence": "high" + }, + { + "path": "packages/db/tests/collection-idle-cleanup.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/collection-idle-cleanup-oracle.test.ts", + "evidenceLines": [ + 26, + 47, + 57, + 80, + 84, + 88, + 90 + ], + "mechanism": "One fixed idle-scheduler regression executes a no-time callback, returns/removes a subscriber, executes the rescheduled callback, and checks literal ready status. The pending callback Map is a controllable scheduler fixture, not an expected lifecycle model.", + "confidence": "high" + }, + { + "path": "packages/db/tests/collection-metadata-publication-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/collection-metadata-publication-oracle.property.test.ts", + "evidenceLines": [ + 285, + 338, + 340, + 374, + 389, + 398, + 407, + 412 + ], + "mechanism": "applyRound maintains independent row and metadata Maps, applying committed metadata writes to a separately cloned expected world. It derives expected change messages and callback-time metadata/row cuts and compares those with the real Collection across generated rounds.", + "confidence": "high" + }, + { + "path": "packages/db/tests/collection-mutation-startup-oracle.test.ts", + "role": "example-test", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/collection-mutation-startup-oracle.test.ts", + "evidenceLines": [ + 63, + 72, + 90, + 145, + 231, + 375, + 406, + 440, + 475 + ], + "mechanism": "The rejectionCases table supplies concrete mutations and per-example error assertions; subsequent startup cases assert fixed starts, handler calls, rows, and errors. The stated five admission cells are descriptive categories, not a reusable executable admission model. Existing pre-PR oracle filename is outside rename correction.", + "confidence": "high" + }, + { + "path": "packages/db/tests/collection-state-retention-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/collection-state-retention-oracle.property.test.ts", + "evidenceLines": [ + 135, + 153, + 164, + 168, + 184, + 204, + 215, + 2718 + ], + "mechanism": "applyAction updates an independent retained-row Map from the input history, and expectRetainedState compares exposed authoritative base, public rows, and stale row-origin keys after production sync/lifecycle actions. Other sections execute the shared optimistic-history model.", + "confidence": "high" + }, + { + "path": "packages/db/tests/collection-subscription-lifecycle-grammar-oracle.ts", + "role": "oracle-definition", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/collection-subscription-lifecycle-grammar-oracle.ts", + "evidenceLines": [ + 183, + 352, + 375, + 412, + 832 + ], + "mechanism": "Owns the independent LifecycleModel, reduceLifecycle command reducer, expected observations/trace, and deferral grammar. The history and publication owners import this model and compare real Collection observations with its transitions.", + "ownerPaths": [ + "packages/db/tests/collection-subscription-lifecycle-history-oracle.property.test.ts", + "packages/db/tests/collection-subscription-lifecycle-publication-oracle.property.test.ts" + ], + "confidence": "high" + }, + { + "path": "packages/db/tests/collection-subscription-lifecycle-history-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/collection-subscription-lifecycle-history-oracle.property.test.ts", + "evidenceLines": [ + 163, + 349, + 356, + 401, + 409, + 446, + 467 + ], + "mechanism": "runHistory executes real subscription/lifecycle commands and separately reduces the shared LifecycleModel. assertState compares loads, unloads, caller outcomes, errors, Collection/subscription statuses, signals, and full event trace with model-derived values at every command.", + "confidence": "high" + }, + { + "path": "packages/db/tests/collection-subscription-lifecycle-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/collection-subscription-lifecycle-oracle.test.ts", + "evidenceLines": [ + 484, + 650, + 685, + 769, + 774, + 778, + 784, + 801, + 809 + ], + "mechanism": "The mixed file contains runAsyncRestartScenario: its independent settlement ledger derives current-generation completeness, success, retained versus replacement row version, failed attempts, publication trace, and status transitions from scenario outcomes. It compares these computed values after each controlled production settlement; this qualifies independently of its many fixed boundary examples.", + "confidence": "high" + }, + { + "path": "packages/db/tests/collection-subscription-lifecycle-publication-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/collection-subscription-lifecycle-publication-oracle.property.test.ts", + "evidenceLines": [ + 261, + 291, + 301, + 335, + 932, + 943, + 954 + ], + "mechanism": "Owns an independent publication model with source, visible, and private replacement Maps. publicationDiff/publishIfChanged/finishReplacement/projectPublication compute expected rows and message batches from lifecycle and source commands; the production driver compares public observations after each command.", + "confidence": "high" + }, + { + "path": "packages/db/tests/collection-subscription-reentrancy-oracle.test.ts", + "role": "example-test", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/collection-subscription-reentrancy-oracle.test.ts", + "evidenceLines": [ + 28, + 97, + 118, + 120, + 123, + 125, + 141, + 273 + ], + "mechanism": "The callback-phase × release/unsubscribe matrix runs one fixed snapshot pattern and checks literal false/no publication/one acquisition outcomes, with small phase-dependent hook/read counts. Remaining cases are pinned regressions. No independent ownership/history reducer or generalized expected-result computation is executed here. Existing pre-PR oracle filename remains unchanged.", + "confidence": "medium" + }, + { + "path": "packages/db/tests/collection-subscription-replay-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/collection-subscription-replay-oracle.property.test.ts", + "evidenceLines": [ + 310, + 383, + 745, + 757, + 762, + 768, + 790, + 798, + 803, + 814, + 815 + ], + "mechanism": "runReplayScenario tracks independent source and published-row Maps plus pending replay state. It derives retained versus replacement publications from controlled settlement/release/source histories and compares source state, visible rows, message diffs, status, and publication counts against real subscription behavior.", + "confidence": "high" + }, + { + "path": "packages/db/tests/collection-sync-reentrancy-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/collection-sync-reentrancy-oracle.test.ts", + "evidenceLines": [ + 121, + 137, + 146, + 149, + 189, + 198, + 206, + 1508, + 1514 + ], + "mechanism": "runListenerScenario builds an independent ordered list of committed keys from generated commit/abort actions. It compares actual application order and batches against that list, checks aborted receipt counts/outcomes and no nested delivery, and executes exhaustive and generated histories.", + "confidence": "high" + }, + { + "path": "packages/db/tests/collection-truncate-ownership-oracle.property.test.ts", + "role": "example-test", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/collection-truncate-ownership-oracle.property.test.ts", + "evidenceLines": [ + 46, + 82, + 86, + 121, + 128, + 140, + 154, + 156, + 247 + ], + "mechanism": "The generator varies a key, two payload values, and rollback/rejection in one fixed two-owner/truncate schedule. expectObservation merely substitutes the accepted input row into a literal ready/key-set expectation; it does not reduce owner histories or compare a broader independent relation. Conservative classification as a parameterized regression; existing pre-PR name remains unchanged.", + "confidence": "medium" + }, + { + "path": "packages/db/tests/comparison-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/comparison-oracle.property.test.ts", + "evidenceLines": [ + 237, + 295, + 313, + 473, + 475, + 482, + 491, + 501 + ], + "mechanism": "Generalized comparator laws independently constrain production ascComparator over generated values: reflexivity, antisymmetry, transitivity, and deterministic valid numeric signs. Additional value-specific comparisons use direct primitive/byte relations. Replay-calibration sections do not replace these executed product laws.", + "confidence": "high" + }, + { + "path": "packages/db/tests/conformance-disabled-laws.test.ts", + "role": "harness-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/conformance-disabled-laws-oracle.test.ts", + "evidenceLines": [ + 9, + 22, + 29, + 68, + 72, + 74, + 79 + ], + "mechanism": "Builds synthetic disabled-result objects, mutates their fields, and verifies expectDisabledResult accepts/rejects them. It calibrates a shared assertion helper without observing production hook behavior.", + "confidence": "high" + }, + { + "path": "packages/db/tests/conformance-infinite-calibration.test.ts", + "role": "harness-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/conformance-infinite-calibration-oracle.test.ts", + "evidenceLines": [ + 6, + 8, + 9, + 41, + 84, + 91, + 100 + ], + "mechanism": "Intercepts Vitest registration, injects deliberately faulty/coalesced fake drivers into the shared suite, and checks whether its concurrent-fetch scenario passes/rejects and cleans up. The object under test is the conformance runner.", + "confidence": "high" + }, + { + "path": "packages/db/tests/conformance-infinite-demand.test.ts", + "role": "harness-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/conformance-infinite-demand-oracle.test.ts", + "evidenceLines": [ + 27, + 50, + 52, + 54, + 59, + 69, + 108, + 124, + 174, + 179 + ], + "mechanism": "Checks the test-only makeInfiniteOnDemandSource provider against literal supported/invalid requests and verifies request capture. Its additional production-query integration uses fixed expected row windows; no independent generalized product oracle is defined here.", + "confidence": "high" + }, + { + "path": "packages/db/tests/conformance-page-laws.test.ts", + "role": "harness-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/conformance-page-laws-oracle.test.ts", + "evidenceLines": [ + 7, + 15, + 20, + 23, + 27, + 37, + 42, + 43, + 48 + ], + "mechanism": "Feeds synthetic page partitions and corrupted page arrays into expectPageRows. The randomized property generates checker inputs, rather than observing production pagination.", + "confidence": "high" + }, + { + "path": "packages/db/tests/conformance-registration.test.ts", + "role": "harness-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/conformance-registration-oracle.test.ts", + "evidenceLines": [ + 2, + 4, + 7, + 11, + 18, + 21 + ], + "mechanism": "Tests duplicate scenario registration and failure-waiver rejection in the test registry using literal names. No production product observation.", + "confidence": "high" + }, + { + "path": "packages/db/tests/conformance-result-laws.test.ts", + "role": "harness-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/conformance-result-laws-oracle.test.ts", + "evidenceLines": [ + 15, + 32, + 55, + 69, + 120, + 132, + 145, + 151, + 181 + ], + "mechanism": "Calibrates result-surface/equality helpers with synthetic rows, generated duplicate groups, wrong order, and wrong keyed values. Generated samples remain inputs to the checker under test, not product observations.", + "confidence": "high" + }, + { + "path": "packages/db/tests/conformance-scenario-lifetime.test.ts", + "role": "harness-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/conformance-scenario-lifetime-oracle.test.ts", + "evidenceLines": [ + 5, + 49, + 88, + 139, + 154, + 178, + 217, + 228 + ], + "mechanism": "Exercises the ScenarioLifetime test resource manager with successful/failed cleanups, aggregate errors, and late acquisitions. These are harness lifecycle checks rather than product laws.", + "confidence": "high" + }, + { + "path": "packages/db/tests/conformance-scenario-sources.test.ts", + "role": "harness-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/conformance-scenario-sources-oracle.test.ts", + "evidenceLines": [ + 3, + 4, + 9, + 10, + 31, + 33, + 53, + 58, + 87, + 102 + ], + "mechanism": "Checks ScenarioSources/ScenarioLifetime ownership, source cleanup, duplicate handles, and error aggregation. Although one case allocates a real Collection, the judged contract is test-harness disposal.", + "confidence": "high" + }, + { + "path": "packages/db/tests/conformance-scope-setup.test.ts", + "role": "harness-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/conformance-scope-setup-oracle.test.ts", + "evidenceLines": [ + 4, + 5, + 45, + 49, + 64 + ], + "mechanism": "Checks the test scope-setup wrapper preserves successful values and combines setup/disposal failures. No production query/reference comparison.", + "confidence": "high" + }, + { + "path": "packages/db/tests/conformance/contract.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/conformance/contract-oracle.ts", + "evidenceLines": [ + 28, + 40, + 65, + 79, + 102, + 135 + ], + "mechanism": "Type/interface declarations for source handles, operation access, result surfaces, and LiveQueryDriver. They describe adapter plumbing and contain no executable independent judgment.", + "confidence": "high" + }, + { + "path": "packages/db/tests/conformance/disabled-laws.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/conformance/disabled-laws-oracle.ts", + "evidenceLines": [ + 5, + 9, + 13, + 17, + 18, + 20 + ], + "mechanism": "Reusable fixed assertion template requires disabled status/flags and either absent values or empty reactive containers. It does not compute a modeled result or a generalized relation beyond those two literal shapes; conservatively restore the helper name.", + "confidence": "medium" + }, + { + "path": "packages/db/tests/conformance/infinite-contract.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/conformance/infinite-contract-oracle.ts", + "evidenceLines": [ + 27, + 32, + 43, + 59, + 73 + ], + "mechanism": "Interfaces describe infinite-query config, result, handles, and driver operations; the file contains no executable window reference computation.", + "ownerPaths": [ + "packages/db/tests/conformance/infinite-suite-oracle.ts" + ], + "confidence": "high" + }, + { + "path": "packages/db/tests/conformance/infinite-on-demand.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/conformance/infinite-on-demand-oracle.ts", + "evidenceLines": [ + 139, + 144, + 182, + 185, + 187, + 193, + 203, + 207, + 210 + ], + "mechanism": "Controlled test provider copies request inputs, filters/sorts a finite backend, and writes resulting rows into production sync. Its evaluator generates provider input responses; it never judges expected versus observed production output. The shared infinite suite owns that judgment.", + "ownerPaths": [ + "packages/db/tests/conformance/infinite-suite-oracle.ts" + ], + "confidence": "high" + }, + { + "path": "packages/db/tests/conformance/infinite-suite-oracle.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/conformance/infinite-suite-oracle.ts", + "evidenceLines": [ + 38, + 45, + 46, + 49, + 54, + 55, + 56, + 57, + 657, + 658, + 674, + 704, + 705, + 712, + 713, + 718, + 722 + ], + "mechanism": "expectedOverlappingWindow independently derives visible rows, page slices/params, continuation, fetching, error, and status from modeled source extent and settlement flags. The overlapping preload/fetch scenario enumerates source sizes and settlement orders, applies source changes, and compares the real exported controller snapshot to this model.", + "confidence": "high" + }, + { + "path": "packages/db/tests/conformance/page-laws-oracle.ts", + "role": "oracle-definition", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/conformance/page-laws-oracle.ts", + "evidenceLines": [ + 5, + 13, + 15, + 18, + 21 + ], + "mechanism": "expectPageRows independently computes complete page count and page slices from expected rows/pageSize, and enforces reconstruction of data by flattening pages. The shared infinite suite applies this generalized page-partition law to real hook observations.", + "ownerPaths": [ + "packages/db/tests/conformance/infinite-suite-oracle.ts" + ], + "confidence": "high" + }, + { + "path": "packages/db/tests/conformance/result-laws.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/conformance/result-laws-oracle.ts", + "evidenceLines": [ + 4, + 19, + 45, + 68, + 79, + 82, + 88 + ], + "mechanism": "Generic selected-row normalization and ordered/unordered/keyed equality helpers compare actual data with caller-supplied expected data. They do not derive domain expected results; the infinite suite and page checker own their independent window/partition computations.", + "ownerPaths": [ + "packages/db/tests/conformance/infinite-suite-oracle.ts" + ], + "confidence": "medium" + }, + { + "path": "packages/db/tests/conformance/suite.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/conformance/suite-oracle.ts", + "evidenceLines": [ + 66, + 247, + 251, + 260, + 267, + 280, + 284, + 289, + 293, + 297, + 298 + ], + "mechanism": "Registers reusable fixed conformance examples over SEED and hand-authored mutations. basic-select expects a literal John Smith row; equality-filter transitions expect literal ID arrays and final rows. Reuse across framework drivers and generic equality helpers does not provide an independent query evaluator or state model.", + "confidence": "high" + }, + { + "path": "packages/db/tests/cursor-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/cursor-oracle.property.test.ts", + "evidenceLines": [ + 70, + 78, + 103, + 115, + 117, + 124, + 146, + 171, + 212, + 256 + ], + "mechanism": "Independent scalar and tuple comparison functions define continuation and ordering. The checker evaluates the expression returned by real buildCursor with a reference evaluator and compares its denotation to the direct boundary rule; public Collection ordered snapshots after preload are compared with independently derived tuple order.", + "confidence": "high" + }, + { + "path": "packages/db/tests/d2-source-reconciliation-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/d2-source-reconciliation-oracle.property.test.ts", + "evidenceLines": [ + 218, + 230, + 246, + 290, + 303, + 346, + 349, + 373, + 378 + ], + "mechanism": "Source operations update an independent source-row Map. Signed production reconciliation outputs are integrated into a separate weighted relation and compared with one unit contribution per expected source row; the production reconciler is the system under test, not the source of expected values.", + "confidence": "high" + }, + { + "path": "packages/db/tests/db-client-hydration-authority-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/db-client-hydration-authority-oracle.test.ts", + "evidenceLines": [ + 77, + 160, + 211, + 217 + ], + "mechanism": "referenceRows independently folds initial rows and declarative adapter/hydration history; runHistory compares synced data, public rows, and a subscription mirror against that recomputation.", + "confidence": "high" + }, + { + "path": "packages/db/tests/db-client.test-d.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/db-client-oracle.test-d.ts", + "evidenceLines": [ + 15, + 47, + 72, + 90, + 111 + ], + "mechanism": "Concrete DbClient dependency, descriptor, hydration payload, preload, and schema type assertions. Expected types are explicitly written for the examples; there is no independent generalized type relation.", + "confidence": "high" + }, + { + "path": "packages/db/tests/db-client.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/db-client-oracle.test.ts", + "evidenceLines": [ + 28, + 168, + 463, + 987, + 1066, + 1173 + ], + "mechanism": "Fixed client isolation, hydration, serialization, and cleanup regressions, including parameterized insert/delete authority cases. No reusable independent state/reference computation judges these observations; the separate hydration-authority oracle owns that model.", + "confidence": "high" + }, + { + "path": "packages/db/tests/effect-disposal-oracle.test.ts", + "role": "example-test", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/effect-disposal-oracle.test.ts", + "evidenceLines": [ + 23, + 34, + 84, + 87, + 105, + 108, + 114, + 124, + 126, + 130 + ], + "mechanism": "A fixed matrix crosses two reentry sites, handler pending/synchronous, and three outcomes for the same three-caller disposal schedule. Assertions check literal one-release/no-early-settlement outcomes and equality of the three returned results. There is a useful common-result relation, but no separate reusable disposal/history checker; conservatively retain example classification. Existing pre-PR name remains unchanged.", + "confidence": "medium" + }, + { + "path": "packages/db/tests/flat-change-tracking-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/flat-change-tracking-oracle.property.test.ts", + "evidenceLines": [ + 165, + 169, + 175, + 601, + 617, + 630, + 660, + 714 + ], + "mechanism": "expectedChanges executes operations on independent plain copies and derives changed/deleted fields. runHistory compares both production tracking paths with that result, including a separate modeled error outcome, across generated histories.", + "confidence": "high" + }, + { + "path": "packages/db/tests/index-suggestion-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/index-suggestion-oracle.test.ts", + "evidenceLines": [ + 34, + 40, + 44, + 71, + 84, + 97, + 103 + ], + "mechanism": "expectedSuggestions computes the policy from advice-enabled, Collection size, indexing mode and indexed field. A finite scenario domain drives real query/index suggestion creation, and the result is compared to the computed expected count. This is a small independent decision rule, not merely per-scenario expected literals. Existing pre-PR name stays untouched.", + "confidence": "high" + }, + { + "path": "packages/db/tests/index-update-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/index-update-oracle.property.test.ts", + "evidenceLines": [ + 102, + 117, + 121, + 133, + 139, + 202, + 207, + 211, + 224, + 235, + 250, + 253 + ], + "mechanism": "A native Map owns the expected key/value rows through generated put, delete, and rebuild histories. Fresh grouping, sorting, and direct range predicates derive expected lookups and complete memberships for both real index types; the same independent model also checks a separately rebuilt index.", + "confidence": "high" + }, + { + "path": "packages/db/tests/live-query-observer-history-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/live-query-observer-history-oracle.property.test.ts", + "evidenceLines": [ + 232, + 251, + 328, + 335, + 413, + 563, + 580 + ], + "mechanism": "Independent listener-eligibility and delivery state machine: subscribeModel, publishModel, and disposeModel evolve expected state; runHistory drives the real observer and compares exact delivery history, source state, and snapshots after commands.", + "confidence": "high" + }, + { + "path": "packages/db/tests/live-query-observer.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/live-query-observer-oracle.test.ts", + "evidenceLines": [ + 23, + 393, + 657, + 734, + 793, + 823, + 849, + 1244 + ], + "mechanism": "Focused observer regressions assert concrete snapshot, publication, listener, and lifecycle outcomes. Setup helpers construct sources; no independent history model is executed in this file. The independent eligibility model is in the separate observer-history file.", + "confidence": "high" + }, + { + "path": "packages/db/tests/local-only-direct-write-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/local-only-direct-write-oracle.test.ts", + "evidenceLines": [ + 163, + 174, + 191, + 198, + 199, + 209, + 263, + 264, + 266 + ], + "mechanism": "Mixed file, but observe runs the same write history through two distinct paths: direct local-only writes and the explicitly confirmed optimistic path. The test compares their computed rows, batches, errors and transaction states. The differential mechanism at 263–266 qualifies independently of surrounding hardcoded guard regressions.", + "confidence": "high" + }, + { + "path": "packages/db/tests/mutation-handler-type-oracle.test-d.ts", + "role": "example-test", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/mutation-handler-type-oracle.test-d.ts", + "evidenceLines": [ + 26, + 44, + 54, + 67, + 80, + 142, + 152, + 155, + 169, + 224 + ], + "mechanism": "Concrete default/explicit/schema-inferred, branded and numeric key fixtures assert callback types and rejected assignments. IsAny is a helper for an ordinary type assertion, not an independent type model/algebra. Classify as examples under the corrected rule, while leaving the existing pre-PR oracle filename untouched.", + "confidence": "high" + }, + { + "path": "packages/db/tests/notification-laws-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/notification-laws-oracle.test.ts", + "evidenceLines": [ + 5, + 17, + 26, + 42, + 75, + 82, + 85, + 90 + ], + "mechanism": "Generated source histories drive a real Collection and live-query Collection. The shared independent expectNotificationHistory checker reconstructs membership and previous values from callback messages and checks callback snapshots against input-derived expected worlds; post-unsubscribe silence is also checked.", + "confidence": "high" + }, + { + "path": "packages/db/tests/observer-cleanup-restart.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/observer-cleanup-restart-oracle.test.ts", + "evidenceLines": [ + 50, + 51 + ], + "mechanism": "A fixed cleanup/restart scenario is repeated across observer mode and resumption variants, then asserts literal empty data and Map results. No independent state machine or differential result computation.", + "confidence": "high" + }, + { + "path": "packages/db/tests/optimistic-composition-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/optimistic-composition-oracle.test.ts", + "evidenceLines": [ + 12, + 54, + 253, + 256, + 261, + 262, + 350, + 359, + 369, + 372 + ], + "mechanism": "Although much of this file is fixed regression coverage, its permutation tests execute an independent snapshot ledger: expected snapshots are assembled from input patches, active owners select the newest surviving snapshot, and a reducer excludes rolled-back owners. Real source rows, event replicas, and downstream rows are compared with the computed snapshot.", + "confidence": "high" + }, + { + "path": "packages/db/tests/optimistic-history-oracle.ts", + "role": "oracle-definition", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/optimistic-history-oracle.ts", + "evidenceLines": [ + 96, + 124, + 172, + 217, + 242, + 350, + 409, + 460, + 722, + 799, + 829 + ], + "mechanism": "Defines the reusable independent HistoryModel over base rows, ordered intents and source batches, plus generic event/outcome comparisons. runOptimisticHistory drives a real Collection and compares reads, event replicas and downstream rows to model state after each action. This module has no test registration; its importing test owners execute it.", + "ownerPaths": [ + "packages/db/tests/collection-state-retention-oracle.property.test.ts", + "packages/db/tests/optimistic-history-outcomes-oracle.test.ts", + "packages/db/tests/optimistic-history-publication-oracle.test.ts", + "packages/db/tests/optimistic-settlement-boundaries-oracle.test.ts" + ], + "confidence": "high" + }, + { + "path": "packages/db/tests/optimistic-history-outcomes-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/optimistic-history-outcomes-oracle.test.ts", + "evidenceLines": [ + 40, + 43, + 57, + 64, + 83, + 93, + 104, + 114 + ], + "mechanism": "The product-test section builds failed-write/peer-survival histories from generated scenarios and executes runOptimisticHistory, which computes expected rows, callbacks and promise outcomes independently. The file also contains harness calibration tests, but is not solely a harness test.", + "ownerPaths": [ + "packages/db/tests/optimistic-history-oracle.ts" + ], + "confidence": "high" + }, + { + "path": "packages/db/tests/optimistic-history-publication-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/optimistic-history-publication-oracle.test.ts", + "evidenceLines": [ + 21, + 24, + 38, + 47, + 53, + 66, + 69, + 82, + 84 + ], + "mechanism": "Pinned source/optimistic histories execute the shared independent HistoryModel/refinement check via runOptimisticHistory; this file supplies action sequences rather than hardcoded expected rows or batches. It also runs deliberate captured-observation corruptions through the same checker. Retained because the production outcome is judged by the reusable model, not by the example names.", + "ownerPaths": [ + "packages/db/tests/optimistic-history-oracle.ts" + ], + "confidence": "high" + }, + { + "path": "packages/db/tests/optimistic-settlement-boundaries-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/optimistic-settlement-boundaries-oracle.test.ts", + "evidenceLines": [ + 26, + 31, + 34, + 40, + 43, + 141, + 144, + 148, + 149 + ], + "mechanism": "Boundary case corpus invokes runOptimisticHistory for every initial-state/action program. Although the corpus is fixed and looks like a regression table, it supplies no expected-result literals: the independent shared model computes and compares every public cut. This qualifies as execution of a reusable oracle under the supplied rule, not only hardcoded regression assertions.", + "ownerPaths": [ + "packages/db/tests/optimistic-history-oracle.ts" + ], + "confidence": "medium" + }, + { + "path": "packages/db/tests/optimistic-transaction-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/optimistic-transaction-oracle.property.test.ts", + "evidenceLines": [ + 102, + 114, + 184, + 196, + 212, + 280, + 802, + 803, + 808, + 916, + 925, + 940, + 1162 + ], + "mechanism": "runSameKey independently reduces operation sequences into net mutation payloads and outcomes, while runTransaction computes proposed public rows in plain Maps and checks source/read/replica/downstream observations. Generated payloads and operation-order matrices execute those computations. Ordinary replacement examples elsewhere do not determine the classification.", + "confidence": "high" + }, + { + "path": "packages/db/tests/oracle-replay.test.ts", + "role": "harness-test", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/oracle-replay.test.ts", + "evidenceLines": [ + 38, + 80, + 185, + 254, + 289, + 311, + 345, + 363, + 427 + ], + "mechanism": "Exercises guarded replay subprocess selection, execution counts, property/reporting failures, cleanup diagnostics, and manifest mapping. These assertions judge oracle execution/reporting infrastructure rather than a production data or lifecycle law. Its existing filename is outside rename correction.", + "confidence": "high" + }, + { + "path": "packages/db/tests/paced-mutations-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/paced-mutations-oracle.test.ts", + "evidenceLines": [ + 77, + 98, + 117, + 141, + 174, + 291, + 420, + 436, + 451, + 1063, + 1064 + ], + "mechanism": "queueStarts/debounceStarts/throttleStarts/nonLeadingThrottleStarts simulate starts from action histories with an independent virtual clock and ordered call IDs. Scenario expectations call these models; runProduction observations of real paced mutations are compared to the computed starts and transaction grouping.", + "confidence": "high" + }, + { + "path": "packages/db/tests/proxy-detachment-contract-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/proxy-detachment-contract-oracle.test.ts", + "evidenceLines": [ + 370, + 379, + 393, + 413, + 414, + 416, + 419, + 423, + 425, + 427 + ], + "mechanism": "Mixed contract/example file contains actual native differential judgment: reorder executes on an independent native Map/Set row and on the draft/Collection production paths, then compares detached and stored iteration contents to the native result across container and operation alternatives. This mechanism, not the surrounding contract prose or fixed expected assertions, warrants the name.", + "confidence": "high" + }, + { + "path": "packages/db/tests/proxy-iteration-contract-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/proxy-iteration-contract-oracle.test.ts", + "evidenceLines": [ + 117, + 135, + 151, + 156, + 158, + 161, + 449, + 452, + 477, + 478, + 481 + ], + "mechanism": "Native differential iteration rule executes the same iterator/edit program on a native array and a production draft, computing expected visits/final contents. A separate Map/Set run computes native membership-change observations and compares the production draft and extracted changes across iteration protocols.", + "confidence": "high" + }, + { + "path": "packages/db/tests/proxy-native-methods-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/proxy-native-methods-oracle.property.test.ts", + "evidenceLines": [ + 209, + 214, + 215, + 217, + 221, + 225, + 257, + 258, + 263, + 365, + 408 + ], + "mechanism": "expectArrayCall and expectTypedCall execute the same built-in method on separate native and draft values; native outcomes, identity and post-write values supply the independent expected result. Arbitraries vary rows, method and arguments and feed that differential checker.", + "confidence": "high" + }, + { + "path": "packages/db/tests/proxy-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/proxy-oracle.test.ts", + "evidenceLines": [ + 48, + 99, + 108, + 111, + 112, + 114, + 123, + 136, + 138, + 243, + 265, + 270, + 272 + ], + "mechanism": "Mostly unit examples, but it actually contains a reusable native array callback oracle: observeArrayCallbacks executes each history step on independent native/proxy rows, derives the expected change set from the native row, and assertArrayCallbacks compares every recorded cut. Generated two-step callbacks invoke this mechanism, so the mixed file qualifies.", + "confidence": "high" + }, + { + "path": "packages/db/tests/proxy-revert-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/proxy-revert-oracle.property.test.ts", + "evidenceLines": [ + 137, + 519, + 569, + 696, + 708, + 709, + 712, + 718, + 725, + 730, + 764, + 774 + ], + "mechanism": "Independent plain-data Spec state reducer step plus canonical equality computes expected changes after generated histories. expectHistory drives createChangeProxy and checks changed keys/values, final draft and untouched baseline against that model.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/aggregate-value-contracts.test-d.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/query/aggregate-value-contracts-oracle.test-d.ts", + "evidenceLines": [ + 54, + 91, + 98, + 144, + 179, + 198, + 242 + ], + "mechanism": "AggregateRow fixtures and concrete expectTypeOf assertions specify numeric/orderable result types and rejected inputs. Generic forwarders exercise production overloads before concrete assertions; they do not implement a generalized independent aggregate type algebra.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/builder/ref-proxy.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/query/builder/ref-proxy-oracle.test.ts", + "evidenceLines": [ + 12, + 21, + 64, + 111, + 157, + 199, + 261 + ], + "mechanism": "Unit examples for proxy paths, descriptors, identity, type guards, and literal wrapping. Expected paths/descriptors/values are literal examples, with no independent reusable semantic model.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/cold-join-reconciliation-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/cold-join-reconciliation-oracle.test.ts", + "evidenceLines": [ + 69, + 225, + 236, + 242, + 243 + ], + "mechanism": "Maintains independent source rows and recomputes the cold join with plain nested filtering/mapping. The checker compares both live output and subscription replica to those rows, and requires a real cold acquisition.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/compiler/evaluators-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/query/compiler/evaluators-oracle.test.ts", + "evidenceLines": [ + 87, + 127, + 154, + 164, + 564, + 572 + ], + "mechanism": "Mixed unit file contains an independent dynamic-programming LIKE/ILIKE interpreter (referenceLike), generated string/pattern cases, and findLikeMismatch comparisons against compiled production expressions.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/compiler/lazy-targets.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/query/compiler/lazy-targets-oracle.test.ts", + "evidenceLines": [ + 38, + 41, + 51, + 55, + 75 + ], + "mechanism": "Three lexical-source targeting examples: matching fallback alias, mismatched fallback alias, and dotted versus nested paths. Each checks a hardcoded target/path result; no independent resolver or generated law checker.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/derived-delete-reconciliation-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/query/derived-delete-reconciliation-oracle.test.ts", + "evidenceLines": [ + 23, + 30, + 66, + 74, + 89, + 117, + 124 + ], + "mechanism": "runFlushHistory tracks an independent expected row Map through generated insert/update histories, drives real derived reconciliation beneath deletes, and compares published rows and a lookup-work bound after removing the overlay.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/identity-output-shape-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/identity-output-shape-oracle.test.ts", + "evidenceLines": [ + 74, + 255, + 261, + 271, + 278, + 368 + ], + "mechanism": "Computes expected complete output bags using plain row mapping/nested-loop joins for each query shape and alias assignment; compares both executions to independent output and checks explicit-shape equivalence. Also owns an explicit equality-partition checker.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/includes-collection-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/includes-collection-oracle.property.test.ts", + "evidenceLines": [ + 304, + 310, + 566, + 577, + 585, + 599, + 826 + ], + "mechanism": "recompute() independently filters and orders source-Map children per parent. collectionProjection compares real live-query rows, callback publications and diff-derived parent events against that recomputation across generated CollectionAction histories; this is more than the neighboring fixed facade examples.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/includes-context-transport-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/includes-context-transport-oracle.test.ts", + "evidenceLines": [ + 1253, + 1257, + 1262, + 1281, + 1307, + 1309, + 1318 + ], + "mechanism": "Recursive-source cells keep modelRows and compute expected filter/projection/aggregate/having/ordered-window results in JavaScript, then compare all public materialization forms against expected(parent.id) after real source updates. The finite grammar executes an independent result rule, alongside fixed path examples.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/includes-cross-formulation-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/includes-cross-formulation-oracle.property.test.ts", + "evidenceLines": [ + 408, + 422, + 439, + 706, + 733, + 759, + 771 + ], + "mechanism": "Independent source-Map recomputeNestedModel/recomputeWindowedModel supply expected rows. expectFormulationsEquivalent compares real nested query, regrouped flat join, fresh per-parent query and TLP partitions at each generated action checkpoint.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/includes-functional-input-boundary.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/query/includes-functional-input-boundary-oracle.test.ts", + "evidenceLines": [ + 11, + 18, + 55, + 95, + 105, + 112, + 115, + 142, + 262, + 268, + 394 + ], + "mechanism": "The form/failureAt matrix applies one fixed parent/children/peer scenario and checks literal ID lists [10,100], [20,200], [20,200,201]. Other tests assert literal count/sum outcomes, exact rejection text and fixed handle identity. The production fn.select computations are the SUT, not independent reference computations. No reusable reference model or differential/metamorphic comparison computes outcomes across the matrix. Restore original name despite the earlier classification.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/includes-functional-projection-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/includes-functional-projection-oracle.test.ts", + "evidenceLines": [ + 1253, + 1264, + 1374, + 1379, + 1382, + 1397, + 1420, + 1457, + 1463 + ], + "mechanism": "Mixed file with an actual reference-model lane: renamedCells retains a separate truth Map and recomputes expected primary/sibling child arrays from current group and source rows. check() compares public results and independently summed totals across materialization/projection/consumer combinations and child/sibling/route updates.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/includes-optimistic-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/includes-optimistic-oracle.property.test.ts", + "evidenceLines": [ + 350, + 357, + 368, + 371, + 572, + 578, + 625, + 656 + ], + "mechanism": "visibleLevels() independently overlays pending whole-row snapshots on cloned synced Maps; recompute() recursively filters/sorts resulting relationship trees. The projection compares real Collection results with this model through optimistic, sync, confirmation and rollback histories with generated route roles.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/includes-oracle-helpers.ts", + "role": "driver-or-fixture", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/includes-oracle-helpers.ts", + "evidenceLines": [ + 41, + 46, + 53, + 54, + 55, + 62, + 65, + 71 + ], + "mechanism": "createControlledCollection creates the real source Collection, clones supplied rows, forwards begin/write/commit and exposes mutation-gate controls. It contains no expected-result computation or judgment. Existing pre-PR oracle spelling is outside this correction.", + "ownerPaths": [ + "packages/db/tests/query/includes-oracle.property.test.ts", + "packages/db/tests/query/includes-collection-oracle.property.test.ts", + "packages/db/tests/query/includes-context-transport-oracle.test.ts", + "packages/db/tests/query/includes-functional-projection-oracle.test.ts", + "packages/db/tests/query/includes-publication-oracle.test.ts" + ], + "confidence": "high" + }, + { + "path": "packages/db/tests/query/includes-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/includes-oracle.property.test.ts", + "evidenceLines": [ + 471, + 480, + 490, + 496, + 3444, + 3449, + 3450, + 3456, + 5973 + ], + "mechanism": "recompute() rebuilds nested trees independently from root/level Maps using correlation equality and total row order. structuralProjection compares real incremental results at each runTrace checkpoint for generated histories. Additional scalar and alpha-renaming mechanisms also execute independent recomputations.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/includes-publication-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/includes-publication-oracle.test.ts", + "evidenceLines": [ + 269, + 274, + 278, + 285, + 297, + 306, + 321, + 769, + 857 + ], + "mechanism": "recomputeRows() independently joins source Maps into two child arrays. publicationProjection compares both real live-query layers, callback-time rows and complete change payloads with model rows and prior delivered membership across generated source/optimistic actions. It judges transient cuts as well as final reads.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/includes-query-shape-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/includes-query-shape-oracle.test.ts", + "evidenceLines": [ + 170, + 176, + 177, + 179, + 307, + 314, + 318, + 481, + 523 + ], + "mechanism": "multiplicityProjection computes public parent membership from independent source Maps via positive child support, while correlationProjection recomputes joined-alias matching from orders/parts/productions. Real live-query observations are checked after source writes and generated input values.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/includes-scope-identity-oracle.ts", + "role": "oracle-definition", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/includes-scope-identity-oracle.ts", + "evidenceLines": [ + 165, + 202, + 337, + 339, + 357, + 378, + 402, + 429, + 816 + ], + "mechanism": "Owns scopedScenarioArbitrary/history grammar, classifyScopedNaming legality and recomputeScopedRows independent source-role relational model (including joins, multiplicity, predicates, DISTINCT and limits). The includes-oracle.property.test.ts alpha-renaming owner imports and executes this judgment against canonical/generated query spellings.", + "ownerPaths": [ + "packages/db/tests/query/includes-oracle.property.test.ts" + ], + "confidence": "high" + }, + { + "path": "packages/db/tests/query/includes-space-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/includes-space-oracle.test.ts", + "evidenceLines": [ + 38, + 134, + 136, + 140, + 146, + 149, + 155, + 168, + 180, + 182, + 193 + ], + "mechanism": "The rootCount lane observes distinct constructed/retained production facade entries and compares all three counts to the independent topology formula rootCount + 2*rootCount + 10*rootCount supplied by the fixture, for 0/1/20 roots. The leaf-count lane checks topology-count invariance while actual leaf volume changes. This is a bounded cardinality law, although this mixed file also has fixed examples and fixture-cleanup tests. Formula evidence: includes-space-oracle-fixture.ts:123.", + "confidence": "medium" + }, + { + "path": "packages/db/tests/query/includes-temporal-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/includes-temporal-oracle.test.ts", + "evidenceLines": [ + 1365, + 1370, + 1371, + 1375, + 2579, + 2722, + 2801, + 2810, + 2813, + 2814, + 2818, + 2836 + ], + "mechanism": "Contains many pinned examples, but scheduled acquisition observations are judged by an independent rule selecting current rows only when the current scheduled acquisition fulfilled. More directly, the generated partialBodies lane independently accumulates an expected child prefix and compares every production publication cut and readiness state while the acquisition is held.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/includes-work-counter-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/includes-work-counter-oracle.test.ts", + "evidenceLines": [ + 422, + 427, + 435, + 436, + 439, + 442, + 457, + 460, + 475, + 577, + 584, + 591 + ], + "mechanism": "Metamorphic work law: observe a minimal production baseline and a separately scaled fixture, then require identical public results and source delivery/read counts as unrelated link/target/all-source cardinality varies. The generated 1..24 filler domain uses this same cross-execution relation; literal baseline calibration alone is not the mechanism.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/index-path-collision-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/index-path-collision-oracle.test.ts", + "evidenceLines": [ + 123, + 128, + 133, + 138, + 147, + 157, + 160, + 215 + ], + "mechanism": "Each predicate has a separately expressed JavaScript accepts rule. Expected keys are recomputed by array filtering and compared against both scan and reached-index production paths; ordering also uses direct JavaScript sorting.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/ir-stable-identity-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/query/ir-stable-identity-oracle.test.ts", + "evidenceLines": [ + 186, + 202, + 213, + 235, + 1534, + 1538 + ], + "mechanism": "Mixed regression file owns interpretOutputProgram, an independent denotation for generated concat/coalesce/caseWhen contexts. assertOutputProgram compares compiled output to that denotation and enforces equal-identity output preservation, with fixed and random campaigns.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/join-result-key-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/join-result-key-oracle.property.test.ts", + "evidenceLines": [ + 96, + 206, + 230, + 234 + ], + "mechanism": "expectedPairs independently recomputes join membership by nested loops over model rows after history mutations; runHistory compares production publications against those expected pair descriptions.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/load-subset-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/load-subset-oracle.property.test.ts", + "evidenceLines": [ + 114, + 199, + 203, + 213, + 219, + 224, + 232, + 242, + 249, + 268, + 274, + 1521, + 1541 + ], + "mechanism": "assertCompletedExactDemandTrace keeps an independent Set of serialized demand descriptors and predicts whether each call starts work; assertConcurrentExactDemandTrace keeps a separate descriptor-to-promise ledger and checks sharing, distinct starts, settlement, and reset against real DeduplicatedLoadSubset calls. Both checkers run over generated traces as well as fixed distinguishing pairs.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/load-subset-replay-refinement-oracle.test.ts", + "role": "example-test", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/load-subset-replay-refinement-oracle.test.ts", + "evidenceLines": [ + 145, + 146, + 221, + 223, + 335, + 361, + 402, + 565, + 584, + 676, + 709, + 723, + 774, + 792, + 820, + 850 + ], + "mechanism": "The shared createHarness supplies production calls, deferred results, and observations. Expected rows and batches are specified as literal old/new snapshots at individual checkpoints; the restart matrix only selects between its fixed initial and replacement rows. No reusable replay-participant state model or independent generalized result computation is executed. The opening model prose does not change that role. Its oracle filename predates this PR and should remain unchanged.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/load-subset-source-readiness-refinement-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/load-subset-source-readiness-refinement-oracle.test.ts", + "evidenceLines": [ + 36, + 43, + 78, + 230, + 240, + 262, + 278, + 284, + 290, + 294, + 312 + ], + "mechanism": "expectedRows independently filters applied child rows by the current parent group. The four old-outcome/settlement-order histories drive real Collections and correlated queries, compare the model at progressive-write and parent-change cuts, and recompute rows and readiness after each old/fresh settlement. The general group-filter computation supplies judgment independently of the controlled adapter.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/load-subset-transaction-refinement-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/load-subset-transaction-refinement-oracle.test.ts", + "evidenceLines": [ + 42, + 46, + 47, + 61, + 70, + 94, + 96, + 121, + 148, + 151, + 159, + 160, + 161 + ], + "mechanism": "expectedOutcome implements the independent publication-started rule: an abort before publication rejects and yields no visible row/events, whereas an abort after publication starts fulfills and preserves the row. The bounded three-phase driver executes real Collection commit and optimistic-transaction blocking, then compares load outcome, visibility, event batches, and callback reads with that computation.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/optimizer-semantics-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/optimizer-semantics-oracle.test.ts", + "evidenceLines": [ + 192, + 203, + 384, + 935, + 1017 + ], + "mechanism": "Contains independent array-based sums and joins plus differential materialized-query formulations. Actual optimized query rows are compared with separately computed expected results, beyond the file's fixed regressions.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/ordered-default-work.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/query/ordered-default-work-oracle.test.ts", + "evidenceLines": [ + 13, + 17, + 25, + 60, + 73, + 181, + 197, + 206, + 218, + 230, + 252, + 254, + 267, + 294, + 305 + ], + "mechanism": "The finite-source filtering/sorting is provider fixture behavior, not an independent expected public-result calculation. Product assertions use fixed rows and fixed request counts for named scenarios. expectRepairSchedule is a reusable assertion helper, but it supplies a hardcoded five-cut expected retry table and a fixed cap rather than computing a general schedule law. Its self-calibration checks only two substituted second delays. Conservatively restore the original filename; this schedule helper is the closest borderline mechanism.", + "confidence": "medium" + }, + { + "path": "packages/db/tests/query/ordered-demand-retirement.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/query/ordered-demand-retirement-oracle.test.ts", + "evidenceLines": [ + 12, + 15, + 31, + 36, + 49, + 88, + 90, + 96, + 106, + 113, + 114, + 121, + 124 + ], + "mechanism": "A single fixed four-row history is repeated with or without one peer. The reference-expression evaluator supplies the fake provider responses; expected public rows remain the literal [2,3]. The owner-scaled work counts describe this pinned mutation/truncate/cleanup sequence and do not constitute a reusable independent model or generalized checker. Restore the original filename.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/ordered-lifecycle-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/ordered-lifecycle-oracle.property.test.ts", + "evidenceLines": [ + 183, + 198, + 218, + 590, + 595, + 598, + 604, + 766, + 776, + 1138, + 1164, + 1189, + 1192, + 1202 + ], + "mechanism": "observeHistory computes referenceWindow by independently sorting the authoritative finite rows and slicing by the requested width. It derives legal publication traces and checks observed rows, waiters, ownership, and restart cuts. assertHistory runs this judgment across the finite acquisition/delivery/window/outcome/sync-run/barrier domain and generated numeric variants.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/ordered-source-loader-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/query/ordered-source-loader-oracle.test.ts", + "evidenceLines": [ + 505, + 519, + 537, + 572, + 594, + 611 + ], + "mechanism": "Mixed unit file contains a bounded independent work-plan rule: expectedStagedWindowTrace derives the request sequence from boundary presence and remaining demand. The full two-by-two semantic domain is driven through OrderedSourceLoader and observed requests are compared to the computed plan. Other route regressions alone would not qualify.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/ordered-source-loader-state-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/query/ordered-source-loader-state-oracle.test.ts", + "evidenceLines": [ + 635, + 636, + 638, + 639, + 658, + 684, + 694, + 704, + 713, + 717, + 723, + 726, + 730, + 734, + 738 + ], + "mechanism": "This mixed regression file contains a bounded independent release-sequence law at lines 635-744. Across older request kind, both settlement orders, and both outcomes for each request, it computes successfulFinite from the full-source-success and finite-success rule, computes failedIndices from outcome plus completion order, and compares their composed acquisition identities with the real OrderedSourceLoader release trace. The expectation uses test-controlled outcomes and observed handle identity, not production transition/classification helpers or a hardcoded expected release array. That narrow finite-domain computation qualifies; the numerous other pinned regressions do not independently justify the name.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/ordered-work-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/ordered-work-oracle.property.test.ts", + "evidenceLines": [ + 149, + 174, + 251, + 257, + 260, + 305, + 309, + 674, + 679, + 682, + 692, + 4293, + 4295, + 4303 + ], + "mechanism": "observeConsumer derives matchingTruth by independently sorting finite authoritative rows and filtering marker membership. expectRawWindow judges exact membership and permitted order, while assertConsumerParity compares equivalent Collection and Effect executions for rows, errors, liveness, and semantic request traces. The same differential checker runs over generated scenarios.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/pagination-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/pagination-oracle.property.test.ts", + "evidenceLines": [ + 626, + 631, + 646, + 648, + 678, + 682, + 686, + 788, + 797, + 829, + 832, + 835, + 841, + 5506, + 5515, + 5531 + ], + "mechanism": "referenceWindowRows recomputes the result by an independent rank/NaN/key ordering and offset-limit slice. expectedPageChanges independently derives insert/update/delete changes from before/after maps. Real live-query preload and setWindow observations are compared with these computations across generated window and state histories, including the structural matrix.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/pooled-live-query-gc-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/query/pooled-live-query-gc-oracle.test.ts", + "evidenceLines": [ + 46, + 58, + 66, + 82, + 85, + 86, + 90 + ], + "mechanism": "Differential lifetime oracle runs the same gcTime/subscription cases through pooled and compiled live queries, independently observes release times under fake timers, and compares them with the documented one-tick cleanup allowance.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/pooled-live-query-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/pooled-live-query-oracle.property.test.ts", + "evidenceLines": [ + 170, + 179, + 196, + 583, + 633, + 672, + 685, + 697 + ], + "mechanism": "Independent modelEq/expectedKeys/expectedRows determine membership, while separately executed compiled Collections supply differential ordering/status/value observations. runHistory compares pooled public snapshots and folded granular changes after generated commands.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/public-container-copy.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/query/public-container-copy-oracle.test.ts", + "evidenceLines": [ + 11, + 52, + 66, + 83, + 90, + 114, + 150, + 177 + ], + "mechanism": "Query checks receive literal key arrays for a fixed parent/children history. Identity-transform and descriptor tests cover a hand-built container topology with selected leaves, using direct regression assertions rather than a reusable independently computed transform over a grammar. The identity relation is a borderline law claim; conservatively restore the original name.", + "confidence": "medium" + }, + { + "path": "packages/db/tests/query/query-api-type-algebra.test-d.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/query/query-api-type-algebra-oracle.test-d.ts", + "evidenceLines": [ + 31, + 35, + 46, + 82, + 206, + 311, + 334, + 440 + ], + "mechanism": "Primarily explicit nullable/output schema type expectations and ordinary generic assignability regressions. Selected uses production WithResult, DeepNullable is a negative control, and the generic source composition is checked using concrete expected field types. Generic preservation could be described as a law, but no independent generalized expected query type algebra is executed; conservatively restore.", + "confidence": "medium" + }, + { + "path": "packages/db/tests/query/query-api-type-algebra.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/query/query-api-type-algebra-oracle.test.ts", + "evidenceLines": [ + 6, + 33, + 37, + 88, + 93, + 126 + ], + "mechanism": "Three concrete runtime examples assert unmatched projection undefined, one matched/one unmatched union result, and one retained null value. There is no independent type or runtime reference computation.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/replay-failure-boundary.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/query/replay-failure-boundary-oracle.test.ts", + "evidenceLines": [ + 13, + 16, + 43, + 104, + 131, + 148, + 166, + 178, + 195 + ], + "mechanism": "The failure-mode/consumer matrix repeats one fixed replay, peer, retained-child, and recovery history with literal versions 1/2/3/4. evaluateReferenceExpression selects fixture rows in the adapter; it is not used to independently judge the publication/recovery result.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/scheduler.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/query/scheduler-oracle.test.ts", + "evidenceLines": [ + 73, + 117, + 133, + 671, + 1038, + 1146, + 1238, + 1422 + ], + "mechanism": "Scheduler setup and batch-recording helpers drive fixed reentry, failure, join, restart, and scheduling regressions. expectedObservation constructs a single literal Ada insertion; the shared-source/consumer matrix checks one hardcoded next-left/next-right publication. No independent scheduler state machine or reusable derived expected schedule.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/subquery-user-value-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/subquery-user-value-oracle.test.ts", + "evidenceLines": [ + 60, + 144, + 155, + 201, + 501, + 580, + 741, + 785 + ], + "mechanism": "expectedDistinctRows independently filters plain source rows and deduplicates with Set. Incremental source model Maps drive complete recomputation and comparison to public query outputs; other lanes independently count/filter and recompute singleton projections.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/validate-aliases.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/query/validate-aliases-oracle.test.ts", + "evidenceLines": [ + 59, + 108, + 165, + 276, + 296, + 368, + 400 + ], + "mechanism": "Concrete query shapes assert throw/pass outcomes for parent/grandparent alias reuse, union branches, and sibling scope reuse. There is no independent lexical-scope model computing validity across a query grammar.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/virtual-row-fields-oracle.test-d.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/virtual-row-fields-oracle.test-d.ts", + "evidenceLines": [ + 64, + 89, + 223, + 267, + 283, + 304, + 313 + ], + "mechanism": "PublishedValueFor independently maps semantic publication categories to values with or without virtual fields; the type suite compares actual whole-row/projected/opaque query outputs to that generalized relation.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/virtual-row-fields-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/virtual-row-fields-oracle.test.ts", + "evidenceLines": [ + 73, + 77, + 388, + 409, + 422 + ], + "mechanism": "expectsVirtualFields independently decides virtual-field eligibility from publication subject (root/whole-row versus projected/nested/opaque). Real query observations are classified and checked field by field against this rule across the category domain.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/where-predicate-publication-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/query/where-predicate-publication-oracle.property.test.ts", + "evidenceLines": [ + 192, + 203, + 229, + 634, + 691, + 825, + 831 + ], + "mechanism": "Independent Kleene evaluator expectedTruth and expectedVisible touched-row visibility model compute expected membership from declarative predicates and model history. runHistory compares the production publication paths with those recomputed observations.", + "confidence": "high" + }, + { + "path": "packages/db/tests/query/where-prefilter-property-visibility.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/query/where-prefilter-property-visibility-oracle.test.ts", + "evidenceLines": [ + 19, + 45, + 68, + 91, + 131, + 168 + ], + "mechanism": "Fixed inherited, non-enumerable, enumerable, and getter fixtures assert literal snapshot/subscriber key arrays and getter behavior. No independent visibility evaluator computes expected results across a model domain.", + "confidence": "high" + }, + { + "path": "packages/db/tests/reference-expression-oracle.ts", + "role": "oracle-definition", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/reference-expression-oracle.ts", + "evidenceLines": [ + 1, + 3, + 15, + 19, + 22, + 29, + 32, + 43, + 46, + 58 + ], + "mechanism": "Defines a standalone interpreter for the supported BasicExpression subset, with its own scalar comparison, property traversal, boolean operators, null handling, comparisons, and IN semantics; it imports only the IR type. The cursor oracle directly uses this interpreter in expectCursorDenotation to judge production-generated cursor predicates against independent tuple comparison (cursor-oracle.property.test.ts:103-119). This is semantic reference computation, although other consumers also use it in provider fixtures.", + "ownerPaths": [ + "packages/db/tests/cursor-oracle.property.test.ts" + ], + "confidence": "high" + }, + { + "path": "packages/db/tests/replay-publication-storage.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/replay-publication-storage-oracle.test.ts", + "evidenceLines": [ + 44, + 83, + 101, + 145, + 213, + 238, + 263, + 275, + 278, + 285, + 301, + 360, + 408 + ], + "mechanism": "Contains fixed replay/restart publication histories with literal expected batches and rows. expectDelegatedStorageAbsent is a small assertion wrapper invoked for one real retention snapshot (plus synthetic calibration); the reference expression evaluator only supplies fixture input rows. This does not establish a generalized storage/model oracle, so restore the original filename.", + "confidence": "high" + }, + { + "path": "packages/db/tests/single-row-ref-proxy.test-d.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/single-row-ref-proxy-oracle.test-d.ts", + "evidenceLines": [ + 59, + 61, + 82, + 112, + 200, + 222, + 234, + 252 + ], + "mechanism": "Ordinary expectTypeOf, assignment-compatibility, and ts-expect-error examples cover optional objects, unions, built-ins, and generic callback use. There is no independent generalized type relation or algebra calculating expected projections.", + "confidence": "high" + }, + { + "path": "packages/db/tests/sync-reused-row.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/sync-reused-row-oracle.test.ts", + "evidenceLines": [ + 47, + 53, + 59, + 69, + 73, + 89, + 100, + 106 + ], + "mechanism": "Five concrete reused-row regression cases assert development errors, previousValue acceptance, unchanged rewrites, fresh-object updates, and the production environment exception. There is no independent row/history model or reusable law checker.", + "confidence": "high" + }, + { + "path": "packages/db/tests/trace-runner.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/trace-runner-oracle.ts", + "evidenceLines": [ + 19, + 30, + 36, + 77, + 88, + 89, + 91, + 106, + 109, + 121 + ], + "mechanism": "Generic execution/checkpoint orchestration: receives observe/recompute/assertEqual callbacks from callers and invokes them after startup/steps; preserves errors and runs cleanup. It supplies no product-specific expected result, model, grammar or independent judgment. This is runner infrastructure, not a harness-test (it contains no tests). Restore trace-runner.ts.", + "ownerPaths": [ + "packages/db/tests/query/includes-oracle.property.test.ts", + "packages/db/tests/query/includes-publication-oracle.test.ts", + "packages/db/tests/query/includes-query-shape-oracle.test.ts", + "packages/db/tests/query/includes-temporal-oracle.test.ts" + ], + "confidence": "high" + }, + { + "path": "packages/db/tests/transactions.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/transactions-oracle.test.ts", + "evidenceLines": [ + 169, + 178, + 191, + 319, + 343, + 348, + 359, + 412, + 421, + 425, + 428, + 437, + 765, + 810, + 821, + 890 + ], + "mechanism": "Ordinary transaction API/regression suite: tests set up concrete transactions and hardcode their expected states, errors, rows and callback batches. The parameterized late-rejection test only substitutes several rejection payloads into one fixed history, with literal expected results. No independent transaction state reducer, differential implementation or reusable generalized judgment was found. Restore transactions.test.ts.", + "confidence": "high" + }, + { + "path": "packages/db/tests/utils-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/db/tests/utils-oracle.property.test.ts", + "evidenceLines": [ + 250, + 260, + 275, + 284, + 286, + 493, + 518, + 534, + 947, + 956, + 958 + ], + "mechanism": "Generated change-event equality laws construct equivalent copies, sharing/unfolding variants, and deliberate single-payload changes independently of deepEquals. The same bidirectional checker judges cyclic mixed-container rings, every changed reachable ring payload, and Map/Set insertion-order invariance against the real comparator.", + "confidence": "high" + }, + { + "path": "packages/db/tests/virtual-props-cache.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/db/tests/virtual-props-cache-oracle.test.ts", + "evidenceLines": [ + 18, + 23, + 35, + 39, + 45, + 57, + 61, + 65, + 79, + 87, + 88 + ], + "mechanism": "Three narrow cache regressions compare callback/read object identity for one row updated with [1,2], then assert cache size zero after explicit deletion/rollback. These are valuable direct invariant assertions, but no independent reusable model/comparison program or transformed-history oracle is present. Conservatively restore the original filename instead of treating every identity assertion as an oracle owner.", + "confidence": "medium" + }, + { + "path": "packages/db/tests/virtual-row-legacy-guard-oracle.test-d.ts", + "role": "example-test", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/db/tests/virtual-row-legacy-guard-oracle.test-d.ts", + "evidenceLines": [ + 5, + 6, + 13, + 15, + 19, + 20, + 21 + ], + "mechanism": "Two concrete expectTypeOf assertions pin optional versus required $hasPendingWrites on one legacy-guard fixture and one WithVirtualProps instantiation. No generalized independent type relation/algebra. Existing pre-PR oracle filename is out of scope for renaming.", + "confidence": "high" + }, + { + "path": "packages/electric-db-collection/e2e/sql-predicate-semantics-oracle.e2e.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/electric-db-collection/e2e/sql-predicate-semantics-oracle.e2e.test.ts", + "evidenceLines": [ + 230, + 292, + 315, + 319 + ], + "mechanism": "The independent recursive three-valued evaluate(Predicate, row) function computes expected selected IDs. The test executes the compiled predicate against real PostgreSQL and compares the returned IDs with that reference computation.", + "confidence": "high" + }, + { + "path": "packages/electric-db-collection/e2e/subset-sql-acceptance.e2e.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/electric-db-collection/e2e/subset-sql-acceptance-oracle.e2e.test.ts", + "evidenceLines": [ + 22, + 47, + 65, + 83, + 110 + ], + "mechanism": "selectedIds only observes provider results. A fixed table fixture is checked against literal expected ID arrays for individual SQL examples; the file has no independent evaluator or generalized law checker.", + "confidence": "high" + }, + { + "path": "packages/electric-db-collection/tests/ORACLE_MUTATIONS.md", + "role": "documentation", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/electric-db-collection/tests/ORACLE_MUTATIONS.md", + "evidenceLines": [ + 1, + 3, + 13, + 115 + ], + "mechanism": "A prose mutation ledger describing source changes, replay commands, and named tests. It does not execute independent expected-result judgment. Its preexisting filename remains unchanged.", + "confidence": "high" + }, + { + "path": "packages/electric-db-collection/tests/electric-descriptor-isolation-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/electric-db-collection/tests/electric-descriptor-isolation-oracle.test.ts", + "evidenceLines": [ + 230, + 242, + 269, + 280, + 285, + 473 + ], + "mechanism": "runTagHistory owns an independent Map of complete rows and tag sets. Generated edits and removals update this model, and the check function compares both production public rows and durable rows with expectedRows computed from the Map.", + "confidence": "high" + }, + { + "path": "packages/electric-db-collection/tests/electric-oracle-lifecycle.test.ts", + "role": "harness-test", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/electric-db-collection/tests/electric-oracle-lifecycle.test.ts", + "evidenceLines": [ + 2, + 11, + 18, + 35, + 105, + 113 + ], + "mechanism": "Tests timeout and resource-cleanup helpers using fake primary/secondary errors and fake timers. These assertions concern the oracle lifecycle harness rather than a product law. Its preexisting filename remains unchanged.", + "ownerPaths": [ + "packages/electric-db-collection/tests/electric-oracle-lifecycle.ts" + ], + "confidence": "high" + }, + { + "path": "packages/electric-db-collection/tests/electric-oracle-lifecycle.ts", + "role": "driver-or-fixture", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/electric-db-collection/tests/electric-oracle-lifecycle.ts", + "evidenceLines": [ + 10, + 37, + 50, + 61, + 73 + ], + "mechanism": "Provides atCheckpoint timeout handling, setup wrappers, and resource cleanup preserving primary failures. It supplies execution lifecycle support and computes no independent expected product result. Its preexisting filename remains unchanged.", + "ownerPaths": [ + "packages/electric-db-collection/tests/electric-oracle.property.test.ts", + "packages/electric-db-collection/tests/electric-sdk-delivery-oracle.property.test.ts" + ], + "confidence": "high" + }, + { + "path": "packages/electric-db-collection/tests/electric-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/electric-db-collection/tests/electric-oracle.property.test.ts", + "evidenceLines": [ + 1241, + 1274, + 1291, + 3831, + 3850, + 3851, + 3852, + 3892 + ], + "mechanism": "applyReferenceBatch and expectedSnapshots independently model pending and committed rows; recomputeCommittedRows independently reconstructs committed results. Generated delivery histories compare full public snapshots from real Electric, persisted, and Query drivers with referenceSnapshots.", + "confidence": "high" + }, + { + "path": "packages/electric-db-collection/tests/electric-recovery-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/electric-db-collection/tests/electric-recovery-oracle.test.ts", + "evidenceLines": [ + 106, + 1067, + 1073, + 1095, + 1132, + 1143, + 1147, + 1204 + ], + "mechanism": "assertPublicationHistory updates an independent complete-row Map from authored commands and compares production public and durable rows with it after transitions. expectWholeRecoveryTrace independently judges complete allowed recovery traces across generated histories.", + "confidence": "high" + }, + { + "path": "packages/electric-db-collection/tests/electric-resume-snapshot-races.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/electric-db-collection/tests/electric-resume-snapshot-races-oracle.test.ts", + "evidenceLines": [ + 124, + 164, + 384, + 445, + 507, + 976 + ], + "mechanism": "Borderline parameterized lifecycle fixture: runRace seeds fixed rows and branches on scenario flags to choose literal expected rows, statuses, metadata, and a fixed legacy observation. replacesUncertifiedBaseline is a scenario predicate, not a separately computed baseline-tuple/state model. No reusable independent reference computation was identified; conservatively restore the original name.", + "confidence": "medium" + }, + { + "path": "packages/electric-db-collection/tests/electric-sdk-delivery-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/electric-db-collection/tests/electric-sdk-delivery-oracle.property.test.ts", + "evidenceLines": [ + 374, + 375, + 687, + 712, + 723, + 790 + ], + "mechanism": "Generated snapshot histories update an independent union Map and compare production public rows with it. Separate generated tag histories use an independent bitmask DNF visibility model, compute visible rows from that model, and compare production observations after transitions.", + "confidence": "high" + }, + { + "path": "packages/electric-db-collection/tests/electric-sdk-framing.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/electric-db-collection/tests/electric-sdk-framing-oracle.test.ts", + "evidenceLines": [ + 7, + 28, + 67, + 72 + ], + "mechanism": "Two boolean-parameterized cases feed fixed HTTP responses to the real SDK and assert three literal callback payloads. This is a useful provider framing example, but it does not own a generalized independent reference checker.", + "confidence": "high" + }, + { + "path": "packages/electric-db-collection/tests/pg-serializer-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/electric-db-collection/tests/pg-serializer-oracle.property.test.ts", + "evidenceLines": [ + 131, + 183, + 187, + 320, + 323 + ], + "mechanism": "An independent finite PostgreSQL array decoder parses the actual serializer output. A generic round-trip checker compares the decoded result with normalized input over generated serializer cases.", + "confidence": "high" + }, + { + "path": "packages/electric-db-collection/tests/schema-transform-conformance.test-d.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/electric-db-collection/tests/schema-transform-conformance-oracle.test-d.ts", + "evidenceLines": [ + 63, + 81, + 87, + 95, + 105 + ], + "mechanism": "Concrete Zod schema examples assert ordinary inferred-type equalities with expectTypeOf and reject particular sample inputs with ts-expect-error. No generalized independent type relation or reference algebra is present.", + "confidence": "high" + }, + { + "path": "packages/electron-db-sqlite-persistence/tests/electron-ipc.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/electron-db-sqlite-persistence/tests/electron-ipc-oracle.test.ts", + "evidenceLines": [ + 367, + 458, + 853, + 2236, + 2237, + 2294, + 2571 + ], + "mechanism": "Large collection of fixed IPC/reopen/ownership regressions. Expected transactions, rows, error types, and callback counts are manually specified. The small parameter tables supply their own expected values (for example expectedUnloads 1/2); no independent generated-history ledger, state reducer, differential computation, or reusable generalized product checker was found. Do not retain the rename merely because these mirror Browser laws.", + "confidence": "high" + }, + { + "path": "packages/expo-db-sqlite-persistence/e2e/mobile-persisted-collection-conformance-suite.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/expo-db-sqlite-persistence/e2e/mobile-persisted-collection-conformance-suite-oracle.ts", + "evidenceLines": [ + 36, + 49, + 138, + 153, + 211, + 225, + 299 + ], + "mechanism": "Creates/seeds eager and on-demand Collections, supplies mutation and cleanup callbacks, and invokes the shared conformance suite. Seed generation and Collection operations are host inputs/driver mechanics, not expected-result logic.", + "ownerPaths": [ + "packages/db-sqlite-persistence-core/tests/contracts/persisted-collection-conformance-contract.ts" + ], + "confidence": "high" + }, + { + "path": "packages/node-db-sqlite-persistence/tests/cached-upstream-history.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/node-db-sqlite-persistence/tests/cached-upstream-history-oracle.test.ts", + "evidenceLines": [ + 14, + 21, + 22, + 26, + 46, + 77, + 78, + 88, + 90, + 93, + 97, + 104, + 105, + 117, + 129, + 136 + ], + "mechanism": "Two fixed histories parameterized only by throw versus reject; cached/next/fresh rows and every expected snapshot are literal, with one branch for whether a failed acquisition was accepted. The later case supplies a real SQLite harness to the same fixed two-row restart helper. No independent ownership state model or generated/differential judgment.", + "confidence": "high" + }, + { + "path": "packages/node-db-sqlite-persistence/tests/expression-index-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/node-db-sqlite-persistence/tests/expression-index-oracle.test.ts", + "evidenceLines": [ + 253, + 258, + 259, + 934, + 951, + 952, + 989, + 998, + 1004, + 1087, + 1092, + 1104, + 1112, + 1125, + 1133 + ], + "mechanism": "Independent path walker and strict scalar filter compute expected keys from seeded/scanned rows; actual indexed adapter rows and captured raw SQL rows must match. Generated scenario matrix additionally checks independently specified keys and named-index plans against real better-sqlite3 execution. Existing pre-PR oracle name remains unchanged.", + "confidence": "high" + }, + { + "path": "packages/node-db-sqlite-persistence/tests/node-driver.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/node-db-sqlite-persistence/tests/node-driver-oracle.test.ts", + "evidenceLines": [ + 10, + 27, + 61, + 73, + 104, + 115, + 119, + 123, + 148 + ], + "mechanism": "Registers the fixed shared driver examples and adds two fixed shared-handle transaction/savepoint schedules. Their expected output is literal [1,2,3] and [1,3]; no independent admission reference or generalized schedule checker exists in this file.", + "confidence": "high" + }, + { + "path": "packages/node-db-sqlite-persistence/tests/node-persistence.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/node-db-sqlite-persistence/tests/node-persistence-oracle.test.ts", + "evidenceLines": [ + 16, + 41, + 55, + 63, + 85, + 115, + 131, + 180, + 187, + 202, + 226, + 233, + 246, + 267, + 274, + 326, + 333, + 393, + 413 + ], + "mechanism": "Provides host registration for the shared fixed examples, followed by hardcoded options/round-trip/pruning/schema-policy regressions. Row-cap cases always write seq 1,2,3 and assert literal [2,3], while age and disabled-pruning cases assert other literal sequences; there is no reference pruning computation.", + "confidence": "high" + }, + { + "path": "packages/node-db-sqlite-persistence/tests/node-sqlite-core-adapter-contract.test.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/node-db-sqlite-persistence/tests/node-sqlite-core-adapter-contract-oracle.test.ts", + "evidenceLines": [ + 9, + 12, + 13, + 18, + 21, + 31, + 33 + ], + "mechanism": "Constructs a temporary better-sqlite3 driver and SQLiteCorePersistenceAdapter, supplies cleanup, and registers runSQLiteCoreAdapterContractSuite. All independent expected-result computation belongs to the imported core adapter owner.", + "ownerPaths": [ + "packages/db-sqlite-persistence-core/tests/sqlite-core-adapter-oracle.test.ts" + ], + "confidence": "high" + }, + { + "path": "packages/offline-transactions/tests/KeyScheduler-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/offline-transactions/tests/KeyScheduler-oracle.property.test.ts", + "evidenceLines": [ + 314, + 324, + 353, + 388, + 395, + 420, + 448, + 470, + 485, + 494, + 506, + 511, + 520, + 522 + ], + "mechanism": "The independent pending ledger plus fake time computes FIFO order, eligible head, active count, and payload snapshot in ordered/expectedNext/expectedSnapshot. runHistory applies each legal generated command separately to that ledger and the real KeyScheduler, then compares observations after every command. This is an executable state-model oracle, not merely a group of scheduler examples.", + "confidence": "high" + }, + { + "path": "packages/offline-transactions/tests/connectivity-replay-oracle.test.ts", + "role": "example-test", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/offline-transactions/tests/connectivity-replay-oracle.test.ts", + "evidenceLines": [ + 32, + 37, + 38, + 39, + 42, + 81, + 98, + 103, + 107, + 109, + 126, + 128, + 132 + ], + "mechanism": "Two parameterized examples execute the same single-write hidden-to-visible history, differing only in the notification trigger. expectedAt is a lookup of two literal expected observation records (0 calls/1 row/pending versus 1 call/0 rows/fulfilled), not a state transition model or generalized computation over histories. Conservatively classify this bounded regression as an example test. Its oracle filename predates this PR and must remain unchanged.", + "confidence": "medium" + }, + { + "path": "packages/offline-transactions/tests/fifo-retry-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/offline-transactions/tests/fifo-retry-oracle.property.test.ts", + "evidenceLines": [ + 63, + 68, + 70, + 71, + 75, + 84, + 85, + 86, + 87, + 99, + 138, + 142, + 146, + 151, + 161, + 167, + 172, + 177 + ], + "mechanism": "expectedStateAt independently computes the full expected provider call sequence and outbox cardinality from elapsed time, generated retry delay, and the input transaction IDs. One shared comparison receives real TransactionExecutor/OutboxManager observations at before, delay-minus-one, and deadline cuts across generated count/delay inputs; this is a reusable threshold/FIFO reference computation.", + "confidence": "high" + }, + { + "path": "packages/offline-transactions/tests/filtered-replay-work.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/offline-transactions/tests/filtered-replay-work-oracle.test.ts", + "evidenceLines": [ + 8, + 9, + 11, + 32, + 40, + 50, + 51, + 72, + 92, + 97, + 98, + 99, + 101, + 102 + ], + "mechanism": "This is one instrumented regression with a fixed n=100 outbox and one even-index beforeRetry filter. It asserts a fixed-scenario work budget (n*4) and the known even-ID sequence. Arithmetic constructs this example's expected array, but there is no reusable reference/checker over variable inputs, history grammar, differential relation, or finite domain enumeration. The computed budget and expected IDs make this borderline; conservatively restore the original name instead of promoting one performance example to an oracle owner.", + "confidence": "medium" + }, + { + "path": "packages/offline-transactions/tests/indexeddb-write-settlement.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/offline-transactions/tests/indexeddb-write-settlement-oracle.test.ts", + "evidenceLines": [ + 4, + 5, + 9, + 43, + 50, + 54, + 66, + 71, + 72, + 84, + 92, + 95, + 99, + 108, + 109, + 113, + 121, + 122, + 126, + 135, + 149 + ], + "mechanism": "controlledWrite is a production-driver fixture that fires chosen IndexedDB callbacks and records the adapter promise outcome. The it.each cases repeat fixed event sequences for set/delete/clear and assert literal pending/fulfilled/rejected outcomes or an exact supplied error. No independent reference state machine or generalized law checker computes an expected outcome from an event history.", + "confidence": "high" + }, + { + "path": "packages/offline-transactions/tests/leadership-replay-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/offline-transactions/tests/leadership-replay-oracle.property.test.ts", + "evidenceLines": [ + 93, + 103, + 107, + 111, + 118, + 1760, + 1763, + 1764, + 1766, + 1767, + 1790, + 1794, + 1799, + 1811, + 1813, + 1819, + 1824 + ], + "mechanism": "Mixed file contains a concrete independent replay oracle: foldReplayLedger folds stored, captured-read, and durably-removed events into unfinished IDs without using production revision fences. A generated stale-read history drives real OutboxManager storage reads/removals and compares delivered IDs with that independent fold. This specific reusable mechanism qualifies the file even though many other cases are fixed examples.", + "confidence": "high" + }, + { + "path": "packages/offline-transactions/tests/oracle-lifecycle.test.ts", + "role": "harness-test", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/offline-transactions/tests/oracle-lifecycle.test.ts", + "evidenceLines": [ + 2, + 13, + 19, + 29, + 30, + 39, + 45, + 48, + 51, + 70, + 81, + 96, + 107, + 111 + ], + "mechanism": "Calls cleanupOfflineOracle and atOracleCheckpoint directly with held/rejecting cleanup actions, asserting warning/error preservation, timeout reporting, and later cleanup calls. It tests the oracle's test harness rather than offline product behavior. Filename already contained oracle before the PR and must remain unchanged.", + "ownerPaths": [ + "packages/offline-transactions/tests/oracle-lifecycle.ts" + ], + "confidence": "high" + }, + { + "path": "packages/offline-transactions/tests/storage-delete-settlement.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/offline-transactions/tests/storage-delete-settlement-oracle.test.ts", + "evidenceLines": [ + 12, + 13, + 17, + 24, + 25, + 26, + 29, + 40, + 49, + 54, + 67, + 69, + 73, + 75, + 76 + ], + "mechanism": "Two hardcoded regression histories: one localStorage key retained after a thrown removeItem error, and one known outbox transaction retained after a held read overlaps failed removal. Assertions compare exact supplied values/objects/errors. No independent reusable computation, model, or generalized checker is present; being a receiving companion for a larger oracle does not make this file an owner.", + "confidence": "high" + }, + { + "path": "packages/offline-transactions/tests/transaction-serializer-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/offline-transactions/tests/transaction-serializer-oracle.property.test.ts", + "evidenceLines": [ + 67, + 84, + 97, + 105, + 110, + 114, + 134, + 147, + 155, + 242, + 261, + 297, + 299, + 302, + 308, + 319, + 333, + 1392, + 1393, + 1400 + ], + "mechanism": "Independent semantic Pair values recursively construct matching runtime and expected-wire trees from generated leaves, arrays, escaped objects, and edits. checkRoundtrip computes expectedWire from those inputs and compares real TransactionSerializer encode/decode output against it, including independently authored decode inputs. mutationMeaningProperty executes that reference across generated edits/time values. Fixed wire examples elsewhere do not replace this clear reusable reference computation.", + "confidence": "high" + }, + { + "path": "packages/offline-transactions/tests/transaction-settlement-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/offline-transactions/tests/transaction-settlement-oracle.property.test.ts", + "evidenceLines": [ + 133, + 136, + 137, + 139, + 140, + 141, + 169, + 196, + 197, + 209, + 212, + 223, + 239, + 242, + 243, + 245, + 256, + 258, + 292, + 293, + 306, + 308, + 309 + ], + "mechanism": "For generated key-sharing, row width, and per-transaction success/failure sequences, assertState computes expected caller statuses from the completed prefix and each supplied outcome, folds independent successful server rows, overlays unfinished local rows, and computes remaining outbox IDs. Actual provider calls, public Collection rows, promise outcomes, and outbox contents are compared after each controlled provider release. This is an independent prefix/row reference computation; the mixed fixed examples do not negate ownership.", + "confidence": "high" + }, + { + "path": "packages/powersync-db-collection/tests/correctness-oracle.test.ts", + "role": "example-test", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/powersync-db-collection/tests/correctness-oracle.test.ts", + "evidenceLines": [ + 98, + 105, + 256, + 644, + 784, + 870 + ], + "mechanism": "Fixed native SQLite and Collection scenarios assert literal patch tuples, key arrays, and comparator order. observeUpdate packages actual observations and observeViewKeys projects actual keys; neither computes independent expected results. The existing correctness-oracle filename predates the PR and must remain unchanged.", + "confidence": "high" + }, + { + "path": "packages/powersync-db-collection/tests/schema-transform-conformance.test-d.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/powersync-db-collection/tests/schema-transform-conformance-oracle.test-d.ts", + "evidenceLines": [ + 159, + 164, + 212, + 218, + 246, + 262 + ], + "mechanism": "Two concrete schema paths assert ordinary expectTypeOf equalities and specific rejected inputs/deserializers. Extracting production types is not an independent generalized type relation or algebra.", + "confidence": "high" + }, + { + "path": "packages/query-db-collection/tests/cursor-pagination-oracle.integration.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/query-db-collection/tests/cursor-pagination-oracle.integration.test.ts", + "evidenceLines": [ + 188, + 246, + 248, + 257, + 271, + 272, + 360, + 370, + 391, + 393, + 394, + 403, + 408, + 409 + ], + "mechanism": "Executes an independent row-array mutation model against Query Collection manual writes, then compares production live-query/window-controller snapshots and callback publications with expectedWindow (full filter/sort/slice and exhaustion). The matrix computes answers from source rows, page width, direction and scope instead of enumerating expected output literals.", + "confidence": "high" + }, + { + "path": "packages/query-db-collection/tests/cursor-pagination-oracle.no-peek.integration.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/query-db-collection/tests/cursor-pagination-oracle.no-peek.integration.test.ts", + "evidenceLines": [ + 86, + 94, + 96, + 98, + 101, + 126, + 129, + 132, + 140, + 141, + 144, + 198 + ], + "mechanism": "The four-size matrix drives real Collection setWindow/preload and createLiveQueryWindowController. It computes full-source expectedWindow for the candidate graph result and differentially compares the production controller data/continuation with that independent candidate result; a filtered graph is also checked against full-source recomputation. The claim is limited to this experimental controlled bridge, but actual production observations are judged.", + "confidence": "high" + }, + { + "path": "packages/query-db-collection/tests/cursor-pagination.no-peek.test.ts", + "role": "harness-test", + "namingDecision": "restore original name", + "evidencePath": "packages/query-db-collection/tests/cursor-pagination-oracle.no-peek.test.ts", + "evidenceLines": [ + 5, + 6, + 7, + 18, + 20, + 70, + 88, + 90, + 92, + 98, + 100, + 111, + 113 + ], + "mechanism": "Model/prototype validation, the closest supplied harness-test role: generated histories compare createNoPeekDemandModel with expectedWindow, but the subject is exclusively the test-only candidate plus synthetic createFactTransport. No production pager, Query Collection, live-query graph or window controller is exercised. Its generated input does not make it an executable product oracle owner; restore the original filename. Ambiguity: this is a prototype-model test rather than a literal runner/configuration test.", + "ownerPaths": [ + "packages/query-db-collection/tests/cursor-pagination-oracle.no-peek.integration.test.ts" + ], + "confidence": "medium" + }, + { + "path": "packages/query-db-collection/tests/cursor-pagination.boundary-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/query-db-collection/tests/cursor-pagination.boundary-oracle.test.ts", + "evidenceLines": [ + 88, + 102, + 114, + 115, + 120, + 140, + 147, + 155, + 178, + 183, + 212, + 214, + 215, + 216 + ], + "mechanism": "Generated page widths exercise real pager/Query Collection cancellation and replacement. The judgment derives complete replacement rows from the independent source array and replacement action, requires success exactly for replacement, and checks that an aborted reader leaves the shared transport and cached source slice usable. This is a partial generalized boundary law, alongside fixed regression tests; its oracle name predates this PR.", + "confidence": "medium" + }, + { + "path": "packages/query-db-collection/tests/cursor-pagination.cache-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/query-db-collection/tests/cursor-pagination.cache-oracle.test.ts", + "evidenceLines": [ + 59, + 67, + 380, + 395, + 412, + 413, + 416, + 435, + 456, + 457, + 461, + 462, + 465, + 469, + 471 + ], + "mechanism": "A small independent cache-history state tracks permitted source snapshot, freshness deadline and presence through generated read/grow/age/invalidate/change/remove actions. Each production pager read is judged using expectedRows; starts and repeated-read work are checked against independently tracked freshness. The existing oracle filename is unchanged by this PR.", + "confidence": "high" + }, + { + "path": "packages/query-db-collection/tests/cursor-pagination.oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/query-db-collection/tests/cursor-pagination.oracle.test.ts", + "evidenceLines": [ + 108, + 114, + 115, + 134, + 143, + 236, + 244, + 245, + 247, + 249, + 259, + 267, + 272 + ], + "mechanism": "checkRead computes expectedRows independently of opaque tokens and compares the real production pager result (the pager companion wraps the exported production createCursorPager). Generated source/window histories, reversed read histories and backend page repartition reuse this judgment, with explicit wrong-order/duplicate/capped-result controls. Existing oracle name, not a PR rename.", + "confidence": "high" + }, + { + "path": "packages/query-db-collection/tests/cursor-pagination.publication-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/query-db-collection/tests/cursor-pagination.publication-oracle.test.ts", + "evidenceLines": [ + 78, + 87, + 96, + 104, + 105, + 106, + 169, + 184, + 212, + 216, + 222, + 233, + 375, + 381 + ], + "mechanism": "checkCachedSlice computes full-source expectedRows and checks both returned values and exact row-access work against the slice cardinality across generated count/offset/limit inputs. The refresh property independently recomputes the replacement snapshot and judges real QueryObserver publication, including a stale-result negative control. Existing oracle name remains unchanged.", + "confidence": "high" + }, + { + "path": "packages/query-db-collection/tests/cursor-pagination/model-oracle.ts", + "role": "oracle-definition", + "namingDecision": "retain PR rename", + "evidencePath": "packages/query-db-collection/tests/cursor-pagination/model-oracle.ts", + "evidenceLines": [ + 13, + 19, + 20, + 21, + 22, + 26, + 29, + 34, + 35 + ], + "mechanism": "Owns expectedRows: filter the independent complete source, sort by declared rank/id order, slice by offset/limit, clone. expectedWindow derives visible prefix and hasNextPage from that full computation. These functions are the expected-result side of the production pager, cache, publication and integration owners.", + "ownerPaths": [ + "packages/query-db-collection/tests/cursor-pagination.oracle.test.ts", + "packages/query-db-collection/tests/cursor-pagination.cache-oracle.test.ts", + "packages/query-db-collection/tests/cursor-pagination.publication-oracle.test.ts", + "packages/query-db-collection/tests/cursor-pagination-oracle.integration.test.ts", + "packages/query-db-collection/tests/cursor-pagination-oracle.no-peek.integration.test.ts" + ], + "confidence": "high" + }, + { + "path": "packages/query-db-collection/tests/cursor-pagination/no-peek.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/query-db-collection/tests/cursor-pagination/no-peek-oracle.ts", + "evidenceLines": [ + 3, + 15, + 33, + 36, + 50, + 63, + 79, + 89, + 99, + 110, + 111 + ], + "mechanism": "Implements the proposed test-only no-peek candidate: retain/retire demand, perform acquisitions, validate response facts, retain snapshots and dispatch listeners. It is the algorithm under experimental evaluation, with independent truth supplied by model-oracle.ts; it does not own that full-source judgment. The integration test does also compare this candidate with the production controller, so the alternative-reference interpretation is borderline. Conservatively restore no-peek.ts instead of treating every tested candidate as an oracle definition.", + "ownerPaths": [ + "packages/query-db-collection/tests/cursor-pagination-oracle.no-peek.integration.test.ts", + "packages/query-db-collection/tests/cursor-pagination.no-peek.test.ts" + ], + "confidence": "medium" + }, + { + "path": "packages/query-db-collection/tests/includes-work-counter-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/query-db-collection/tests/includes-work-counter-oracle.test.ts", + "evidenceLines": [ + 138, + 177, + 178, + 179, + 244, + 247, + 269, + 286, + 440, + 444, + 448, + 470, + 478, + 490, + 493, + 501, + 504, + 532, + 560 + ], + "mechanism": "expectedTreeCounts derives cardinalities from root count and branching factors; expectedTreeEntries independently reconstructs the source foreign-key world and checks observed nested values/parentage. An additional metamorphic comparison adds disconnected branches and requires identical tree rows, source delivery and demand work. The finite size matrix uses computed judgments against real Query Collection/includes results, not hand-listed answers.", + "confidence": "high" + }, + { + "path": "packages/query-db-collection/tests/load-subset-lifecycle-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/query-db-collection/tests/load-subset-lifecycle-oracle.test.ts", + "evidenceLines": [ + 386, + 403, + 419, + 420, + 422, + 432, + 443, + 446, + 449, + 763, + 777, + 784, + 793, + 897, + 918, + 947, + 949, + 951, + 969, + 979, + 1438, + 1441, + 1448, + 1450 + ], + "mechanism": "Mixed lifecycle examples plus a narrow metamorphic demand-identity owner: constructs operand-swapped AND/OR/equality and equal-Date requests, exercises real Query Collection loads, and checks transport reuse plus distinct-result/owner separation through a common independent trace judgment. The qualifying mechanism is semantic-equivalence transformation and identity checking, not the numerous fixed failure/refetch examples. This is a conservative borderline owner classification; no PR rename exists to retain or reverse.", + "confidence": "medium" + }, + { + "path": "packages/query-db-collection/tests/ownership-lifecycle.oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/query-db-collection/tests/ownership-lifecycle.oracle.test.ts", + "evidenceLines": [ + 90, + 114, + 137, + 151, + 160, + 190, + 218, + 226, + 231, + 400, + 428, + 4903, + 4919, + 4938, + 4949, + 4953, + 5011, + 5020, + 5028, + 5034 + ], + "mechanism": "Owns reusable independent result-settlement and explicit-refetch obligation state models. Model actions compute waiting/deferred/applied/rejected/cancelled outcomes; expectPublicRefetchObservation judges real refetch promise observations against those computed states across overlapping barrier histories. Concrete production creation, calls and model-check checkpoints establish ownership even though the large file also contains ordinary examples and calibration tests.", + "confidence": "high" + }, + { + "path": "packages/query-db-collection/tests/query.test-d.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/query-db-collection/tests/query-oracle.test-d.ts", + "evidenceLines": [ + 39, + 58, + 63, + 73, + 76, + 127, + 189, + 228, + 492, + 507, + 513, + 514, + 535, + 549, + 559, + 573, + 579, + 874, + 887 + ], + "mechanism": "Concrete API type-inference and rejection examples use expectTypeOf and @ts-expect-error for named row/schema/config types. The documented schema-output case tests one string-to-Date transform with paired concrete configs; it supplies no generalized independent type relation, mapped algebra or reusable type-level reference computation. Restore query.test-d.ts.", + "confidence": "high" + }, + { + "path": "packages/query-db-collection/tests/ssr-dehydration-oracle.test.ts", + "role": "example-test", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/query-db-collection/tests/ssr-dehydration-oracle.test.ts", + "evidenceLines": [ + 43, + 45, + 59, + 95, + 97, + 100, + 106, + 113, + 155, + 156, + 167, + 210, + 241 + ], + "mechanism": "Four fixed SSR regression shapes all use the same literal row. expectDehydratedRow/expectHydratedRow assert that literal row and selected metadata at stream/hydration checkpoints; the serialization helper is a driver, not an independent expected-result model. There is no generalized row/cache round-trip relation evaluated from arbitrary input state. The existing pre-PR oracle filename must remain unchanged despite this narrower role classification.", + "confidence": "high" + }, + { + "path": "packages/react-db/tests/conformance.test.tsx", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/react-db/tests/conformance-oracle.test.tsx", + "evidenceLines": [ + 49, + 125, + 170, + 203, + 220 + ], + "mechanism": "React source/mount/current/cleanup adapter passes a LiveQueryDriver to runSuite in packages/db/tests/conformance/suite-oracle.ts. Additional checks are fixed publication examples or driver/scope calibration; this file does not compute independent product expectations.", + "confidence": "high" + }, + { + "path": "packages/react-db/tests/infinite-query-conformance.test.tsx", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/react-db/tests/infinite-query-conformance-oracle.test.tsx", + "evidenceLines": [ + 33, + 63, + 100, + 193, + 210 + ], + "mechanism": "React hook adapter supplies sources, mount/current observations, scheduling, and disposal to runInfiniteQuerySuite. The independent window expectation is owned by the imported shared suite; local extra checks are fixed examples or scope calibration.", + "ownerPaths": [ + "packages/db/tests/conformance/infinite-suite-oracle.ts" + ], + "confidence": "high" + }, + { + "path": "packages/react-native-db-sqlite-persistence/e2e/mobile-persisted-collection-conformance-suite.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/react-native-db-sqlite-persistence/e2e/mobile-persisted-collection-conformance-suite-oracle.ts", + "evidenceLines": [ + 138, + 144, + 150, + 154, + 161, + 180, + 205, + 212, + 300 + ], + "mechanism": "Creates a hosted database, persistence-backed eager/on-demand collections, seeds fixtures, exposes mutations and teardown, then delegates registration to runPersistedCollectionConformanceSuite. Independent judgment is in the imported E2E suites.", + "ownerPaths": [ + "packages/db-collection-e2e/src/suites/joins-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/live-updates-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/moves-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/pagination-oracle.suite.ts", + "packages/db-collection-e2e/src/suites/predicates-oracle.suite.ts" + ], + "confidence": "high" + }, + { + "path": "packages/react-native-db-sqlite-persistence/tests/op-sqlite-driver-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "retain PR rename", + "evidencePath": "packages/react-native-db-sqlite-persistence/tests/op-sqlite-driver-oracle.test.ts", + "evidenceLines": [ + 804, + 806, + 811, + 829, + 831, + 836, + 866, + 871, + 901, + 906, + 915, + 917, + 972, + 975, + 980, + 986 + ], + "mechanism": "Generated alias properties independently construct expected row objects from alias/value grammar. They check equivalent documented result carriers through the production driver, and execute generated SQL aliases through a hosted SQLite-backed OpSQLiteDriver.query against the independently constructed exact rows.", + "confidence": "high" + }, + { + "path": "packages/react-native-db-sqlite-persistence/tests/react-native-persistence.test.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/react-native-db-sqlite-persistence/tests/react-native-persistence-oracle.test.ts", + "evidenceLines": [ + 145, + 157, + 176, + 178, + 269, + 270, + 291, + 292, + 330, + 351, + 363, + 371, + 372 + ], + "mechanism": "Fixed restart and lifecycle examples check authored single/two-row values and metadata; the burst writes 50 known rows and checks count. No independent reusable persistence state machine or reference calculation is executed. A before/after registry comparison is a fixed reopen regression witness.", + "confidence": "high" + }, + { + "path": "packages/rxdb-db-collection/tests/schema-transform-conformance.test-d.ts", + "role": "example-test", + "namingDecision": "restore original name", + "evidencePath": "packages/rxdb-db-collection/tests/schema-transform-conformance-oracle.test-d.ts", + "evidenceLines": [ + 70, + 77, + 83, + 89, + 101, + 110 + ], + "mechanism": "Concrete schema examples check output/mutation inference equalities, rejected sample inputs, and an input-shaped RxCollection case. They contain no generalized independent type relation or algebra.", + "confidence": "high" + }, + { + "path": "packages/solid-db/tests/conformance.test.tsx", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/solid-db/tests/conformance-oracle.test.tsx", + "evidenceLines": [ + 52, + 136, + 176, + 220, + 284 + ], + "mechanism": "Solid source/mount/current/cleanup adapter passes a LiveQueryDriver to runSuite in packages/db/tests/conformance/suite-oracle.ts. Additional checks are fixed publication examples or driver/scope calibration; this file does not compute independent product expectations.", + "confidence": "high" + }, + { + "path": "packages/svelte-db/tests/conformance.svelte.test.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/svelte-db/tests/conformance-oracle.svelte.test.ts", + "evidenceLines": [ + 50, + 133, + 159, + 211, + 228 + ], + "mechanism": "Svelte source/mount/current/cleanup adapter passes a LiveQueryDriver to runSuite in packages/db/tests/conformance/suite-oracle.ts. Additional checks are fixed publication examples or driver/scope calibration; this file does not compute independent product expectations.", + "confidence": "high" + }, + { + "path": "packages/svelte-db/tests/infinite-query-conformance.svelte.test.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/svelte-db/tests/infinite-query-conformance-oracle.svelte.test.ts", + "evidenceLines": [ + 40, + 81, + 104, + 207, + 224 + ], + "mechanism": "Svelte hook adapter supplies sources, mount/current observations, scheduling, and disposal to runInfiniteQuerySuite. The independent window expectation is owned by the imported shared suite; local extra checks are fixed examples or scope calibration.", + "ownerPaths": [ + "packages/db/tests/conformance/infinite-suite-oracle.ts" + ], + "confidence": "high" + }, + { + "path": "packages/tauri-db-sqlite-persistence/e2e/shared/register-persisted-collection-conformance-suite.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/tauri-db-sqlite-persistence/e2e/shared/register-persisted-collection-conformance-suite-oracle.ts", + "evidenceLines": [ + 18, + 24, + 30, + 36, + 44 + ], + "mechanism": "Registers before/after lifecycle hooks, retrieves config, and forwards it to the shared suite. No independent product judgment.", + "ownerPaths": [ + "packages/db-sqlite-persistence-core/tests/contracts/persisted-collection-conformance-contract.ts" + ], + "confidence": "high" + }, + { + "path": "packages/trailbase-db-collection/tests/ORACLE.md", + "role": "documentation", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/trailbase-db-collection/tests/ORACLE.md", + "evidenceLines": [ + 1, + 3, + 11, + 51, + 72 + ], + "mechanism": "Prose describing the TrailBase lifecycle oracle contract, campaign commands, and mutation evidence. The actual independent row model and production comparisons live in lifecycle-oracle.property.test.ts. Its preexisting filename remains unchanged.", + "ownerPaths": [ + "packages/trailbase-db-collection/tests/lifecycle-oracle.property.test.ts" + ], + "confidence": "high" + }, + { + "path": "packages/trailbase-db-collection/tests/lifecycle-oracle.property.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/trailbase-db-collection/tests/lifecycle-oracle.property.test.ts", + "evidenceLines": [ + 164, + 176, + 181, + 298, + 319, + 325, + 600 + ], + "mechanism": "An independent expected Map models row state. The history interpreter updates the Map for authored insert, update, and delete commands and compares public production rows with the model while driving the real adapter and native stream across generated histories.", + "confidence": "high" + }, + { + "path": "packages/vue-db/tests/conformance.test.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/vue-db/tests/conformance-oracle.test.ts", + "evidenceLines": [ + 51, + 133, + 175, + 218, + 277 + ], + "mechanism": "Vue source/mount/current/cleanup adapter passes a LiveQueryDriver to runSuite in packages/db/tests/conformance/suite-oracle.ts. Additional checks are fixed publication examples or driver/scope calibration; this file does not compute independent product expectations.", + "confidence": "high" + }, + { + "path": "packages/vue-db/tests/infinite-query-conformance.test.ts", + "role": "driver-or-fixture", + "namingDecision": "restore original name", + "evidencePath": "packages/vue-db/tests/infinite-query-conformance-oracle.test.ts", + "evidenceLines": [ + 41, + 94, + 118, + 210, + 227 + ], + "mechanism": "Vue hook adapter supplies sources, mount/current observations, scheduling, and disposal to runInfiniteQuerySuite. The independent window expectation is owned by the imported shared suite; local extra checks are fixed examples or scope calibration.", + "ownerPaths": [ + "packages/db/tests/conformance/infinite-suite-oracle.ts" + ], + "confidence": "high" + }, + { + "path": "packages/vue-db/tests/useLiveQuery-publication-oracle.test.ts", + "role": "oracle-owner", + "namingDecision": "pre-existing name unchanged", + "evidencePath": "packages/vue-db/tests/useLiveQuery-publication-oracle.test.ts", + "evidenceLines": [ + 79, + 90, + 94, + 113, + 115, + 119, + 126, + 130, + 132, + 133, + 134, + 136, + 137, + 138 + ], + "mechanism": "Captures source state before/after a real transaction, records synchronous Vue watcher publications with source-at-callback state, and requires every publication to be a complete before/after snapshot with one final coherent publication. This checks an independent cross-surface publication relation. Filename already existed before this PR and remains unchanged.", + "confidence": "high" + } + ] +} diff --git a/docs/contributing/oracle-reviews/2026-10-03-oracle-naming-scope.md b/docs/contributing/oracle-reviews/2026-10-03-oracle-naming-scope.md new file mode 100644 index 0000000000..edabb5e583 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-03-oracle-naming-scope.md @@ -0,0 +1,86 @@ +# Oracle naming scope correction — 2026-10-03 + +Reviewed implementation: [fe40ab59191d046bda12cc9563cdc4a82b5fd97c](https://github.com/TanStack/db/commit/fe40ab59191d046bda12cc9563cdc4a82b5fd97c). +Classification evidence refers to `134b7e8bacce5999ab7e06da96d5dbe9989820f5`, before the reverse moves. +The [classification ledger](2026-10-03-oracle-naming-scope.json) names each evidence path and its line numbers, final path, role, and naming decision. + +## Corrected scope + +The earlier claim of 251 executable oracle owners and dedicated companions was too broad. +The audit category included ordinary examples, type assertions, harness tests, fixtures, and documentation. +Converting that category directly into a rename list was incorrect. +This record supersedes the naming inventory in [the original review](2026-10-02-glossary-alignment.md). +Its 39 terminology resolutions remain intact. + +This correction restores 101 original filenames and their imports, commands, replay references, and current documentation links. +The PR retains 63 moves: 57 files that execute an independent oracle and six model, grammar, or checker definitions. +Each retained move has a named mechanism and source evidence in the ledger. +Existing filenames that already contained `oracle` before this PR remain unchanged. + +| Role in the original 251-file review scope | Files | +| --- | ---: | +| Executes an independent model, comparison, or generalized checker | 121 | +| Defines an independent model, checker, or its history grammar | 10 | +| Ordinary example, regression, or concrete type-assertion tests | 67 | +| Drivers, fixtures, registration, or supporting declarations | 34 | +| Harness, checker-calibration, or prototype tests | 17 | +| Documentation | 2 | + +These are file roles within the previous inventory, not a count of distinct oracles across the repository. +Several files execute the same model, and a mixed test file can contain multiple oracle mechanisms. +The classification is conservative for borderline cases. It does not declare all pre-existing oracle names correct. + +## Naming evidence + +A retained owner executes an independent expected-result computation, state model, differential comparison, metamorphic relation, or reusable law checker. +A definition owns that model, checker, or its history grammar and names its receiving owner. +Random generation is not required. Fixed histories can execute an independent model, as the optimistic settlement histories do. +Ordinary fixed assertions and parameterization alone are insufficient. +Coverage-map entries and opening comments do not substitute for executable evidence. + +Examples of restored names include `transactions.test.ts`, `query.test-d.ts`, the framework `conformance.test` entrypoints, and `trace-runner.ts`. +They contain fixed assertions, registration/driver mechanics, or generic execution plumbing. +Examples of retained names include `SortedMap-oracle.test.ts`, which compares against native Map plus full sorting, +and `proxy-oracle.test.ts`, which contains a native-value differential callback model alongside ordinary examples. +The test-only no-peek candidate returns to `no-peek.ts`; the independent filter/sort/slice reference remains `model-oracle.ts`. + +The contributor guidance now states this distinction and requires separate counts for owners, definitions, and support. +It does not add a CI linter or introduce new product contracts. + +## Preservation checks + +An independent content comparison accounted for all 2,164 tracked files before final whitespace formatting. +It found 2,048 unchanged blobs, 114 files with mapped path-reference substitutions, and the two naming-policy documents. +The correction preserves glossary definitions, identifier/prose fixes, assertions, model rules, and campaign inputs. +Final formatting changes only whitespace around shortened paths. + +Static checks found all 101 restored paths and all 251 classified files present and indexed. +They checked 672 relative imports, 68 explicit command paths, five browser script entries, and 42 replay targets. +Replay title selectors remained valid. No stale restored-name references or escaped filename patterns remained in current files. +Historical review records and immutable revision links retain their original paths. + +## Validation after the correction + +| Runtime check | Passed | Files | +| --- | ---: | ---: | +| DB | 7,962 | 210 | +| IVM | 704 | 44 | +| Offline | 242 | 18 | +| SQLite core | 375 | 9 | +| Node SQLite | 109 | 5 | +| Browser SQLite | 242 | 9 | +| Electron SQLite | 97 | 3 | +| Electric | 629 | 13 | +| Query DB affected no-peek suites | 18 | 2 | +| React, Vue, Solid, Svelte, Angular receiving suites | 290 | 9 | +| Total | 10,668 | 322 | + +SQLite core also reports one todo. Scoped ESLint passed for 166 changed TypeScript files. +Formatting and `git diff --check` passed. +These runs used the same installed dependencies described in the original record, with coverage and integrated typechecking disabled. +The direct command was `node /vitest.mjs run --coverage.enabled=false --typecheck.enabled=false --maxWorkers=1 --reporter=dot`. +DB used two workers. Framework and Query DB checks selected only the affected receiving files. + +The earlier locked-dependency, integrated-typecheck, and intermittent Query DB baseline limits remain. +This correction did not rerun the full Query DB package, package builds, or external-provider/native-host E2E campaigns. +No new typecheck or real-host claim follows from the runtime passes. diff --git a/docs/contributing/oracle-reviews/2026-10-03-settlement-drop.md b/docs/contributing/oracle-reviews/2026-10-03-settlement-drop.md new file mode 100644 index 0000000000..8ff8818088 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-03-settlement-drop.md @@ -0,0 +1,190 @@ +# Optimistic state drops at settlement + +Pre-repair executable revision: `origin/main` at `95c3f9ec9`. +Repair branch: `fix-settled-snapshot-ack`. Production code `b5e498ddb` +through `9b82ac3dc`. Evidence head `3c85ac0de`. + +## Law + +An optimistic transaction overlays its optimistic state only while its +mutation function runs. When the mutation function settles, fulfilled or +rejected, the state drops. Success does not wait for a sync confirmation: a +handler that returns before its server row arrives shows the previous synced +row until that row applies. + +A sync transaction committed while an optimistic transaction persists is +*accepted* and held. It becomes *visible* in the publication that drops the +optimistic state, and `isPersisted` settles after that publication. + +Each sync transaction therefore has two moments: + +- **Accepted:** `commit()` returns, or a persistence wrapper finishes its + durable step. Handler-facing writes wait for this moment: Query Collection + direct writes, persisted mutation confirmation and startup, and the PowerSync + mutation path. +- **Visible:** the rows apply and publish. Commit receipts, subset loads, and + Collection readiness wait for this moment. + +An accepted transaction always applies, in commit order, and a later abort +cannot withdraw its rows. A load whose caller aborted still rejects with +`AbortError`. A source discards a stale page by checking the signal before +`commit()`. + +A sync write is `$origin: 'local'` only if it was committed while the +transaction persisted. A write committed after settlement is `'remote'`. + +Authority: user decisions recorded in the PR discussion for this branch. These +replace the accepted-snapshot retention law (#1213, #1807, #1822, #1870) and +`begin({ immediate })` (#1130). + +## Gap + +Since #1213, `recomputeOptimisticState` retained a completed direct +transaction's row until a later sync transaction wrote its key. Query +Collection direct writes used `begin({ immediate: true })` to apply during +persistence. After such a write, no later sync transaction retired the +retained row. Passing runs of `awaits persisted server responses in update +handlers` depended on an unrelated persisted sync transaction arriving later. + +The optimistic-history oracle encoded the retention law as its reference +model, so the oracle agreed with the drift. + +In #1990, a refetch canceled an earlier result's commit that was waiting for +storage. The cancellation invalidated the dependent result, and the collection +entered `error`. + +## Repair and witnesses + +- **Core:** removes `begin({ immediate })`, post-commit cancellation, retention + and its dependency machinery, the truncate optimistic snapshot, and the + direct-transaction marker. The only hold left keeps a completed row while a + queued sync transaction touches its key. +- **Receipts:** a commit receipt resolves at visibility. It carries its + acceptance moment (`withAcceptedReceipt` / `whenSyncAccepted`). Readiness + waits for accepted rows to publish. +- **Query Collection:** direct writes read and validate accepted rows, and + update the Query cache after acceptance. That cache read was the root cause + of #1130. A refetch commits without a signal and no longer rolls back + accepted ownership. An eager fetch that started before a direct write is + discarded. +- **Persistence:** stores a held source transaction at acceptance. Internal + applies inside the apply mutex wait only for acceptance. Electric reserves + its commit turn through `persistence.reserveCommitTurn()`. +- **Electric and TrailBase:** reject an aborted load after its accepted rows + apply. +- **Optimistic-history oracle:** the prose, model, grammar, and driver are + rewritten. The handler writes at every cut, with and without awaiting + acceptance. It observes the rows visible when `isPersisted` settles, allows a + single cut at settlement, keeps receipts pending while queued, and checks + `$origin` for held and later confirmations. + +| Run | Result | +| --- | --- | +| #1990 refetch, first durable write held, on `origin/main` | 3 of 3 isolated runs fail: `expected 'error' to be 'ready'` | +| #1990 handler `writeUpsert`, result write held, on `origin/main` | 4 of 5 full-file runs fail: `expected +0 to be 3` (the retained optimistic row) | +| Real-SQLite handler test ported from #2002, on `origin/main` (`await source.commit()`) | times out after 5000 ms | +| Same three on the branch | pass; Query Collection file passes 5 of 5 full-file runs | +| `awaits persisted server responses in update handlers`, branch | 20 of 20 full-file runs pass | +| Eager refetch older than a handler `writeUpsert`, before its fix | `expected 2 to be 3` | + +## Mutants + +Each mutant is a temporary production edit that is restored after its run. + +| Mutant | Owner | Result | +| --- | --- | --- | +| Retention kept: hold a completed row without a queued sync transaction | state-retention oracle | killed | +| Held sync applies before the drop (immediate bypass) | state-retention oracle | killed, 11 failures | +| `isPersisted` resolves before publication | state-retention oracle | killed, 40 failures | +| Handler-facing receipt waits for visibility | state-retention oracle | killed, 6 failures | +| Load settles at acceptance | load-subset oracle and state retention | killed, 10 and 4 failures | +| `$origin` marker persists until the next write | state-retention oracle | killed, 6 failures | +| Held confirmation marked `'remote'` | state-retention oracle | killed, 7 failures | +| Core abort cancels an accepted transaction | load-subset and refinement oracles | killed | +| Stale eager fetch applies | Query Collection | killed | +| Query cache reads applied rows | Query Collection | killed | +| TrailBase aborted load resolves | TrailBase | killed | +| Electric aborted load resolves | Electric | killed | +| Query Collection passes its signal to `commit()` again | Query Collection | survived; equivalent | +| Query Collection rolls back accepted ownership on supersession | ownership oracle | survived at first, then killed | + +Survivors: + +- **Signal passed to `commit()`:** this mutant is equivalent. Core now ignores + a post-commit abort, so the signal cannot cancel an accepted transaction. The + core-level mutant above covers this law. +- **Ownership rollback:** every supersession test held durable storage. In that + case the result's core transaction had already applied, so restoring the old + ownership did nothing. The ownership oracle now also holds the result behind + a persisting mutation and checks visible and synced rows after each refetch, + including a later empty refetch. The mutant fails there. + +### After the merge with the state-stack refactor + +Every mutant above, plus five more, ran again on the merged code. The merge +took #2004's smaller state stack and kept this branch's laws. + +| Mutant | Owner | Result | +| --- | --- | --- | +| Retention kept | state-retention oracle | killed, 5 failures | +| Held sync applies before the drop | state-retention oracle | killed, 12 failures | +| `isPersisted` resolves before publication | state-retention oracle | killed, 44 failures | +| Handler-facing receipt waits for visibility | state-retention oracle | killed, 5 failures | +| Load settles at acceptance | load-subset oracle and state retention | killed, 11 and 4 failures | +| `$origin` marker persists until the next write | state-retention oracle | killed, 11 failures | +| Held confirmation marked `'remote'` | state-retention oracle | killed, 9 failures | +| An open sync transaction counts for holds | state-retention oracle | killed, 5 failures | +| Older held row wins over the newest completed row | state-retention oracle | killed, 1 failure | +| Pre-sync capture counts an open transaction | state-retention oracle | killed, 1 failure | +| Replay invalidates a committed transaction instead of throwing | state-retention oracle | killed, 1 failure | +| DbClient chunk queues after open transactions | DbClient hydration oracle | killed, 2 failures | +| Core abort listener drops an accepted transaction | load-subset and refinement oracles | killed, 1 and 1 failures | +| Core abort listener calls the cancel path | load-subset and refinement oracles | survived; see below | +| Stale eager fetch applies | Query Collection | killed | +| Query cache reads applied rows | Query Collection | killed | +| Query Collection passes its signal to `commit()` again | Query Collection | killed, 1 failure | +| Query Collection rolls back accepted ownership on supersession | ownership oracle | killed | +| TrailBase aborted load resolves | TrailBase | killed, 5 failures | +| Electric aborted load resolves | Electric | killed, 1 failure | +| Reject a source delete for a never-held key | state-retention oracle | killed, 11 failures | +| Drop a source delete for a never-held key | state-retention oracle | survived; equivalent | + +Corrections and survivors: + +- **Signal passed to `commit()`:** the earlier run selected no test, because + its name filter no longer matched the supersession test. With the correct + filter, the mutant fails. It is not equivalent, and the earlier note about + it is withdrawn. +- **Dropped never-held delete:** the key is absent from the applied synced + rows, and the optimistic state drops at settlement either way. The + [absent-key review](2026-10-02-absent-key-source-delete.md) records the + rewritten pinned histories. +- **Core abort calls the cancel path:** `cancelPendingSyncedTransaction` now + throws `SyncQueueInvariantError` for a committed transaction. The guard + stops the cancellation before it changes state, so the accepted rows still + apply. The unguarded variant, which removes the accepted transaction + directly, fails in both oracles. + +## Limits + +- **On-demand self-load:** a handler that awaits an on-demand load of its own + Collection waits for itself. This history is documented and outside the + generated grammar. +- **Truncate:** a truncate still applies while a transaction persists, and the + still-persisting transactions overlay it. +- **`duplicate subset loads correctly`:** 0 of 10 full-file runs fail on + `origin/main`. On the branch it failed every time because its handler wrote + no confirmation, until the fixture was changed. +- **Eager refetch race:** the guard covers eager mode. On-demand mode keeps its + post-write authority path. + +## Verification + +At `3c85ac0de` with built `dist`: + +- **db:** 7920 tests, and 8224 with type checking; no type errors. +- **Query Collection:** 945 tests with type checking; no type errors. +- **Other packages:** Electric 629, persistence core 374, node SQLite 110, + PowerSync 176, offline 242, TrailBase 67, RxDB 12. React, Vue, Solid, Svelte, + and Angular pass. +- **Checks:** `test:oracles`, `check:mangle`, and `test-minified-db` pass. diff --git a/docs/contributing/oracle-reviews/2026-10-05-compound-joins.md b/docs/contributing/oracle-reviews/2026-10-05-compound-joins.md new file mode 100644 index 0000000000..02a78029d8 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-05-compound-joins.md @@ -0,0 +1,381 @@ +# Compound joins: fresh implementation and oracle evidence + +Reviewed implementation: `d64ba0aa66dca3651b41c1b4ac20edfaa1526bf4`. +Feature commit: `a3911f6eb97fcceb319272dba3c3a877222c6890`. +Main merged: `931e8346f5df09b68f436e7ed589a7f46e766104`. +Date: 2026-10-05. Local branch: `codex/compound-joins-861`; no push. + +The user requested a fresh implementation of PR #861 as a feature, with its +old implementation and examples removed first. The earlier +[assessment](pr-861-compound-join-relevance.md) applies to the discarded code. +The replacement extends the current equality, identity, and route contracts. +It stores the whole predicate in `JoinClause.on`, validates nonempty nested +AND-of-equalities, and uses graph-scoped component equality identities. The +first equality supplies candidate demand; every equality controls matching. +No new runtime state machine, fallback lifecycle, or dependency was added. + +Direct IR consumers must replace `left`/`right` with an `on` expression. Public +builder syntax for existing single equalities is unchanged. Existing IR tests +were migrated mechanically, preserving their laws and assertions. + +## Contract, grammar, and limits + +The live-query architecture's Identity and route-context laws supply value and +parent semantics. The feature request authorizes compound equality syntax; +`ARCHITECTURE.md` and the live-query guide now state that extension. + +- **Relational owner:** `cold-join-reconciliation-oracle.test.ts`. A label table + declares 23 representative values and their equality classes independently + of production normalization. Nested loops recompute exact ID-pair bags and + unmatched outer rows. Public rows, size, and an event-reconstructed replica + are compared after preload and every applied sync transaction. +- **Identity owner:** `identity-output-shape-oracle.test.ts`. Independent numeric + nested loops supply expected public rows for four predicate variants across + direct, from-QueryRef, and joined-QueryRef boundaries. Only after output is + checked do identity and compiler cache-equivalence assertions run. Reordering, + reversing, and duplicating terms preserve identity; changing the later field + changes identity and cannot reuse the subquery. +- **Transport owner:** `includes-context-transport-oracle.test.ts`. Independent + numeric source-pair recomputation checks a parent value used only in the + second equality. The finite grammar crosses both operand sides and direct + versus joined QueryRef sources. Collection, array, and materialized forms + are compared after preload, a parent update, and updates to each child side. + +The generated relational grammar has 0–4 distinct keys per source, keys 0–3, +23 value labels, a second component in `{0, 1, null}`, a third in `{0, 1}`, +2–3 terms, and 0–8 keyed put/remove steps. It varies term order, nesting, +independent operand direction, all four join types, scan/eager-index paths, +and eager/controlled-cold acquisition. Remove-absent is a driver no-op; writes +use insert/update according to installed keys. The nonselected revision field +makes reference-only replacements observable to source change detection. + +The larger fixed matrix separately covers all 23 values together, nulls in +both tuple positions, and leave/restore/delete/reinsert histories. Each local +matching distinction can be reconstructed in the generated domain by retaining +its relevant rows and relabeling IDs and ordinary numeric equality classes. +The larger matrix itself is outside the generated four-row bound. + +Each axis has a distinguishing purpose: labels reject structural/JSON equality; +nulls reject null matching; width rejects partial tuple matches; term order +prevents lazy candidate filtering from masking bad tuple equality; reversal +requires independent operand analysis; nesting exercises recursive admission; +join type preserves unmatched rows; updates exercise retractions and restored +matches; indexes and cold sources cross different acquisition paths. OR, +inequalities, empty AND, and an invalid nested term are explicit rejection +controls. Wider tuples and arbitrary expression trees are not established. + +The cold provider publishes its finite table on demand. This proves controlled +acquisition and result truth, not provider I/O, eviction/reentry, minimal demand, +or performance. Async/optimistic/paginated/deeper include cross-products and +framework consumer caches remain scoped follow-ups in the coverage map. + +## RED, calibration, and GREEN receipts + +The first oracle run occurred after restoring the old PR's production files +and example-test file to main. Its 17 feature cases failed at join admission. +On the reviewed revision, copying only the merged main's builder back produced +21 feature-case failures at the same admission boundary; four invalid-predicate +controls passed. This latter receipt is specifically a builder-boundary check, +not an all-main integration run. + +Temporary mutants were restored after each run. The final source remained +unchanged. These receipts distinguish assertion failures from pre-check errors: + +| Wrong design | Mutation | Observed result | +| --- | --- | --- | +| Only the first equality matters | Compiler condition list sliced to its first entry | 13 failures at exact public-pair assertions; includes the generated property | +| Raw JSON defines tuple equality | Normalized component array replaced by `JSON.stringify(values)` | Distinct objects, opposite infinities, and Date/ISO-string pairs fail public-pair assertions; BigInt separately fails during evaluation before publication | +| Predicate does not affect identity | Canonical join predicate replaced with constant TRUE | All three identity cells fail the unequal-identity assertion after independently checking public rows | +| Predicate does not affect cache equivalence | `normalizeQuery` omits `on` | All three cells fail the cache-equivalence assertion | +| Only first joined operand discovers parent routing | Parent-use scan restricted to first condition | Direct-source/joined-side cell fails public forms immediately after preload; the other three cells pass | +| Only first equality contributes parent references | Both builder walkers visit only first AND term | All four transport cells fail public forms immediately after preload | + +The initial raw-JSON mutation name filter selected no tests. It was classified +as unreached, not a kill. Inspection identified the candidate-filter masking +risk, so term-order variation and explicit safe-first-term witnesses were added +before the corrected run. +An initial omitted-identity mutation passed `undefined` to the canonicalizer; +that was a pre-check error, not a semantic kill. The constant-predicate mutant +above reaches and fails the intended identity assertion. + +The property runs 60 histories with fixed seed `861593`, then the identical +property and budget without a seed. Direct replay selects only the requested +lane. The drop-equality mutant shrank to seed `861593`, path +`1:1:2:2:3:3:3:3:3:3:3:3`. The guarded command below executed exactly one case, +failed at the public-pair checkpoint with that mutant, then passed after restore: + +```sh +cd packages/db +TANSTACK_DB_ORACLE_PROPERTY=cold-join.compound \ +TANSTACK_DB_ORACLE_SEED=861593 \ +TANSTACK_DB_ORACLE_PATH=1:1:2:2:3:3:3:3:3:3:3:3 \ +node --import tsx tests/oracle-replay.ts \ + tests/query/cold-join-reconciliation-oracle.test.ts \ + --coverage.enabled=false --typecheck.enabled=false --maxWorkers=1 +``` + +Final regression: **858 tests passed in 20 files**. This includes all compiler +suites, join/builder/optimizer suites, the three changed oracle owners, stable +identity, join-result-key, includes-query-shape, and includes-cross-formulation. +DB build and declaration emit passed, as did the DB TypeScript check. Changed +TypeScript lint passed with six pre-existing async-without-await warnings. +`git diff --check` passed. No full monorepo or framework campaign is claimed. + +Frozen installation was blocked by registry errors (configured proxy 403, +public registry 503). Verification used the main checkout's installed external +dependencies and this worktree's built DB/IVM packages. This is not a clean +frozen-install CI receipt. Missing Expo example dependencies emit warnings. + +## Oracle-guide audit + +| Requirement | Outcome and evidence | +| --- | --- | +| ORC-001 | Contract and limits are stated in each executable owner and above. | +| ORC-002 | Label equality and numeric nested loops do not call production semantic helpers to compute expected rows. Identity helpers are observations under test. | +| ORC-003 | Existing literate owners now contain adjacent compound law, model, grammar, driver, and checkpoint prose/code. | +| ORC-004 | Reconstruction, per-axis contribution, bounds, and invalid neighboring forms are recorded above. | +| ORC-005 | Public query construction, preload, sync commits, exact row bags, events, and all materialization forms execute; failures identify reached checkpoints. | +| ORC-006 | Six wrong designs are rejected at the relevant comparisons; pre-check and unreached attempts are separately classified. | +| ORC-007 | Identical fixed/random grammar and 60-run budget; property registry, manifest filter, and guarded red/green seed-and-path replay. No commands arbitrary is used. | +| ORC-008 | Model state is only current keyed source rows. Key, equality-class label, and component values distinguish future matching or removal; no production indexes/caches/transitions are duplicated. | +| ORC-009 | Value labels abstract equality classes; installed-key bookkeeping and revision belong to the driver. The replica folds public events, not internal D2 state. | +| ORC-010 | Existing cleanup helpers preserve primary failures and secondary diagnostics. Construction is inside cleanup ownership; identity drivers register each created live query immediately. | +| ORC-011 | No additional shared-model semantic fault was identified requiring a second formulation. Independent label/numeric models plus mutations distinguish the reported wrong designs. Existing single-equality predicate comparisons remain intact. | +| ORC-012 | This versioned record identifies the exact reviewed implementation, evidence, and remaining owners. | +| ORC-013 | True matches and same-prefix/later-component mismatches, real nulls, missing outer peers, and same-correlation/different-parent-parameter cases distinguish nearby wrong rules. | +| ORC-014 | Controlled provider boundary is explicit; real-provider acquisition is not claimed and is assigned in the coverage map. | + +## Code weight + +Compared with merged main, production TypeScript changes are **+73 / −67, +net +6 lines in eight files**, including comments and blank lines. Tests are +**+938 / −133, net +805 lines in eleven files**, including mechanical migration +of direct IR fixtures. Before this evidence record, docs and the changeset are ++196 / −3 lines; the architecture document is counted as documentation, not +production code. These are source-line counts, not bundle-byte measurements. + + +## Review follow-up: acquisition premise and test maintenance + +Review started at `cb1262754aecc6ebc8de975f0b2b0581fd29a9ad`. The fixes are +in `dc78e6947441a51e2317f6fb9bd99232bdcc0ced`. This section is a later receipt; +the implementation, counts, and environment limits above retain their original +revision scope. This review changed tests and documentation only. Production +weight remains net +6 source lines against merged main. + +The compound oracle incorrectly required acquisition when the atom component +was nonnull but the other component was null. A null first demand key can +correctly produce an unmatched LEFT row without acquiring the right source. +The violated test law was conditional acquisition: a positive witness requires +a satisfiable tuple. Result truth does not require minimal candidate demand. +The original generator already reached this overlap; the assertion premise was +wrong. The fixed seed had missed it, and an independently generated history +exposed it. + +The exact failing history had left row `{id: 0, atom: 0, b: null, c: 0}`, empty +right rows and steps, LEFT join, width two, `atomFirst: false`, no nesting or +reversal, cold source, and indexing off. Public rows, size, and the event +replica were correct before the acquisition assertion failed. + +This guarded replay ran exactly one case and failed before the fix, then +passed with the corrected premise: + +```sh +cd packages/db +TANSTACK_DB_ORACLE_PROPERTY=cold-join.compound \ +TANSTACK_DB_ORACLE_SEED=3 \ +TANSTACK_DB_ORACLE_PATH=39:1:1:2:2:2:3 \ +node --import tsx tests/oracle-replay.ts \ + tests/query/cold-join-reconciliation-oracle.test.ts \ + --coverage.enabled=false --typecheck.enabled=false --maxWorkers=1 +``` + +The oracle now pins 16 premise cells: scan/eager indexes, both term orders, +nonnull/null atoms, and nonnull/null second components. Before the fix, the +null-first cells failed in both index modes; the other 14 passed. All 16 pass +after requiring both components to be nonnull for positive acquisition. +No zero-acquisition assertion was added for unsatisfiable tuples. + +Two calibration tests hide acquisition on a satisfiable tuple whose empty +right source produces the same unmatched output. Both reject the hidden +observation. A temporary broad waiver of the positive acquisition assertion +made both calibration tests fail because their checks incorrectly resolved. +Restoring the assertion made both green. Thus the fix does not waive required +acquisition merely because row output happens to be correct. + +Maintenance changes reuse the existing `createEq` helper in 41 optimizer +fixtures, attach expected semantics to named identity variants, replace two +nested grammar ternaries with explicit branches, format the lazy-demand +fixture, and correct the contradictory coverage-map sentence. No contract was +changed to fit a production result. Both identity and cache-equivalence source +mutants still fail all three compound boundary cells at their intended +assertions; after restoration, those three cells pass. + +Validation at the fix revision: **876 tests passed in 20 files**, DB TypeScript +passed, changed-test ESLint passed with two existing async-without-await +warnings, formatting passed, and `git diff --check` passed. One intermediate +type check rejected passing the now-two-parameter history driver directly to +fast-check; a one-argument callback fixed the inference without changing the +grammar. The original build receipt remains valid for unchanged production +sources; no new clean-install or full-monorepo receipt is claimed. + +All 26 review items were accounted for: six fixed, eight refuted within their +stated paths, three deferred, and nine duplicates. The residual measurement +and evidence questions have durable destinations in the Compound joins section +of the [coverage map](../oracle-coverage.md): candidate-demand minimality, +single-equality allocation cost, and a hypothetical throwing-unsubscribe path. +No measured performance regression or reachable cleanup failure was supplied. +These limits do not establish untested provider or lifecycle cross-products. + + +## External review follow-up: null demand and filtered acquisition + +Reviewed head: `ee490df80d200f701df805faf81d8105ba492527`. +Fix revision: `de61f1dd32737da86a3d1d9503b3134e078b06a9`. +Comparison base: `84dc899bfc8d6a16487c1584dbb1cb177109aacc`. +These later receipts supersede the earlier deferral of null-tuple candidate +demand and operand-array cost. Historical counts above retain their scope. + +A LEFT join over `{sku: 1, region: null}` and `{sku: 2, region: null}` requested +`sku IN (1)`. Deleting the first row left that request active. Public unmatched +rows were correct, but neither tuple justified keyed acquisition. The shared +unmatched key kept the first raw demand representative. Both pipelines now +suppress raw demand whenever their tuple is unsatisfiable. + +The compiler demand oracle models a keyed table and recomputes the first-field +set from nonnull tuples after each graph turn. Its eight histories cross +LEFT/RIGHT, two/three components, both orders, shared primary keys, null and +undefined, and put/remove/restore. Before repair six cells failed exact demand +comparison and two controls passed. All eight now pass. The public adapter +probe now observes no requests before or after deletion while unmatched rows +remain correct. Other acquisition-minimality and asynchronous provider claims +remain outside this finite compiler boundary. + +The cold provider now interprets direct-field IN requests using independent +atom equality labels. Four cold-load witnesses use distinct component values +1, 7, and 9 across both term orders and index modes. A source mutant that uses +the last field with the first field's values passes all four with the former +whole-table provider. With the filtered provider, all four fail at public-pair +comparison: an unmatched row appears instead of the matching pair. Restoring +production returns GREEN. An earlier broad mutation run already failed a +non-cold generated history under the original fixture; the review's blanket +claim that wrong demand could never be detected was too broad. The missing +regime was this controlled cold adapter boundary. + +The per-row extractor now evaluates the first operand directly and constructs +one normalized tuple only for multiple equalities. Instrumentation at the +operand-array creation sites, restored after each run, measured these counts +with 10,000 input rows on each side and 10,000 matching pairs: + +| Representation | Single equality | Two equalities | +| --- | ---: | ---: | +| Reviewed head | 20,000 | 40,000 | +| Fix | 0 | 20,000 | +| Always construct a tuple alternative | 20,000 | 20,000 | + +These counts exclude existing stream/output arrays. They measure allocations, +not elapsed runtime or retained heap. The always-tuple alternative passed 258 +relevant tests, but restores the single-equality cost. It remains unselected. +No new production state, dependency, or fallback was added. + +At the fix revision, temporary source mutants produced these outcomes: + +- Restoring raw demand for unmatched keys: six demand assertion failures. +- Dropping later equalities: thirteen relational assertion failures. +- Raw JSON tuple equality: three public-pair assertion failures, plus a BigInt + evaluation error before publication. The latter is not an assertion kill. + +The observation-level hide-acquisition control remains useful to calibrate the +positive acquisition assertion. ORC-006 and ORC-013 explicitly do not require +permanent source mutants. The source mutation receipts above complement that +control without adding mutation infrastructure to ordinary CI. + +The other review outcomes are bounded: + +- Field-to-literal equality passes builder syntax checks and fails compilation + with `InvalidJoinConditionSourceMismatchError` on both main and the reviewed + branch. This is a source-binding error, not a newly accepted valid join. + Architecture admission specifically rejects OR and non-equality predicates; + it does not promise builder-time source binding. The guide and agent skill + now state where literal filters belong and when source binding fails. +- First-equality planning is intentional. Probes distinguish a full-source + request for computed-first, SKU IN for plain-first, and region IN for + region-first. The guide and agent skill now explain that work difference. +- Both builder and compiler use the shared syntax validator. The compiler also + accepts direct IR and binds source aliases. Removing a validation boundary + would need a separate representation design and measured compile-work benefit. +- The old assessment already identified itself as superseded. It is now a short + historical pointer that preserves access to its exact old diagnostics. + +Final validation: **888 tests passed in 20 files** after restoring every source +variant. DB build/declaration output, DB TypeScript, changed-file ESLint (two +existing warnings), formatting, and whitespace checks passed. An initial +wrong-directory test invocation failed before execution and is not RED evidence. +The first literal probe used the wrong live-query overload; the corrected +`{query}` probe reaches the compiler and establishes the stated behavior. + +Production weight is now **+84 / -74, net +10 source lines** in eight TypeScript +files against the comparison base. Tests are +1,132 / -127, net +1,005 lines. +Documentation and executable contract prose are separate from production. +The previous +281-byte gzip receipt belongs to `ee490df80`; a later CI size +check must measure this revision. Current provider, temporal, cleanup, and +performance limits remain owned in the coverage map. + + +## Second high-effort review: planning and cleanup + +Reviewed head: `5bb21151917911510683d6819c8e659e295bac3f`. This follow-up +preserves matching, loading-plan selection, and semantic identity. It simplifies +the existing keyed-input construction and corrects documentation. + +Eight numbered findings contain nine distinct claims because item 2 combines +reordered predicates and unary conjunctions. The lossless evaluation retains +both. Six claims receive fixes here; three planning/normalization proposals +remain deferred with owners in the coverage map. + +| Item | Verified behavior and action | +| --- | --- | +| 1 | A computed joined operand first loads the whole source. The plain-field control uses keyed demand. The architecture permits this plan; first-eligible selection remains an optimization proposal. | +| 2a | Real React rerender keeps the same Collection and region-first demand after reordering to SKU-first. Semantic identity is unchanged. The guide and agent skill now distinguish initial compilation from reuse. Loading-order-sensitive identity requires a separate design decision. | +| 2b | Public unary `and(eq(...))` rejects because `and` requires two operands. Direct unary AND IR and valid duplicate equality terms produce the same rows as bare EQ but distinct identities and a replacement hook Collection. Join-specific normalization remains deferred. Global Boolean-wrapper removal could change coercion for nonboolean operands. | +| 3 | Repeated no-match sentinel checks could diverge. The keyed-input helper now owns sentinel selection and raw demand together. Its comment explains why serialized values cannot collide with the raw NUL prefix. | +| 4 | The helper now evaluates the first expression itself and returns the keyed-stream tuple already needed by `map`. It adds no separate per-row result object. | +| 5 | First-field demand overfetch is real and permitted. Exact compound acquisition needs tuple-preserving demand, not independent IN sets that admit cross-pairs. The coverage map names the receiving owners. | +| 6 | `validateJoinConditions` now states the builder call's purpose. The same helper returns source-order operands to the compiler, which still validates direct IR. | +| 7 | Regenerated the stale `JoinClause` reference with the repository's TypeDoc configuration. Only that page was retained, with correct `on: BasicExpression` and source links. | +| 8 | The historical assessment already exists at the pinned upstream commit. Its link now uses `TanStack/db`, removing dependence on the contributor fork. The upstream API returned blob `cfa6bf850e2ce021c19992b5994df417056a2383`, 5,884 bytes. | + +### Executable receipts and limits + +The controlled provider has one product `(region=1, sku=7, code='a')` and stock +rows `(1,7,'A')`, `(1,9,'B')`, `(2,7,'C')`. It applies the received direct-field +IN predicate before writing rows. A computed `lower(stock.code)` condition first +acquires three rows; SKU-first acquires two; both publish one exact matching +pair. Region-first also acquires two and publishes one pair. These are work and +result observations at initial preload, not a claim of minimal acquisition. + +The React probe renders the region-first query and rerenders SKU-first with the +same source Collections and no explicit query key. Both prepared identities +match, the Collection reference is unchanged, and the provider receives only +`region IN (1)`. Separate fresh-query controls receive the SKU request. +For direct unary AND IR and duplicate EQ, the identity differs from bare EQ, +the Collection reference changes, and the provider receives a second SKU +request. Both old and replacement Collections are cleaned up explicitly. +Five focused probes pass on exact reviewed production and on the cleanup. +They are evidence for these finite React paths, not every framework cache. + +The existing 888 tests in 20 files pass, including all compiler tests, cold +joins, null-demand histories, identity, optimizer, and includes contracts. +DB type checking, build and declaration output pass. Regeneration succeeds +with existing TypeDoc tag warnings. The temporary operand-array counter still +reports zero arrays for one equality and 20,000 for two equalities at 10,000 +rows per side, with 10,000 output matches in each case. This measures operand +arrays only, not elapsed runtime or all allocations. + +No new product failure was claimed or found in this round, so maintenance and +documentation findings do not receive invented product RED assertions. The +previous null-demand and filtered-provider RED/GREEN receipts remain applicable. +Initial probe setup failures (missing dependency links and an unavailable +public import) are excluded from behavioral evidence. Missing Expo tsconfig +warnings are unrelated to the successful React tests. No probe or production +instrumentation remains in the committed implementation. diff --git a/docs/contributing/oracle-reviews/2026-10-05-indexeddb-law-audit.md b/docs/contributing/oracle-reviews/2026-10-05-indexeddb-law-audit.md new file mode 100644 index 0000000000..9cd2dce504 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-05-indexeddb-law-audit.md @@ -0,0 +1,162 @@ +# IndexedDB law enforcement follow-up + +> Historical audit: the [October 6 refinement audit](2026-10-06-indexeddb-tla-refinement.md) records the subsequent design decision, completed repair and executable evidence. The open dispositions below describe the earlier snapshot. + +This audit starts at PR #1179 head +`34eb98b16d6174d1722878de3cb3b2cd5dc4dabe`. It revisits all 15 findings from +`2026-10-05-indexeddb-xhigh.md` under the stronger evaluate-review standard: +identify the reusable law, inspect its model and reach, and prove its assertions +reject plausible wrong implementations. Changes described here are in the +working tree; they are not yet committed or pushed. The deletion law remains +**open**, with a concrete failing history and a design decision requested. + +## Result + +The previous evaluation overstated closure. Four wrong implementations passed +its relevant green suites. The strengthened oracles reject them: + +| Wrong implementation | Previous evidence | Strengthened evidence | +| --- | --- | --- | +| Match Collection IDs instead of Collection references | 21 compatibility/persistence tests passed | Four same-ID owner-projection assertions fail; distinct-ID controls pass | +| Ignore a current durable row without a version record | 38 transport tests passed | Four current-row comparisons fail; versioned controls pass; the native witness fails in Chromium, Firefox and WebKit | +| Read storage for malformed envelopes, then ignore them | 38 transport tests passed | The zero-storage-work assertion fails; valid neighboring invalidation still reads | +| Replace a native cause with an equal-looking DOMException | 20 wrapper tests passed | Exact request-cause identity assertion fails | + +These were missing distinctions, not missing random runs. Production already +satisfies the strengthened four laws. No production workaround was added for +these tests. + +The audit also found a reachable counterexample to the repaired deletion design. +A connection that observed native deletion is insufficient evidence that a +received success notification belongs to that deletion. XH-07 is reopened. + +## Laws and enforcement, in original finding order + +Paths below refer to `packages/indexeddb-db-collection/tests` unless qualified. +The original findings remain separate even when they share a law. + +| ID | Law or obligation | Model, histories, production path and enforced observation | Disposition | +| --- | --- | --- | --- | +| XH-01 | Successful automatic writes in one Collection persist in author order; rejected handlers or failed native batches contribute no durable effect and release successors. Authority: approved ordering policy and native atomicity. | `local-write-order-oracle.test.ts` folds authored whole-row effects over the independently authored peer base. 576 histories cross three same/disjoint/reinsert effects, all six handler orders, eight decision masks and four ordered peer prefixes. At every decision it checks the decided author prefix in raw storage, the peer and fresh restore, plus absence of premature caller success. Four more cases cover held predecessors, handler reentry and queued successors after native failure. A completion-order mutant causes 287 assertion failures. Intermediate local optimistic publications are a separate law below. | fixed-now within these stated receiving boundaries | +| XH-02 | Mutation ownership is Collection-reference identity, independent of user IDs and cleanup of its sync run. | Compatibility checks renamed IDs and adds equal/distinct IDs, both acceptance orders, live/cleaned-up sync runs and mixed manual payloads. Every acceptance checks both stores, version ownership and restore. Keys are disjoint so core same-ID/same-key mutation merging cannot replace the tested adapter boundary. The ID-equality mutant changes both stores and fails. | fixed-now | +| XH-03 | Optional row version metadata cannot define source membership. A notification reads current durable whole-row values; duplicates cannot introduce duplicate rows or restore omitted fields. | Transport crosses initial membership, key type and metadata present/absent at the receiving read, independently of existing startup-metadata cases. Raw writes occur before delayed real peer notifications; duplicate delivery and a later versioned suffix check cache continuity. A native two-page receiving witness uses real writes, reads and duplicate BroadcastChannel messages. Low-level writers explicitly send invalidation; wrapper writes do not automatically broadcast. The missing-version mutant fails four controlled comparisons and all three engines. | fixed-now for delivered invalidations | +| XH-04 | Import accepts schema input, validates/transforms once, uses transformed keys, and rejects the entire replacement before storage if any input or key is invalid. Export returns durable schema output. | The Date example retains the input/output distinction. Settlement now crosses schema errors and transformed-key collisions with all three positions in a batch; it compares unchanged public/raw/peer rows, exact prior versions and zero storage work. An accepted suffix must produce independently authored numeric keys and uppercase values, including fresh restore. Source and published-consumer types distinguish input from output. A universal inverse transform is not promised. | fixed-now; separate trusted-output restore remains an unrequested API design | +| XH-05 | Both the requested store and the metadata store must exist before options are accepted. | Synchronous configuration examples cover missing data/metadata stores and healthy creation. The native pre-existing same-version schema reaches the missing-metadata premise. Original-source calibration failed the intended admission assertion. This finite configuration predicate needs no history state machine. | fixed-now | +| XH-06 | Managed connections close on versionchange; existing native transactions may finish, later work through the retired connection fails, and the app recreates affected Collections. | Transport checks exact native recipients, pending unmanaged blockers, preserved upgrade rows, failed old-descriptor writes, downstream error after an old read, and fresh restore/write. Native tests receive versionchange while native work is held. The policy is explicitly approved and documented; no new automatic restart or in-memory fallback is inferred. | accepted-design, not deferred implementation | +| XH-07 | Successful deletion may empty only Collections belonging to the deleted database lifetime, and only after that deletion completes. A different lifetime's notification cannot supply completion. | The existing connection-scoped model distinguishes old and recreated connections only until the recreated connection sees another deletion. The new controlled/native histories retain an old successful receipt, recreate and populate the database, start another deletion behind an unmanaged blocker, then deliver the old receipt. Durable rows remain; the recreated peer incorrectly becomes empty. The comparison reaches the intended publication cut. | design-decision; confirmed behavioral defect remains open | +| XH-08 | Invalid protocol envelopes carry no authority to access storage or publish changes; valid neighboring messages remain effective. | The existing malformed-value matrix now counts native transaction admission before any row observation can hide unnecessary work. It requires zero transactions for malformed inputs and one for a valid unseen-row invalidation. The eager-read mutant previously passed and now fails. Self/database/store routing has separate positive witnesses. | fixed-now | +| XH-09 | Contextual wrapper errors retain the original native cause object; callback rejection keeps its identity. | Wrapper checks native request/open failures and separate open/delete/store/transaction admission branches. Assertions now use identity rather than structural equality for native causes. A cloned DOMException survives the previous suite but fails the strengthened request comparison. Existing callback/native-completion matrices retain exact callback failure outcomes. | fixed-now | +| XH-10 | Exported error APIs must describe actual behavior; the approved API uses native causes instead of unused constructors. | The five unused constructors and reference pages were removed in the prior repair. Source/export inventory and declaration checks are the appropriate evidence; no product state machine is implied by removal of unused symbols. | fixed-now | +| XH-11 | Runtime/type oracle results depend on workspace source, not pre-existing distribution files. | Dedicated runtime/type source configuration and coverage-disabled oracle scripts remain unchanged. The prior full suite ran with all three dist directories absent; current source runtime and type checks pass. Declaration-consuming tests are explicitly separate. This is a test-integrity contract, not a row model. | fixed-now | +| XH-12 | Default runtime tests must not rebuild shared dependencies; published consumers must still receive completed builds. | Default scripts have no builds, root package checks follow runtime suites, and CI reuses completed builds after its runtime group. The published consumer lane builds ESM/CJS and compiles five module-resolution configurations without skipLibCheck. Duplicate builds were verified; an actual historical CI race was not claimed. | fixed-now | +| XH-13 | A native batch admits its data/version requests before awaiting their success, but only native transaction completion can establish atomic success. | Settlement counters now cover automatic insert/update/delete and import, with empty import and one/ten-row batches. Counts are exactly 2N before the first successful request. A serial-delete mutant fails both delete cases. Existing first/middle/last clone-failure and abort matrices retain value atomicity. No elapsed-speedup claim follows. | fixed-now | +| XH-14 | Wall-clock metadata has no row/version-order authority; removing it must preserve legacy reads and unrelated records. Utility types retain schema input/output and Collection key types without an unused parameter. | Persistence crosses absent/past/future legacy timestamps, update, untouched anchors, mixed-format restore and replacement. New records have only a version token. Three format assertions failed before removing updatedAt; the two-parameter utils type failed before removing the phantom key parameter. Updated source and published consumers pass. | fixed-now in this working tree | +| XH-15 | estimatedSize, when available, is the origin's approximate usage, not this database's size. | The empty-database controlled estimate and corrected property/guide/README scope remain intact. This diagnostic forwarding contract does not require a stateful oracle or invent a database-size estimate. | fixed-now | + +## The deletion counterexample and decision + +A database lifetime starts when the named native database is created and ends +when it is deleted. A connection can end earlier, for example during an upgrade. +Those are distinct identities. The new model cannot collapse a retired connection +from the first lifetime with a connection observing deletion in a second lifetime: +the same incoming old notification must have different authority. + +1. Native deletion of lifetime A completes; its success callback is held. +2. Another context creates lifetime B and persists a row. +3. Deletion of B begins. Its peers observe versionchange and close, but an + unmanaged connection keeps native deletion incomplete. +4. A's held callback broadcasts its old success notification. +5. B's peer becomes empty although B's durable row still exists and B's deletion + is still pending. + +The controlled oracle fails at `old receipt cannot complete newer deletion`. +The native oracle checks real versionchange, a live unmanaged connection, retained +durable rows and pending native completion before judging the peer snapshot. +A first Firefox fixture waited for a blocked event that did not arrive at that +cut. That timeout is not credited as a product failure; blocked-event timing is +not the authority being tested. The corrected native witness fails at the +intended peer-snapshot assertion in Chromium, Firefox and WebKit; all three +retain the durable row while the newer deletion is pending. + +The recommended correction is a persisted identity for each database lifetime, +carried by deletion notifications and retained by each descriptor. Native +connection retirement would remain a necessary condition. An old lifetime's +success could not authorize clearing a newer lifetime, even after that newer +connection sees another deletion. This changes the previously approved design +and stored metadata. The maintainer requested research before deciding. An +alternative is to stop clearing peer snapshots on deletion and require explicit +recreation; that changes the existing publication contract. No deferral is claimed. + +The research found stored dataset identity in RxDB and PouchDB, and separate +connection retirement/deletion APIs in Dexie and idb. It also identified a limit +on the proposed correction: a native delete is queued by database name, so a +captured descriptor identity does not prove which lifetime the request removes. +A native-only delete/recreate/delete probe reproduced removal of the recreated +dataset at the same version in Chromium, Firefox and WebKit, with both delete +requests admitted while the original connection still existed. This is platform +research, not a newly claimed adapter RED. A concrete design must distinguish +notification authority from native request targeting; merely adding an identity +field does not prove both. No deletion implementation has changed. The research +record and native traces remain in task review evidence. + +## Validation and limits + +Before adding the newly discovered deletion counterexample, the complete suite +passed **783 runtime/type tests**, **12 published-consumer tests**, and **57 +browser cases**. These numbers are not a current all-green claim: the deletion +counterexample now makes the relevant suite red. The latest complete +runtime/type run has **783 passing tests and one failing deletion assertion**, +with no type errors. The corrected native deletion witness has **three +assertion failures**, one in each engine. TypeScript, lint and whitespace +checks pass for the work through that point. The local build still uses cached +Rollup 4.59.0 because tracked 4.64.0 is unavailable from the configured registry; +the lockfile is unchanged. + +The four formerly surviving mutants now fail assertions. The additional +completion-order mutant fails 287 assertions, and serial delete admission fails +two. These are counted separately from the original review's earlier RED evidence. +The new cases are bounded enumeration, not claims of generated grammar coverage; +existing fixed/fresh campaigns and replay interfaces are unchanged. + +The local-order owner observes intermediate durable/peer/restore prefixes and +final local rows. It does not replace the core optimistic-publication model. +The pending companion still receives only one local intent's intermediate +publications. Multi-intent intermediate local publications, peer work between +local acceptances, pending mutation outcome across cleanup/restart, unordered +same-key cross-Collection writes, lost notifications, and physical crash durability +retain their named coverage-map boundaries. None is proved by adjacent green +checks. The deletion counterexample is inside the claimed deletion boundary and +therefore prevents closure of XH-07. + +## Oracle guide audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Approved author ordering, Collection-reference ownership, atomic native completion, schema-input and deletion-publication laws are stated; the new deletion design is not silently adopted. | +| ORC-002 | Authored effects/store projections determine expected rows; native reads and native error objects are independent observations. No production cache computes truth. | +| ORC-003 | Prose beside each extended owner explains law, model, finite histories, production path and cut. | +| ORC-004 | No new generated property; new matrices are explicitly bounded. Existing generated owners and their controls are unchanged. | +| ORC-005 | Actual Collection operations, native request admission, raw storage, caller outcomes, peer snapshots and fresh restores reach the asserted cuts. | +| ORC-006 | Four previous survivors now fail comparisons; completion-order and serial-delete mutants fail their intended cuts. The new deletion counterexample fails on current production. | +| ORC-007 | New matrices are finite enumeration. Existing fixed/fresh/replay campaigns remain active; no random-run claim substitutes for the matrices. | +| ORC-008 | Decided author prefixes and Collection references retain distinctions exposed by later actions. The deletion counterexample proves connection retirement loses a database-lifetime distinction. | +| ORC-009 | Collection, sync run, settlement and publication remain distinct. Database lifetime versus connection lifetime is defined above. | +| ORC-010 | Held local decisions release in finally; native/controlled deletion blockers release before comparing captured evidence. Primary failures survive cleanup. | +| ORC-011 | Raw storage and fresh restore challenge optimistic-only agreement; exact native cause identity distinguishes structurally equal replacements. | +| ORC-012 | All 15 original findings and their law evidence are accounted for. This working-tree record explicitly reports the open defect and does not claim closure. | +| ORC-013 | Equal/distinct IDs, absent/present metadata, invalid/valid messages, pending/completed prefixes, and old/new deletion lifetimes distinguish reusable boundaries. | +| ORC-014 | Missing metadata receives a native witness in all three browsers. The corrected two-deletion receiving witness fails its intended snapshot assertion in all three engines; the initial Firefox setup timeout is not credited as a kill. | + +## Loss audit + +The disposition accounting is **13 fixed-now + 1 accepted-design + 1 +design-decision = 15**. XH-07 is confirmed and unresolved. There are no deferred +items. This accounting does not mean the implementation work is finished. + +## Subsequent TLA+ refinement + +The [TLA+ translation and loss audit](2026-10-06-indexeddb-tla-refinement.md) +supersedes the open deletion-design disposition above. It records the approved +administrative deletion contract, oracle-first RED, source repairs, loss recovery, +production fault calibration and native receiving evidence. The historical +counterexamples above are retained; no deferral is used to claim closure. diff --git a/docs/contributing/oracle-reviews/2026-10-05-indexeddb-xhigh.md b/docs/contributing/oracle-reviews/2026-10-05-indexeddb-xhigh.md new file mode 100644 index 0000000000..055d4162e6 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-05-indexeddb-xhigh.md @@ -0,0 +1,165 @@ +# IndexedDB xhigh review evaluation + +This record evaluates the 15 code-reading findings supplied for PR #1179 at +`6ad2edc429720601ecc9b4c1b19bc2bc27b0dabd`. The reviewer reported no test +execution. Evaluation began at `eb4b8751f09b6b3c1dce0708cd0953e3e13bb37d`, whose +only additional change was the minification cache. The repaired implementation +and executable evidence are committed at +`b6f763ffe3b6319218e565d682d461cc030cbf7f`. This document adds no runtime change. + +## Reviewer assessment + +**Hire for code review.** The review found consequential persistence defects +beside a substantial green suite: id overrides, missing version metadata, local +write ordering, and deletion/recreation. Its coverage across runtime, test +integrity and API diagnostics was useful. The top findings deserved priority. + +Its evidence quality was incomplete: no execution was reported, and several +claims combine an observation with an unstated product policy. Old-connection +failure is a consequence of the explicitly approved recreation policy. A stale +deletion clears public rows but does not delete the recreated durable data. +The unused error classes were already documented as unused constructors. +Duplicate builds are real, but an intermittent CI failure caused by their overlap +was not reproduced. Sequential request admission is measurable; an elapsed-time +speedup was not measured. These qualifications affect severity and scope, not +whether the review's surviving ideas should be retained. + +## Finding ledger + +Paths in this table are relative to `packages/indexeddb-db-collection`, except +where stated. IDs preserve the source order; no findings were merged. + +| ID | Original claim and suggested action | Evidence and technical verdict | PR action | Durable value and destination | +| --- | --- | --- | --- | --- | +| XH-01 | `src/indexeddb.ts:739`: slow update A overwrites later fast B; both report persisted. | Confirmed at public and raw durable cuts: B then A, both completed. Mutation ordering required a decision; the maintainer approved author order within one Collection. | **fixed-now**, P1. Handlers run concurrently; successful persistence reserves and follows invocation order. Rejections release the next position. | `tests/local-write-order-oracle.test.ts`: 146 cases, completion permutations, rejection masks, repeated/disjoint keys, delete/reinsert, held predecessor, synchronous handler reentry. Native held insert/update witness in all three engines. | +| XH-02 | `:722`: overriding `id` after options construction silently saves nothing; use Collection reference. | Confirmed: `isPersisted` fulfilled with an empty store. The options-captured id differed from the real Collection id. | **fixed-now**, P1. Match the receiving Collection reference, retained across cleanup. | Compatibility owner: options/Collection id placement crossed with automatic/manual/after-cleanup acceptance; raw storage and fresh restore. | +| XH-03 | `:678`: restored rows without `_versions` cause duplicate inserts on peer update and ignore peer delete. | Both confirmed. Update entered Collection error; deletion left the restored row visible. | **fixed-now**, P1. Version metadata no longer decides membership. Reconcile durable values as full updates and missing values as deletes. | Transport owner: none/mixed/all initial metadata, numeric/string keys, update/delete, duplicate delivery and untouched anchor. | +| XH-04 | `:791`: export/restore fails for a transforming schema; output and input types differ. | Confirmed string-to-Date output rejection. Import's existing contract is schema input, and arbitrary transforms have no inverse. | **fixed-now** documentation, P2. Qualify direct round trips and explain explicit conversion. No trusted-output API was added. | API test checks output rejection without changing storage, then explicit inverse conversion and equal export. A separate output-restore API remains a maintainer design option. | +| XH-05 | `:440`: missing `_versions` passes option validation; fail early with `ObjectStoreNotFoundError`. | Confirmed using a pre-existing native store reopened at the same version. No upgrade callback creates metadata. | **fixed-now**, P2. Validate both required stores before constructing options. | API test distinguishes existing native schema from factory-created schema. | +| XH-06 | `:652`: a peer upgrade closes the connection; later notification errors old Collections/live queries. | Same-path probe confirms old source and dependent query error while a fresh descriptor stays usable. This follows the previously approved automatic-close/recreate policy. | **deferred** alternative lifecycle design. Retain the approved behavior; document the background-read consequence. | Transport connection owner and coverage map: a different retirement/status/restart API needs an explicit contract and old/fresh/downstream witnesses. | +| XH-07 | `:638`: stale name-only deletion message wipes a recreated database; add incarnation protection. | Partly confirmed: old deletion success callback held across recreation clears fresh public rows; raw durable rows survive. | **fixed-now**, P1. Maintainer approved connection-scoped deletion authority from native deletion or local initiation. No persisted epoch was introduced. | Transport owner and native two-page tests: native deletion completes, fresh descriptor writes, old success callback releases, real notification arrives, public/durable/fresh-restored rows remain equal. | +| XH-08 | `:629`: null/malformed messages reject listeners or error Collections. | Confirmed null TypeError outside the catch and malformed-key Collection error. | **fixed-now**, P2. Validate the protocol envelope before routing or storage work. | Transport owner: null/primitives/missing fields/invalid kinds/key carriers/key values plus an unseen durable row reached by a valid neighboring message. | +| XH-09 | `src/wrapper.ts:54`: contextual Error loses native error name/cause. | Confirmed native VersionError became Error with no cause. Request errors also lost identity. | **fixed-now**, P2. Contextual errors preserve the original cause; callback rejection identity remains intact. | Wrapper owner compares exact request-error cause, synchronous cross-realm cause and native open failure. | +| XH-10 | `src/errors.ts:3`: five exported classes are never thrown; adapter errors extend Error. | Confirmed source inventory and failed native-error instanceof probe. Existing docs explicitly disclaimed that hierarchy. | **fixed-now**, P3. Maintainer approved removing unused constructors, exports and reference pages. | README and wrapper cause contract; no new exception hierarchy or changed callback identity. | +| XH-11 | Root `test:oracles` uses arbitrary existing `db/dist`, fails fresh and leaves coverage enabled. | Confirmed: hiding db/dist fails package resolution before any test runs. Other adapters' tsconfig source paths corroborate the missing setup. | **fixed-now**, P2. Dedicated runtime/type source configuration; oracle commands disable coverage. | Complete runtime/type suite passes with db, db-ivm and adapter dist absent. Build-consuming declaration tests remain separate. | +| XH-12 | Package `test` rebuilds all dependencies after CI already builds them, potentially racing other readers. | Duplicate build and concurrent CI topology confirmed from scripts. The claimed intermittent CI failure was not reproduced. | **fixed-now**, P2. Default tests never build. Root package checks build after runtime suites; CI reuses completed builds after its runtime group. | `test:package`, `vitest.package.config.ts`, root test script and `scripts/ci-tests.mjs`; published consumer validation remains covered. | +| XH-13 | `:552`: every put/delete awaited serially; enqueue the batch and await native completion. | Ten-row import issues 20 requests but only one before the first succeeds. Improved request admission is proven; elapsed speedup is unmeasured. | **fixed-now**, P3. Queue native writes in the existing transaction and retain transaction completion/abort as settlement. | Settlement owner checks 0/1/10 rows, exact 2N request count and admission before first success; existing clone/abort matrices retain atomicity. | +| XH-14 | `:481`: unused collectionVersion, timestamp, stored updatedAt and phantom `_TKey` add cost. | Source search confirms no semantic readers. Persisted updatedAt has an observation in the existing persistence tests; generic position is public. | **deferred** stored-format/public-type cleanup, P3. Removed unused notification fields now; retained stored updatedAt and generic position. | Persistence owner must receive old/new record compatibility; declaration consumers must receive generic-arity changes. Both remain listed in the coverage map. | +| XH-15 | `:775`: estimatedSize is origin usage, not database size. | Confirmed: empty database returns a mocked origin usage of 987654321 bytes. | **fixed-now** documentation, P3. Property, guide and README state origin scope; name retained for compatibility. | Focused API witness and DatabaseInfo reference; no database-size guarantee or fabricated estimate. | + +## Changes and GREEN results + +The repair started with failing executable witnesses rather than adopting all +review claims as policy. Three decisions were explicitly approved: automatic +write order, connection-scoped deletion, and removal of unused error exports. +Import remains a schema-input API. No separate backup API was implemented. + +At the implementation snapshot: + +- **314 runtime/type tests** pass across 11 files, with coverage and no unhandled + errors or type diagnostics. This includes **146** local-order cases. +- **12** published-declaration tests pass after ESM/CJS builds, using Node16, + NodeNext and Bundler consumers without skipping library type checking. +- **54** browser cases pass, 18 each in Chromium, Firefox and WebKit, including + existing fixed/fresh campaigns and hostile controls. +- The complete runtime/type suite passes with all three dist directories + temporarily absent: **313 tests** before the final schema-contract example + was added. Each directory was restored in a finally block. +- TypeScript, ESLint, formatting, private-member cache safety and CI group + assignment checks pass. + +Commands from the package directory were the direct installed Vitest, +TypeScript, ESLint and Playwright CLIs. `pnpm --filter +@tanstack/indexeddb-db-collection test:package` ran the package build and consumer +lane. As recorded in earlier evidence, the local registry cannot supply tracked +Rollup 4.64.0; local builds use cached 4.59.0. The lockfile was not changed. +These are local results; remote CI is tracked separately. + +Production weight is **+102/-133 lines, net -31**, including removal of unused +constructors. Tests and configuration add independent evidence; the much larger +negative documentation diff removes the five obsolete generated API pages. + +## RED evidence and checker calibration + +With the new tests/configuration retained, replacing the two production modules +with their `eb4b8751f` contents gives **47 assertion failures / 209 passes**: + +| Owner | Intended assertion failures | +| --- | ---: | +| Local ordering | 28 | +| Transport boundaries | 10 | +| Collection identity compatibility | 3 | +| Request admission/atomic settlement | 2 | +| Native error causes | 3 | +| Required metadata-store admission | 1 | + +No timeouts or setup failures are counted in those 47. Restoring the fixed +modules makes the comparisons pass. The two new native tests each fail in all +three engines: stale deletion yields an empty public snapshot, and the later +local write reaches storage while the earlier handler is held. All six failures +occur at the intended assertions; all six pass with the repair. + +An initial deletion probe accidentally allowed fake-IDB to call the original +success callback directly. It did not reach the held-receipt premise and was not +credited as a refutation. Correcting the event-property control reached the +failure. An initial full browser run used a blocker after the first automatic +write; the new automatic queue made that fixture schedule deadlock. The omission +witness now uses explicit manual acceptance to admit both native transactions +before the blocker, retaining its original full-row and publication assertions. +Its automatic-order responsibility is covered by the separate new native test. + +## Oracle guide audit + +| Requirement | Evidence or applicability | +| --- | --- | +| ORC-001 | Approved local order/deletion laws; existing persistence, input validation and native transaction contracts; bounded limits stated beside each owner. | +| ORC-002 | Authored row folds and independent raw IDB observations; expected results do not read adapter caches or returned payloads. | +| ORC-003 | Opening/adjacent prose distinguishes law, model, legal cases, production driver and observation cuts. | +| ORC-004 | New matrices are bounded enumeration, not claims of generated grammar coverage. Existing fixed/fresh generated owners are unchanged and run green. All six orders/eight masks are explicit; metadata and identity axes retain their independent witnesses. | +| ORC-005 | Actual Collection APIs, real handler gates, raw storage and public/peer/fresh restore cuts. Native callback counters prove real notification delivery. | +| ORC-006 | 47 original-source assertion failures and six native original-source failures reach the intended checkpoints. | +| ORC-007 | No new important generated property; existing fixed/fresh/replay machinery remains in use. New local-order claim is finite enumeration. | +| ORC-008 | Local reference stores authored effects and decision positions; changing either can change the final accepted fold. It does not model a second persistence queue. | +| ORC-009 | Mutation order, handler settlement, native completion, Collection status and publication are distinct. The local fold is a stated abstraction of accepted whole-row effects. | +| ORC-010 | Existing harness preserves primary/cleanup diagnostics; held local gates release before assertions and native evidence remains runner-owned. | +| ORC-011 | Public/raw storage/fresh restore are separate observations that reject optimistic-only agreement and adapter-export-only false greens. | +| ORC-012 | This record identifies the exact implementation commit, RED/GREEN receipts and remaining owners; the coverage map preserves unresolved design boundaries. | +| ORC-013 | Held predecessor versus released predecessor, valid neighbor versus malformed message, missing versus present metadata, old versus recreated descriptor, and zero/one/many requests distinguish the asserted cuts. | +| ORC-014 | Native ordering and deletion/recreation premises are received in all three engines. The callback hold is explicit test control; uncontrolled tab suspension, quota, eviction and physical crash remain unproved. | + +## Deferred design and loss audit + +XH-06 retains the approved connection-retirement policy. XH-14 retains the +stored-field and public-generic compatibility question. Their destinations are +in the IndexedDB section of `oracle-coverage.md`. XH-04's separate trusted-output +restore API remains an optional design question; the documentation now describes +the existing input API correctly, and the invalid-input preservation witness is +retained. No new restore API is implied by a corrected example. + +The audit returned to all 15 raw findings, including the cleanup and diagnostics +items. Counts: **13 fixed-now + 2 deferred = 15**. There are no duplicates, +unaccounted items, or missing reproduction evidence for claimed behavioral +fixes. The build-race failure and elapsed performance claims remain explicitly +unmeasured rather than reported as reproduced. This is bounded evidence, not a +claim that every cross-tab history is correct. Cross-Collection ordering, +utility/automatic ordering, pending writes across cleanup/restart, lost +notifications, nested mutable values, and physical crash durability keep their +existing coverage-map owners. + +## Subsequent law audit + +The [law enforcement follow-up](2026-10-05-indexeddb-law-audit.md) supersedes +the closure and deferral assessment above without changing this historical +evidence. It records four previously surviving hostile implementations, +their stronger oracle checks, completion of XH-14, and XH-06 as an accepted +design. It also reopens XH-07 with a deletion-lifetime counterexample that +fails in the controlled suite and all three native browser engines. The +follow-up is working-tree evidence; a deletion design decision is pending. + +## Subsequent TLA+ refinement + +The [TLA+ translation and loss audit](2026-10-06-indexeddb-tla-refinement.md) +supersedes the open deletion-design disposition above. It records the approved +administrative deletion contract, oracle-first RED, source repairs, loss recovery, +production fault calibration and native receiving evidence. The historical +counterexamples above are retained; no deferral is used to claim closure. diff --git a/docs/contributing/oracle-reviews/2026-10-05-infinite-first-paint.md b/docs/contributing/oracle-reviews/2026-10-05-infinite-first-paint.md new file mode 100644 index 0000000000..f761089ff6 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-05-infinite-first-paint.md @@ -0,0 +1,424 @@ +# Infinite-query first-paint oracle + +- Reviewed semantic commit: `415a8d4a1ee0b950eca6f06f25c250adab05a15d` +- Comparison main: `987f6ca0dfc404de8c76bd8c2b840421cc26311e` +- Owner: `packages/db/tests/conformance/infinite-suite-oracle.ts`, scenario `[first-paint-ready]` +- Supporting contract: `packages/db/tests/conformance/infinite-contract.ts` (`InfiniteQueryObservedHandle.firstPaint`, `mountObserved`) +- Per-driver recorders: React `packages/react-db/tests/infinite-query-conformance.test.tsx`, Vue `packages/vue-db/tests/infinite-query-conformance.test.ts`, Svelte `packages/svelte-db/tests/infinite-query-conformance.svelte.test.ts` +- Independent cross-hook reference: `packages/react-db/tests/infinite-query-first-commit-parity.test.tsx` +- Runtime: local Node, Vitest. React ran in full. Vue and Svelte ran before the rebase and again under per-framework spot checks. CI runs every package suite. + +## New term + +This change introduces the observation term **first paint**. A first paint is +the first value a framework binding publishes to its consumer after mount. The +term is not in the project glossary yet. Each driver records the first paint +through its own native commit hook, because the frameworks reach it through +different cuts: + +- React records every layout commit and keeps the first. `current()` cannot + serve the law, because React can coalesce the idle commit before `renderHook` + returns. +- Vue reads the hook result immediately after setup. Its `watchEffect` with + `flush: 'sync'` already subscribed and produced the first value. +- Svelte reads the construction snapshot before `flushSync`, which is its first + render value, ahead of the subscribing `$effect`. + +The term describes a cross-framework observation. It does not combine or split a +production concept. + +## Requirement outcomes + +| Requirement | Result | +| --- | --- | +| ORC-001 | Pass. The law: for a synchronously loaded source, `useLiveInfiniteQuery` shows the ready first page on the first paint, with no empty idle paint before data. Limit: synchronous eager sources and the query-callback input form. The recorder observes a framework commit value, not a browser paint. | +| ORC-002 | Pass. The expected first page `['1','2','3']` comes from the source rows and the `orderBy(rank desc)` plus `pageSize` arguments, independent of the hook output. The parity test adds `useLiveQuery` as a documented prior-behavior reference. Neither reads the controller snapshot logic to compute the expectation. | +| ORC-003 | Pass. The scenario prose states the law and why `current()` cannot observe it. The contract comment states the first-paint obligation and the per-framework cut. The driver records the real hook; the checkpoint reads `firstPaint()` after flush. | +| ORC-004 | Not applicable. The trigger is a generated property that claims history coverage. This is a fixed example scenario over one synchronous source. It makes no generated-history coverage claim. | +| ORC-005 | Pass. Each driver mounts the real framework hook and records the real first paint through the framework commit hook, then flushes to `ready`. The checkpoint is the first recorded paint. | +| ORC-006 | Pass. Mutant calibration: reverting React or Svelte to `startSync: false` makes `[first-paint-ready]` fail at the first-paint checkpoint with `{ status: 'idle', ids: [] }`. This is an assertion failure at the intended checkpoint, not a setup error. The fixed `startSync: true` passes. | +| ORC-007 | Not applicable. The trigger is an important generated property. This scenario has no random campaign. Direct replay is running the named `[first-paint-ready]` scenario in each driver package. | +| ORC-008 | Not applicable. The change adds no production state and no stateful model. `firstPaint()` is a single test-only observation; it does not introduce, remove, combine, or split a modeled state. | +| ORC-009 | Pass. The one new term is "first paint", declared above with its per-framework mapping. No production concept is combined or split. | +| ORC-010 | Pass. The React recorder keeps the first observed commit and throws when no commit was observed, so it cannot silently report an empty paint. Cleanup uses the suite's existing `track()` and lifetime teardown; it does not replace the assertion. | +| ORC-011 | Pass. The parity test is an independent second formulation: it asserts that `useLiveQuery` and `useLiveInfiniteQuery` agree on the first commit for the same synchronous source. No further distinct semantic classifier was identified. | +| ORC-012 | Pass. This versioned record accounts for ORC-001 through ORC-014 at the reviewed semantic commit above. The pull-request description alone did not satisfy this requirement. | +| ORC-013 | Pass. The scenario protects a reusable cross-framework boundary law. The distinguishing witness is the mutant: `startSync: false` fails for React and Svelte at the first-paint checkpoint, while the fix passes. Vue's distinct sync-subscribe cut stays green under both, which the law allows. | +| ORC-014 | Not applicable. No controlled provider or host supplies a premise. The source is a standard synchronous mock collection shared with the rest of the suite. | + +## Bug-class closure + +The claim is bounded, not universal. + +- Boundary. Contract: ready first page on the first paint for a synchronous + source. History: mount of a query-form `useLiveInfiniteQuery` over a + synchronous eager source, plus React dependency replacement. Production path: + the React, Vue, and Svelte hooks that build the live-query window collection. + Observation: the first paint status and first-page ids. +- Distinguishing witnesses. Original: `startSync: false` yields an idle first + paint for React and Svelte. Adjacent: Vue's `watchEffect({ flush: 'sync' })` + yields a ready first paint even with `startSync: false`, which shows the law + constrains the observable first paint, not the flag. +- Rejected wrong design. `startSync: false`, the production defect, is rejected + at the first-paint checkpoint for React and Svelte. + +Unresolved in-scope cells remain open under the owner above: + +- Pre-created collection input form, rather than the query callback. +- Suspense, concurrent, or StrictMode first paint. + +Out-of-scope cells, where an empty or loading first paint is correct: + +- Asynchronous or on-demand sources that are not yet loaded. +- `dbClient`-materialized sources whose sync is deferred to commit. + +## Verification + +- react-db: the full suite passed locally (330 tests), including this scenario + and the mutant run. +- Vue and Svelte: `[first-paint-ready]` and the package suites passed under spot + checks. I could not re-run them against the latest `main` locally, because the + sandbox npm proxy returned 403 for unrelated security-bumped dependencies. CI + runs every package suite. +- This record does not mark any CI check green. + +## Addendum: duplicate pre-commit render fix + +A follow-up review found a regression from `startSync: true`: because the hooks +built a new collection per render and only recorded it at commit, a duplicate +pre-commit render (React StrictMode, a discarded concurrent render, or a +Suspense retry) started a second collection and loaded an on-demand source's +first page twice. `useLiveQuery` avoids this with a render-time instance memo +(its pool excludes window and on-demand queries, so the pool is not the cause). + +Fix: React now records each render's state in a render-time ref and reuses it +when every identity input matches, while committed state is still recorded at +subscribe so an abandoned render cannot overwrite preserved pages. Svelte's +`$derived` controller now reuses the previous controller when nothing that +defines it changed, instead of rebuilding on every recompute. + +Regression: `packages/react-db/tests/infinite-query-strictmode-dedup.test.tsx` +mounts an on-demand source under StrictMode and asserts the first peek-ahead +window loads once. It fails (two loads) when the memo is recorded only at +commit, and passes with the render-time reuse. This closes the StrictMode part +of the previously unresolved cell. The pre-created-collection input form and +Suspense or concurrent first paint remain open under the owner above. + +A later review found that the React reuse could bind a collection that GC had +already cleaned up. A committed component keeps its refs across a suspended +update, so the update's uncommitted collection survived to the retry, and the +retry committed an empty `cleaned-up` page before recovering. The reuse now +requires a collection that is not `cleaned-up`. +`packages/react-db/tests/infinite-query-stale-reuse.test.tsx` covers both a +fresh-mount retry and a mounted suspended update after GC. The mounted-update +case fails without the liveness check and passes with it. + +## Addendum: start-sync gate replaces render-time reuse + +A further review found that the two reuse mechanisms above caused new +defects. In Svelte, the reuse guard returned the previous controller when the +`deps` getters were unchanged, so the hook ignored reactive state read directly +inside the query callback. In React, `renderedRef` could keep a stale page +count. It did not dedupe React 18 StrictMode renders, and it duplicated the +identity rules. + +Both mechanisms are removed. The duplicate page requests came only from +on-demand sources, which load asynchronously and gain nothing from an early +start. `canStartLiveQueryWindowSyncInRender` now starts sync during render only +when no source collection is on-demand. Eager sources keep the ready first +paint. On-demand queries start when the subscription commits. + +Evidence at this change: + +- `infinite-query-strictmode-dedup.test.tsx` passes with one first-page load. + It fails with two loads when the gate always starts sync. +- `useLiveInfiniteQuery.svelte.test.ts` adds a test where state read inside the + query callback, with no deps, rebuilds the query. It fails with the removed + Svelte guard and passes now. +- `infinite-query-stale-reuse.test.tsx` passes. A retry after GC binds a live + collection and shows the ready first page. +- Local suites passed: react-db 333, vue-db 121, svelte-db 118, and the db + infinite calibration 9. + +Known limit: an eager collection started during render has only the 50 ms +unsubscribed GC floor before its commit. If a commit arrives later than that, +GC may clean the collection up first. `useLiveQuery` shares this exposure. No +test reaches this case. + +## Addendum: nested on-demand sources and retained-page windows + +A review of `725e37a28ac81e3d5041d990eb4abeebedc60399` reproduced two +regressions from starting sync in render: + +- The start gate read only a query's immediate sources. A live-query + Collection does not copy its sources' sync mode, so an on-demand source + behind one passed the gate. An abandoned React Suspense render then sent a + page request. The gate now follows each live-query Collection to the sources + it reads. +- A dependency replacement that keeps page depth built its collection with a + one-page window and started it in render. React committed a `ready` result + with fewer rows than the retained pages and `hasNextPage: false`, then + corrected it. React and Svelte now size the new window for every retained + page. + +Evidence, produced on the working tree above that commit: + +- `packages/react-db/tests/infinite-query-render-start.test.tsx` has three + tests. A wrapped on-demand source gets no acquisition from an abandoned + render, and a committed control acquires it. Every ready commit after an + equal dependency replacement keeps all retained rows and continuation. +- Mutants: without the nested walk, only the abandoned-render test fails. With + a one-page window, only the retained-pages test fails. +- `useLiveInfiniteQuery.svelte.test.ts` checks that no on-demand source, direct + or wrapped, is acquired at construction or by a superseded recompute before + the subscribing effect runs, and that one is acquired after it runs. Against + a `db` build without the nested walk, the wrapped case loaded once at + construction. +- Local suites: react-db 336, vue-db 121, svelte-db 119, and the db infinite + calibration 9. + +Svelte's retained-page window was fixed by the same rule, but no test observes +an intermediate Svelte value. The coverage map lists that cell. + +## Addendum: publication laws in the shared oracle + +This entry moves the laws found in the earlier reviews into the shared +infinite-query suite where they are framework-independent, and leaves only +pre-commit work to the drivers. + +| Law | Owner | +| --- | --- | +| A synchronous source's first published value is ready with the first page. | Shared `first-paint-ready` | +| Every ready value in a fixed-source, fixed-query scenario equals the source prefix for its own page count, with matching pages and continuation. | Shared, in 11 scenarios including `equal-dependency-depth` and `circular-dependency` | +| A mount requests an on-demand first window once. | Shared `on-demand-paging` | +| A render that never commits, or a superseded pre-commit recompute, does not acquire an on-demand source, direct or wrapped; commit does. Retired by the addendum "On-demand sources follow useLiveQuery"; replaced by acquisition parity with `useLiveQuery`. | React `infinite-query-render-cuts-oracle.test.tsx`, Svelte hook tests | +| A StrictMode double render requests an on-demand first window once. Narrowed by the same addendum to React 19, where both hooks keep refs across the double render. | React render cuts | +| After GC reclaims an abandoned render's collection, the retry's first commit is the ready first page. | React render cuts | + +Each shared handle now records every value its framework published, which +replaces the single first-paint recorder. Pre-commit work stays in drivers, +because Vue's setup is its commit. + +Evidence, produced on the working tree above `0f0e05cbb`: + +- With a one-page replacement window, `equal-dependency-depth` and + `circular-dependency` fail in React and in Svelte. The Svelte half of the + retained-page regression was real; no earlier test could observe it. +- With `startSync: false`, `first-paint-ready` fails in React and Svelte. +- In the React render-cuts file, removing the nested walk fails only the + wrapped abandoned-render test. Starting every source in render also fails the + StrictMode test. +- The four React-only files from earlier entries are merged into one. Their + retained-page test is dropped, because the shared law covers it in every + driver. +- Local suites: react-db 335, vue-db 121, svelte-db 119, and the db infinite + calibration 9. + +A probe found one more open cell. A supplied collection that has not started +publishes an idle first value in React and Svelte, and a ready one in Vue. +`useLiveQuery` starts a supplied collection in render. The infinite hook does +not, and a React test requires that an abandoned render leave a supplied +collection unsubscribed. Closing this cell changes that contract, so it is +recorded for decision rather than changed here. + +## Addendum: supplied collections match useLiveQuery + +Decision: the maintainer chose to make `useLiveInfiniteQuery` behave like +`useLiveQuery` for a supplied collection, and to reverse the rule from #1675 +that no supplied collection starts before commit. That rule came from a #1675 +review, which asked that collection construction be inert so that a render +that never commits leaves no resources behind. This PR keeps the purpose of +that rule with two safeguards: GC reclaims an abandoned start, and on-demand +sources still wait for commit. The contract test +`does not activate a supplied collection for an abandoned render` is replaced +by `reclaims a supplied collection that an abandoned render started`. + +A supplied collection starts during render only when no source behind it is +on-demand and its window already holds the requested rows from offset 0. A +shifted or narrower window waits for the controller to adjust it at commit. +Both hooks now validate a query or collection before starting it, so a +rejected `.findOne()` query never reads its source. + +Evidence, produced on the working tree above `083247d18`: + +- The shared `first-paint-ready-collection` scenario failed in React and + Svelte with an idle first value, and passed in Vue, before the change. All + three drivers pass after it. +- `collection-window-normalization` now checks every ready value. A supplied + gate that ignores the window fails it. +- A supplied gate that ignores on-demand sources fails the supplied-input case + of the React render-cuts abandoned-render test. +- `findone-runtime` now requires an unread source. Starting before validation + fails it with two subscribers. +- `live-query-window-controller.test.ts` adds a controlled-premise witness for + a commit that arrives after the 50 ms GC floor. The collection is cleaned up + first, and subscribing restarts it without publishing a non-ready value. A + restart guard that skips cleaned-up collections fails it. Delivering the + subscribe handshake's publications to the listener does not. The witness + drives the controller directly, because a test DOM cannot hold a React + commit, so it does not prove React scheduling. +- The walk from a live-query Collection to its sources is one shared helper, + `everySourceCollection`, which the observer's persisted-readiness check also + uses. The addendum "Supplied windows request only what the hook needs" + removes it with the on-demand gate it served. +- Local suites: react-db 338, vue-db 122, svelte-db 120, and the full db suite + of 8267 tests. + +Still open: an on-demand source that loads synchronously publishes ready +first in `useLiveQuery` and idle first in `useLiveInfiniteQuery`, because the +infinite hook defers on-demand sources to commit. The two hooks differ there +until that choice is made. With a DbClient, a source that is first created +during render waits for commit in both hooks, by the deferral contract from +#1564. + +## Addendum: on-demand sources follow useLiveQuery + +Decision: the maintainer chose that `useLiveInfiniteQuery` follow +`useLiveQuery` for on-demand sources too, unless that is plainly wrong. The +two rules conflict for an on-demand source. Ready on the first paint needs a +request during render, and no request before commit forbids one. +`useLiveQuery` starts on-demand sources in render, so the infinite hook now +does too. The start gate is removed, and the law that a render that never +commits sends no on-demand request is retired. A parity law replaces it: under +the same history, the infinite hook acquires an on-demand source as often as +`useLiveQuery` does, and commits the same first value. + +Two parts of `useLiveQuery` were matched in substance, not copied: + +- `useLiveQuery` dedupes a React 19 StrictMode double render with a + render-time instance memo. The infinite hook now keeps the window collection + from its latest render and reuses it when the query identity matches, the + collection is not `cleaned-up`, and its window holds the retained pages. It + keeps only the collection. Each render still builds its own controller and + page count from committed state. One identity rule serves both the committed + and the rendered baseline. +- A supplied collection starts in render as in `useLiveQuery`, but only when + its window already holds the requested rows from offset 0. Starting a + shifted or narrower window would publish the wrong rows first, which breaks + the ready-value law. + +Evidence, produced on the working tree above `e6a31f6eb`: + +- Before the change, an on-demand source that loads synchronously committed + ready first in `useLiveQuery` and idle first in the infinite hook. With the + gate removed alone, a StrictMode mount sent two first-window requests in the + infinite hook and one in `useLiveQuery`. With the memo, both send one, and + both send the same count for an abandoned render. +- React `infinite-query-render-cuts-oracle.test.tsx` compares both hooks for an + abandoned render with a direct source, a wrapped source, and a supplied + collection, and for a StrictMode mount. Deferring query collections to + commit fails the abandoned-render and StrictMode cases. Deferring supplied + collections fails the supplied case. Removing the memo fails the StrictMode + case. Removing the memo's liveness check fails the mounted suspended-update + retry with a `cleaned-up` commit. +- `useLiveInfiniteQuery.svelte.test.ts` compares settled acquisition counts + with Svelte's `useLiveQuery` after construction and a superseded recompute, + for direct and wrapped sources. Deferring query collections fails it and + Svelte `first-paint-ready`. The two hooks issue their requests at different + moments inside the same tick; only the settled count is compared. +- Local suites: react-db 337, vue-db 122, svelte-db 120, and the full db suite + of 8267 tests. + +Remaining exposure, shared with `useLiveQuery` by this decision: a render that +never commits, including a Suspense render that throws, sends on-demand +requests. On React 18, StrictMode does not keep refs across the double render, +so both hooks send two first-window requests there. Removing early on-demand +requests from both hooks needs a change in the live-query Collection itself. + +## Addendum: supplied windows request only what the hook needs + +An external review of `c3341af0f` raised ten findings. This entry records the +behavioral ones. + +Law: a mount requests no on-demand rows beyond the window the hook needs, for +either input form. Authority: established behavior on `main`, where a supplied +collection waits for the controller to set its window at commit. The shared +`on-demand-paging` scenario covered only query callbacks, and the supplied +scenarios used an exact `.limit(4)` window, so a wider window was never +generated. The new shared scenario `on-demand-collection-window` declares a +wider `.limit(10)` and an exact `.limit(4)`, and checks the rows the +on-demand source holds after mount, which are the rows it was asked for. + +- With the earlier rule, which started any window at least as wide as needed, + the scenario fails in React and Svelte: the source loads all eight rows. It + passes on `main` and with the repair. Vue starts at commit and passes both. +- The repair starts a supplied collection in render only when its window is + exactly the needed one, or unbounded. An unbounded window, from a query with + no `.limit`, sends one unbounded request on `main` too, because the + controller cannot narrow it before the source request, so starting it in + render adds no request and keeps its first paint ready. That pre-existing + unbounded request is outside this law and stays open. +- The React render-time reuse path uses the same rule. A reuse mutant that + accepts a wider window survives every suite: the reused collection's rows + are already loaded, so no observation distinguishes it. No harm was found. +- In Svelte, starting sync inside `$derived.by` can throw + `state_unsafe_mutation` when the start synchronously writes rows a peer's + pending load is waiting for. A two-component probe reproduces it for the + infinite hook and, on `main`, for Svelte's `useLiveQuery`, which starts in a + derived too. On the follow-up branch, where a live-query Collection defers + acquisition until a subscriber or preload, the same probe passes for both + hooks. + Decision: the maintainer accepted this exposure in this PR, shared with + `useLiveQuery`, and the deferred-acquisition follow-up removes it for both. +- On React 18, StrictMode does not keep refs across the double render, so both + hooks still send two first-window requests. The changeset now names React 19. + +## Addendum: render-time reuse follows useLiveQuery's identity and error laws + +A second external review of `16a34e061` reproduced two React regressions in +the render-time collection cache, both against `useLiveQuery` as the +reference. + +- A mounted replacement whose synchronous startup throws reached the error + boundary under `useLiveQuery` but committed `status: error` under the + infinite hook. React retried the render, and the retry reused the collection + the failed render had cached before it started. The hook now caches a created + collection only after it validated and started. The render-cut oracle, + renamed `infinite-query-render-cuts-oracle.test.tsx`, compares both hooks on + this history; caching before startup fails the infinite case. +- A mounted hook rerendered with a different source object under a claimed + Collection ID showed the old source's rows, after a suspended update and, + before this PR, after a committed one too. `useLiveQuery` rejects that + history. Its rule now lives in `source-id-bindings.ts`, shared by both hooks, + and the source ID reuse oracle adds an infinite-hook driver over its model: + direct reuse, reuse after another ID, the same object and a new ID, reuse + after a suspended render, and release of a shared descriptor's sync deferral + on rejection. Removing the infinite hook's claim fails five of them, and a + claim that releases nothing fails the deferral case. +- `on-demand-paging` and the exact supplied window now assert the first value + itself, not only later ready values, and the StrictMode parity test asserts + one first-window request on React 19. An infinite hook that leaves query + collections unstarted in render fails `on-demand-paging`, and one without + render-time reuse fails the StrictMode test. + +A third review of `f490f3d4d` found that the cache also reused a collection +that entered terminal error after it was cached, for example when its source +restarted after cleanup, so selecting its query again showed stale rows with +`status: error`. `useLiveQuery` builds a fresh collection there. The cache now +rejects a collection in terminal error. The render-cut oracle compares both +hooks on that history and checks the restarted source's rows; dropping the +error check fails the infinite case. A synchronous load failure still throws +from render in both hooks, and an asynchronous one settles after commit, so +neither path reaches the cache. + +The deferred-acquisition follow-up must revisit the new first-value assertion +for `on-demand-paging`: before a subscriber, its ordered on-demand window is +unpublished, so that law and this one conflict and need a decision there. + +## Revision index + +Each entry's evidence applies to the revision named here. The first section's +table and closure apply to `415a8d4a1`. + +| Entry | Revision | +| --- | --- | +| Record and requirement outcomes | `415a8d4a1` (record added in `1c74a335d`) | +| Duplicate pre-commit render fix | `a4af6a251` | +| Cleaned-up reuse liveness check | `fc5f136a5` | +| Start-sync gate replaces render-time reuse | `b31b84ff9` | +| Nested on-demand sources and retained-page windows | `2b461f8cd` | +| Publication laws in the shared oracle | `083247d18` | +| Supplied collections match useLiveQuery | `e6a31f6eb` | +| On-demand sources follow useLiveQuery | `c3341af0f` | +| Supplied windows request only what the hook needs | `ff7549054` | +| Render-time reuse follows useLiveQuery's identity and error laws | the commit that adds this entry, on top of `16a34e061` | diff --git a/docs/contributing/oracle-reviews/2026-10-05-round-3-followups.md b/docs/contributing/oracle-reviews/2026-10-05-round-3-followups.md new file mode 100644 index 0000000000..ce0e25a3e3 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-05-round-3-followups.md @@ -0,0 +1,127 @@ +# State mutation round 3 follow-ups + +Base revision: `cfb03f201` (`main` after #2032 and #2033). + +This record closes the four items that the round 3 record and the +ready-callback truncate record left open. One is a bug fix. The cache delete +stays, with witnesses. #2030 on `main` made the other two unnecessary. + +## 1. Metadata rebuild after a canceled earlier transaction + +**Gap.** The round 3 record called RB2, a mutant that keeps stale automatic +metadata writes through a rebuild, equivalent within legal histories. Reaching +it needed a canceled earlier transaction, which the grammar did not generate. + +**Correction, 2026-10-06.** The outcome below is wrong. A transaction begun +inside an open one can still commit first, and the rebuild then reclassifies +the open one's inserts. `main` loses last-write-wins there. See +[`2026-10-06-nested-begin-metadata-rebuild.md`](2026-10-06-nested-begin-metadata-rebuild.md). + +**Outcome: made unnecessary by #2030.** A first revision of this branch added a +canceled lane to the metadata composition oracle. An earlier held transaction +deleted key 1, the open transaction wrote against that projection, and the +earlier one was canceled. The lane found that `main` lost an explicit +`metadata.row.set` when the canceled delete turned the open insert into an +idempotent re-insert, and the revision fixed the rebuild. + +PR #2030 then allowed only the open last sync transaction to be canceled. Aborting +an accepted transaction's signal now has no effect, so a rebuild can no longer +reclassify an open write. The canceled lane's history no longer exists, and RB2 +is equivalent again. This branch drops the lane and the rebuild change and +keeps `main`'s rebuild. + +## 2. A ready callback error from a sync-entry truncate + +**Gap.** During sync entry, `ops.markReady()` deferred a ready callback's error +until the sync function returned. A truncate committed inside the sync +function threw the same error from `commit()`. The rest of the sync function +never ran, and the Collection moved to `error`. + +**Witness.** The sync reentrancy oracle runs a throwing `onFirstReady` callback +with `markReady`, a truncate commit, and a truncate commit followed by +`markReady`. The sync function must finish, the Collection must stay ready with +its rows, and the error must surface when sync entry returns. The two truncate +cases fail on `main`. + +**Repair.** The lifecycle holds ready-effect failures while a sync function +runs, whichever path makes the Collection ready, and sync entry reports the +first one when it returns. This replaces `markReadyDuringSyncStart` and the +per-entry flag that chose between two `markReady` calls. + +## 3. The sync commit's virtual props cache delete + +**Finding.** The round 3 record reported 422 reads that returned a cached +enriched row whose fields differed from the stored row. Those came from +comparing `NaN` with `!==`. With `Object.is`, instrumentation found no such +read across the `@tanstack/db` suite, with or without the delete. + +**Outcome: kept.** A first revision removed the delete. A high-effort review +then showed that the suite runs only in development builds, where the +reused-row check rejects an in-place change without `previousValue`. In a +production build that write is accepted and publishes no update, so only the +commit's delete keeps reads fresh. A deferred publication also enriches late. +New cases in `virtual-props-cache.test.ts` stub production mode for that write +and read inside a deferred publication. Both fail without the delete, so it +stays. + +## 4. The sync commit's completed optimistic keys + +**Outcome: removed by #2030.** A first revision of this branch removed the set, +and review then described a history where it might still matter, so the +revision restored it. #2030 drops optimistic state at settlement and removed +the set on `main`. + +## Review of this record + +A high-effort review found nine items. + +1. The cache delete still mattered in production builds. Reverted, with + witnesses that fail without it. +2. Insert-shaped and deferred publications of a reused row read the cache + late. The deferred-publication witness covers the second; the delete covers + both. +3. A history may still need `completedOptimisticKeys`. Reverted, then + removed on `main` by #2030. +4. A ready failure held during sync entry is dropped if the sync function then + throws. `ops.markReady()` already behaves this way on `main`, so the + truncate path now matches it. No change. +5. The ready failure sink restored an outer sink that cannot exist. Removed. +6. A cache comment described the removed delete. Resolved by the revert. +7. The rebuild replay had a simpler equivalent form. Adopted, then dropped + with the rebuild change after #2030. The 2026-10-06 nested-begin fix + restores it. +8. A hydration transaction rebuilt through a cancellation could lose its + metadata. #2030 marks hydration metadata as explicit, which fixes it on + `main`. +9. The first reused-row witness had no demonstrated kill. Replaced by the + production and deferred cases. + +The simplifier pass suggested one helper for the explicit metadata write, +which `metadata.row.set` and `metadata.row.delete` now share. + +## ORC outcomes + +- **ORC-001: met.** Item 2 cites the deferred ready-failure contract of + `ops.markReady()`. Item 3 cites the reused-row contract: reads return a + row's current value. Items 1 and 4 change no code here. +- **ORC-002: met.** The sync-entry and reused-row witnesses expect fixed + outcomes and read no production state. +- **ORC-003: met.** Each witness states its law beside its code. +- **ORC-004: not applicable.** No generated grammar changed. +- **ORC-005: met.** Each witness runs a real Collection through its sync API + and observes `collection.status`, `collection.state`, and public reads. +- **ORC-006: met.** `main` fails the two sync-entry truncate cases. Removing + the cache delete fails the production and deferred reused-row cases. +- **ORC-007: not applicable.** The witnesses are fixed cases. +- **ORC-008: not applicable.** No stateful model changed. +- **ORC-009: met.** "Sync entry" means the synchronous run of the sync + function inside `startSync`. +- **ORC-010: met.** Each witness cleans up its Collection in a `finally` + block. +- **ORC-011: not applicable.** No reviewer named a shared fault. +- **ORC-012: met by this record.** +- **ORC-013: met.** The sync-entry witness keeps `markReady` as the control + case. The reused-row witnesses sit beside the existing cases that declare + `previousValue` and publish at once. +- **ORC-014: not applicable.** No controlled provider or host supplies a + premise. diff --git a/docs/contributing/oracle-reviews/2026-10-05-state-mutation-round-3.md b/docs/contributing/oracle-reviews/2026-10-05-state-mutation-round-3.md new file mode 100644 index 0000000000..38b0400636 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-05-state-mutation-round-3.md @@ -0,0 +1,202 @@ +# State stack mutation round 3 + +Base revision: `987f6ca0d` (`main` after #2004 and #2008). The `@tanstack/db` +source was unchanged at the branch base. + +## Method + +Round 3 applied 47 plausible maintenance mistakes to the ported state stack: +35 in `state.ts` and 12 in `sync.ts`, `lifecycle.ts`, and `mutations.ts`. +Each mutant ran against the full `@tanstack/db` suite with `--bail=1`. Of the +47, 35 failed the suite and 12 survived. Each survivor received a probe that +passes on `main`, a code argument, or full-suite instrumentation that counts +the cases where the mutant changes a decision. + +## Survivors + +| Mutant | Change | Verdict | +| --- | --- | --- | +| S13 | Default `rowUpdateMode` becomes `full` | Gap. Closed here. | +| A1 | A sync delete that carries metadata keeps it | Gap. Closed here. | +| A3 | An insert without metadata no longer clears metadata | Gap. Closed here. | +| S6 | `markReady` runs before the truncate reapply | Gap. Closed with a fix in #2033. | +| S4 | The shared clear skips the pending direct-upsert marker | Equivalent. The later retirement loop removes every confirmed key. | +| L3 | The commit skips `restoreOrder` | Equivalent. `SortedMap` restores order on the next ordered read or write. | +| L4 | `reappliedKeys` starts as an empty set | Equivalent. An empty set skips no key. | +| X4 | A completed load-subset operation stays active | Equivalent. Every reader ignores or tolerates a completed operation. | +| M1 | Previous row origins omit direct keys | Equivalent on reached histories. Instrumentation found no differing virtual props. | +| M2 | The commit copies the pre-sync visible state | Equivalent by argument. The commit clears that state afterward. | +| S10 | Previous virtual props ignore completed optimistic keys | Equivalent on reached histories. See below. | +| S3 | The commit keeps a cached enriched row | Open. See below. | + +A fifth mutant, RB1, came from this review. It makes +`rebuildAutomaticRowMetadataWrites` overwrite explicit metadata writes. It +survived the suite and the metadata publication oracle. + +## Gaps closed here + +**Partial row updates.** The optimistic-history and retention oracles always +set `rowUpdateMode: 'full'`. No test sent a partial update in the default mode. +A separate partial lane now keeps the default mode. It reuses the generated +optimistic histories and marks source batches partial from its own stream, so +the full-mode campaign's fixed seed 86103 still produces `main`'s histories. A +partial batch omits `c` from its updates, and the model merges such an update +into its base row. The lane runs its own fixed (86104) and random campaigns. +Its fixed campaign must write at least one partial update whose omitted `c` +differs from the source's held row. Two fixed histories merge a partial update +while an accepted delete and an accepted schema-default re-insert overlay the +row. An in-suite wrong-answer witness writes the same rows in `full` mode and +must be rejected. S13 and MS, a mutant that merges onto the visible row +instead of the synced row, both fail the partial campaigns and those +histories. + +**Row metadata composition.** No test wrote a row delete that carried metadata, +or an explicit set followed by an insert without metadata. The new +`collection-row-metadata-composition-oracle.test.ts` enumerates all 1,457 +legal one-key histories of up to three writes, including `truncate` and an +idempotent re-insert, from three starting states. A last-write-wins model predicts the final metadata. Three +lanes reach the immediate, held, and rebuilt production paths. The rebuilt lane +also varies the earlier held transaction over a key-2 update and every key-1 +row write, for 1,767 histories. A1 and A3 fail all three lanes. RB1 fails only +the rebuilt lane. RI1, a mutant that computes metadata from a re-insert's +original type, fails all three lanes. TR1, a mutant that keeps a +transaction's earlier metadata writes through a truncate, passes the rest of +the suite and fails all three lanes. + +**Maintainer decision.** The written contract covered only writes that carry +metadata. On 2026-10-05 the maintainer adopted the current behavior as the +contract for the rest: an insert without metadata clears the value, an update +without metadata keeps it, a row delete or a truncate clears it, and +`metadata.row.set` after a delete or a truncate keeps metadata for the absent +row. A second decision the same day made an insert equal to the held row an +idempotent re-insert: without metadata it keeps the value. + +## Gap closed by a separate fix + +The ready-callback witness in `collection-sync-reentrancy-oracle.test.ts` +covered only an edit of a replaced key, through a subscriber with initial +state. Its sent-key filter hides a duplicate message. #2033 replaces it with a +model-based grid and publishes ready-callback messages after the truncate +batch. Its own record covers the evidence. + +## Review of this record + +A code review of the first version found six items: + +1. The insert-clears and update-keeps rules came only from production's helper. + Fixed: the maintainer decision above is now the authority. +2. Metadata kept for an absent row was pinned without a source. Fixed: the + same decision covers it. +3. The grammar had no `truncate`. Fixed: the grammar includes it, and TR1 + shows the gap. +4. The partial lane never ran with the schema default on `c`. Fixed: four + schema histories run it. +5. The reach counter counted writes, not writes that distinguish the modes. + Fixed: it counts only partial updates whose omitted `c` differs from the + held row. +6. The record said that the counter fails under S13. The counter is computed + by the driver and cannot fail under a production mutant. Fixed: the claim + is removed. + +## Second review of this record + +A high-effort review found nine more items: + +1. An insert equal to the held row keeps metadata, against the insert rule. + Fixed: the maintainer made it an idempotent re-insert, and the grammar + covers it. +2. The rebuilt lane's earlier transaction never touched key 1. Fixed: the lane + varies the earlier write. A mutant that keeps stale automatic writes (RB2) + still passes. In legal histories an open write keeps its type through the + rebuild, so RB2 is equivalent there. A canceled earlier transaction would + reach it, and this grammar does not generate one. +3. The schema-default histories could not distinguish a merge onto the + snapshot. Fixed: the new histories retain a default-carrying snapshot when + the partial update arrives, and MS fails them. +4. The partial field changed every history of the full-mode fixed seed. + Fixed: the partial lane has its own campaigns. +5. The partial lane had no in-suite wrong-answer witness. Fixed: the + `partial-as-full` witness. +6. The `sourceRows` comment was wrong. Fixed. +7. The `partialUpdates` counter was unused. Removed. +8. One `it` ran all lifecycles under the default timeout. Fixed: each lane has + a timeout. +9. The immediate lane did not assert its path, and a throw lost its label. + Fixed. + +CodeRabbit then found that the `partial-as-full` witness was vacuous. The +driver's "observation mutant reached its checkpoint" assertion always failed +for it, and the witness accepted any assertion error, so it passed even with +the `full` override removed. The configuration mutant is now exempt from that +assertion, and the witness requires a row observation to reject it. With the +override removed, the witness fails. + +## Open items + +The follow-up record +[`2026-10-05-round-3-followups.md`](2026-10-05-round-3-followups.md) resolves +the items this record left open. S3 was not equivalent in production builds, +which the suite did not exercise, so the delete stays with new witnesses. +PR #2030 removed S10's code on `main`. The follow-up record also said that +#2030 made RB2 unreachable. That was wrong: a transaction begun inside an open +one can commit first and reclassify the open one's inserts. See +[`2026-10-06-nested-begin-metadata-rebuild.md`](2026-10-06-nested-begin-metadata-rebuild.md). + +## ORC outcomes + +This record repairs two grammars and adds one oracle, so ORC-012 applies. + +- **ORC-001: met.** Each oracle names its contract. The metadata contract is + the last-write-wins test in `collection.test.ts` plus the 2026-10-05 + maintainer decision for writes without metadata. The partial-update contract + is the default `rowUpdateMode`. +- **ORC-002: met.** The optimistic-history model merges partial updates in its + own base map. The metadata model folds writes over one value by the decided + rules. Neither reads production state or imports its helper. +- **ORC-003: met.** The new oracle's opening prose states the law and limits. + Prose beside the model, grammar, and driver explains each one. The + optimistic-history prose now describes the partial-update lane. +- **ORC-004: met.** The metadata grammar is exhaustive within its bound. A + control checks the count, named witnesses, and two excluded illegal + histories. The partial-update counter shows that the partial campaign writes + partial updates whose omitted `c` differs from the held row. The rebuilt + lane's count control checks the earlier-write dimension. +- **ORC-005: met.** Both oracles write through a real Collection's sync API and + read public rows, change messages, and `metadata.row.get`. +- **ORC-006: met.** S13, MS, A1, A3, RB1, TR1, and RI1 each fail the extended + oracles. The `partial-as-full` witness checks in the suite that the + partial lane rejects a replacement. RB2 is equivalent within legal + histories. The metadata oracle also checks three named wrong + answers against its model. +- **ORC-007: met for the generated property.** The partial lane runs a fixed + seed (86104) and a random campaign under + `collection-state.optimistic-history-partial`, which accepts a seed and path + for replay. The full-mode campaigns keep seed 86103 and `main`'s histories. The metadata + oracle enumerates its whole bounded domain, so it has no random campaign. +- **ORC-008: met.** The optimistic-history model gains one flag. Two histories + that differ only in that flag can produce different base rows after a + partial update, so the flag is needed. +- **ORC-009: met.** "Partial update" and "row update mode" match the + production option names. +- **ORC-010: not applicable.** No shrinking or cleanup path changed. The new + oracle cleans up each Collection in a `finally` block. +- **ORC-011: not applicable.** No reviewer named a fault shared by production + and either model. +- **ORC-012: met by this record.** +- **ORC-013: met.** The metadata grammar includes neighbors on both sides of + each rule: an update with and without metadata, and an insert before and + after an explicit set. +- **ORC-014: not applicable.** No controlled provider or host supplies a + premise. + +## Note added 2026-10-05: settlement drop + +The settlement-drop change +([2026-10-03 review](2026-10-03-settlement-drop.md)) removes the retention +of completed optimistic rows. Under that law, a delete and a re-insert can no +longer overlay the source row after they settle. The two fixed partial-update +histories therefore run the partial update while both transactions persist. +The update is held, and it publishes with the drop at settlement. The row keeps +the source's merged `c`. The partial-as-full witness is still rejected, now +by the row observation at settlement. S13 still fails the retention oracle on +the merged revision, and S6 still fails the reentrancy oracle. diff --git a/docs/contributing/oracle-reviews/2026-10-05-truncate-ready-callback-delete.md b/docs/contributing/oracle-reviews/2026-10-05-truncate-ready-callback-delete.md new file mode 100644 index 0000000000..c16bb7e669 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-05-truncate-ready-callback-delete.md @@ -0,0 +1,158 @@ +# Ready-callback writes during a truncate + +Base revision: `931e8346f` (`main`). Grid revision: `4c338ddbd`. First +repair revision: `79c7786b4` (deferral). Split-transition revision: +`9bbeb1063`. + +## Law + +Each change message must be valid for a consumer that has applied every +earlier message: an insert names an absent key, and an update or delete names +a present one. Authority: the change-message contract in +`packages/db/tests/change-event-history-oracle.test.ts` and issue #1901. + +A truncate commit that makes the Collection ready runs ready callbacks +(`onFirstReady` and `status:change` listeners) during the commit, and a +callback may write. The truncate's messages and the callback's messages +together must be valid for every subscriber, with or without initial state and +with or without a filter. Each subscriber must end at the Collection's rows. +Subscribers receive the truncate's messages only after the Collection is ready. + +## Gap and bug + +State mutation round 3 found that a mutant which ran `markReady` before the +truncate reapply survived the suite. The ready-callback witness covered only an +edit of a replaced key, through a subscriber with initial state, whose sent-key +filter hides a repeated message. + +The bug on `main`: the truncate built its batch from the replaced rows, then +called `markReady` before it published. A callback's messages describe the +replaced rows, but they reached subscribers before the batch that moves +subscribers to those rows. Reachable failures: + +- A callback deletes a replaced key. The callback publishes the delete, and + then the prefix deletes the key again. +- A callback deletes a key that only the replacement holds. The subscriber + gets a delete for a row it never held. +- A callback changes a key that has an active prior request. The batch's + re-applied insert still carries the prior value. +- A filtered subscriber holds the pre-truncate row. The callback's delete + carries the replaced value, so the filter drops it, and the subscriber keeps + a row that the Collection removed. + +The first repair (`7331f25aa`) dropped prefix deletes for keys a callback +deleted. Code review and CodeRabbit showed the other three failures. That +repair also made the stale re-applied row silent, because the repeated delete +no longer exposed it. + +## Repair and witnesses + +- The ready transition takes a step that runs after the status reads `ready` + and before status listeners, ready callbacks, and the empty ready event. A + truncate that makes the Collection ready emits its batch in that step. So + the batch is built and enriched before any callback runs, subscribers + receive it while the Collection is ready, and every ready effect follows it. +- An earlier revision held the batch in a publication deferral around + `markReady`. A high-effort review found side effects of that design, listed + below, and the maintainer chose to split the ready transition instead. +- The grid in `collection-sync-reentrancy-oracle.test.ts` derives expected + rows from a model that overlays active intents, in order, on the source rows. + It crosses two hooks, four prior requests, every legal callback write to keys + 1 through 4, and three subscribers. Before the truncate the source holds keys + 1 and 4. The replacement holds 1 and 2, so key 4's prefix delete must remain. +- Each grid case also checks three observations. A message that carries a + source row is synced and remote unless a prior request owns its key. A live + query over the Collection becomes ready showing replaced rows. A raw + subscriber that the ready callback creates receives messages that are valid + for the rows it saw. + +## High-effort review of the deferral + +| Item | Finding | Outcome | +| --- | --- | --- | +| 1 | The deferral enriched the batch at publication, so a source row could go out as local. | Fixed by the split. 48 grid cases fail with the deferral. | +| 2 | The empty ready event skipped the deferral, so a live query became ready with pre-truncate rows. | Fixed by the split. All 144 cases fail with the deferral. | +| 3 | A truncate committed inside the sync function, with a throwing `onFirstReady`, throws from `commit()` and moves the Collection to `error`. `ops.markReady()` defers the same error. | Confirmed on `main` too. Open, for a separate fix. | +| 4 | A subscriber that a ready callback creates received the whole batch. | Fixed by the split. 120 cases fail with the deferral. | +| 5 | The deferral delivered the batch and the callbacks' messages as one uncomposed batch. | Fixed by the split: they are separate publications. | +| 6 | The deferral is a truncate-only special case. Split the ready transition. | Adopted. | +| 7 | One failure-fidelity case was vacuous. | Fixed: an already-ready Collection runs no ready callbacks, so the case is removed. | +| 8 | The grid did not check virtual props, live queries, or callback subscribers. | Fixed: see the witnesses above. | +| 9 | The two guards for the deferral encoded one condition. | Gone with the deferral. | +| 10 | A cleanup inside a ready listener dropped the deferred batch. | Fixed by the split: the batch emits before listeners run. | + +## Failure fidelity + +CodeRabbit found that the deferral let a subscriber error escape from the +publication before the commit resolved its applied receipts. A probe showed the +same hang on `main`, through `markReady`'s empty ready event. When a truncate +made the Collection ready, a throwing subscriber or `onFirstReady` callback left +a held receipt pending forever. + +The emit and the ready transition now run through one capture that keeps the +first error. The commit reports that error after its receipts settle, as it +does when the Collection is already ready. A witness holds a sync transaction +behind a persisting request, then truncates while a subscriber or a ready +callback throws. A third case throws from a subscriber on an already-ready +Collection. The two not-yet-ready cases fail on `main` and on the uncaptured +deferral. + +## ORC outcomes + +- **ORC-001: met.** The law above names its authority and the subscriber + modes it covers. +- **ORC-002: met.** The overlay model reads no production state. The checker + validates each message against the subscriber's own replica. +- **ORC-003: met.** The prose above the grid states the law, the model, and + why keys 2 and 4 are in the grid. +- **ORC-004: met.** The enumeration control checks the case count, five named + witnesses, and two excluded illegal writes. +- **ORC-005: met.** Each case writes through a real Collection's sync and + mutation APIs. It observes `subscribeChanges` batches, `collection.status` + at delivery, and `collection.state`. +- **ORC-006: met.** + - `main` and the first repair each fail 54 of 144 cases. + - A variant that marks the Collection ready after emitting fails 122 cases, + because subscribers then receive the batch while the Collection is loading. + - The publication deferral fails all 144 cases on the live-query check, 120 + on the callback-subscriber check, and 48 on the virtual-props check. +- **ORC-007: not applicable.** The grid is a fixed enumeration. +- **ORC-008: not applicable.** No stateful model changed. +- **ORC-009: met.** "Ready callback" means an `onFirstReady` callback or a + `status:change` listener for `ready`. +- **ORC-010: met.** Each case unsubscribes, settles its requests, and cleans + up the Collection in a `finally` block. The failure-fidelity witness checks + that a thrown subscriber or callback error is reported after every receipt + settles. +- **ORC-011: not applicable.** No reviewer named a fault shared by production + and the model. +- **ORC-012: met by this record.** +- **ORC-013: met.** Each callback write appears beside its neighbors: the same + key with and without a prior request, and a replaced key beside a + replacement-only key and an omitted key. +- **ORC-014: not applicable.** No controlled provider or host supplies a + premise. + +## Limits + +A truncate committed inside the sync function itself, whose ready callback +threw, threw from `commit()` and moved the Collection to `error`. The +[follow-up record](2026-10-05-round-3-followups.md) defers that error the way +`ops.markReady()` does. + +The grid covers one optimistic callback write per truncate. These histories +remain outside it: + +- a callback that rolls back an existing request; +- a non-optimistic callback write; +- several callbacks that write; +- generated histories that combine ready callbacks with the optimistic-history + grammar. + +## Note added 2026-10-05: settlement drop + +This fix merged with the settlement-drop change +([2026-10-03 review](2026-10-03-settlement-drop.md)), where readiness counts +accepted rows. A ready truncate batch still publishes before ready effects +run: the reentrancy oracle passes on the merged revision, and S6 still fails +it. diff --git a/docs/contributing/oracle-reviews/2026-10-06-indexeddb-donor-followup.md b/docs/contributing/oracle-reviews/2026-10-06-indexeddb-donor-followup.md new file mode 100644 index 0000000000..aac145e5b7 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-06-indexeddb-donor-followup.md @@ -0,0 +1,178 @@ +# IndexedDB approved donor follow-up + +Implementation revision: `08c58153f`. Starting revision: `32240d53af003f223f333c3bca1105ada02e4fbd`. +The maintainer approved all recommendations after the donor port: core typed-key +identity, abnormal closure, mutable capture, dedicated workers, native naming +and WebKit Blob evidence, and optional blocked diagnostics. Atomic storage +read-modify-write remains a separate API proposal; native durability stays unchanged. + +## Laws and receiving evidence + +| Law and authority | Independent owner and legal histories | Required observations and enforcement | +| --- | --- | --- | +| Collection key identity distinguishes numeric and string keys; existing public key contract, HC005. | Core optimistic transaction payload owner: four same-text numeric/string pairs, both author orders, four operation histories and success/rejection (64 cells). Existing same-key collapse remains. | Exact handler list preserves order, type, multiplicity and values; public keyed rows and size after settlement. Original source fails 32 original-order cells. Native host owner receives a same-transaction typed pair through persistence, peer update, import, clear and restore. | +| Supported mutable updates capture values at callback return; imports capture validated rows before awaiting storage. Approved ownership contract. | Core detachment model uses independent native construction/structuredClone and graph checks. Adapter value companion crosses six mutable shapes with update/import and real handler/storage holds. | Caller changes cannot alter immediate optimistic update, settled public, peer, export or raw durable values. Removing import capture fails all six import assertions. Original proxy fails 11 corrected detachment assertions; no timeout is counted as a kill. | +| Binary snapshots preserve native bytes, range, and buffer/view aliases. Existing typed-array compatibility remains. | Core detachment owner crosses local/foreign/shared buffers and both property orders, plus strict one-argument and prototypal subclasses. Native iframe companion receives foreign buffer/Uint8Array/DataView in each engine. | Detached identity, exact entire bytes, offset/length, native type and graph alias. Intermediate fixes fail strict-constructor and buffer-first alias cells; final source passes. Shared-memory concurrency, detached/resizable buffers, and arbitrary class private state are outside this law. | +| Actual native close retires the connection with error status and retained snapshot. Approved extension of managed closure. | Retirement owner: idle/ready/loading and five admitted persistence operations. Independent expected native abort/complete and caller outcomes. | Native transaction result, error status, retained public rows, late-admission rejection and fresh restore. Removing close listener fails six status assertions (idle/loading already reject failed native reads). Native Chromium storage clearing receives seven actual close histories, six with admitted work. | +| Blocked diagnostics observe a pending operation; only native terminal outcome settles it. Approved optional API. | Wrapper native open/delete matrix plus managed factory forwarding. Independent blocked listener proves reach. | Exact old/new versions, callback count and pending state, reentrant blocker release, final result. Omitting factory forwarding fails the callback observation. No timeout, cancellation or fallback is inferred. | +| Names route to the declared store, with unaffected neighbors. Existing isolation contract. | Native host owner substitutes six names with prefix neighbor and anchor stores. Authored per-store arrays receive typed insert, peer update, replacement, clear and restore. | Every public, durable, export and restored store snapshot. Controlled exact-name metadata mutant calibration is inherited from the donor port. Native metadata-version isolation remains owned by the controlled compatibility suite. | +| Dedicated workers share the page persistence contract. Approved host scope. | Real worker/page IndexedDB and BroadcastChannel history: insert, peer update, import, close, late write and worker recreation. | Public and durable rows, retained error snapshot, rejected late write and restored rows. Existing transport calibration remains authoritative; service/shared workers and suspended-host delivery are unclaimed. | +| A provider failure cannot count as value preservation. Existing truthful-settlement law. | Ordinary WebKit raw Blob failure has exact rejection and healthy-suffix checks. Disposable persistent WebKit requires raw acceptance and executes the same full histories. | Persistent WebKit cannot take the failure branch. Both Blob-containing preservation cells execute successfully there. This closes the local receiving gap without broadening the rejection classifier. | + +These laws are encoded and enforced within the finite histories above. They do +not establish every combination of lifecycle, value shape and host. Existing +coverage-map obligations for unordered cross-Collection writes, suspension/lost +notifications, post-durable notification failure, quota/eviction and physical +crash remain separate; no new implementation is silently deferred. + +## Follow-up reviewer reconciliation + +Two read-only agents reviewed the proposed implementation and simplifications. +The reviewer found actual compatibility regressions in the first binary-copy +repair and identified missing native receiving evidence. Those findings were +useful and technically accurate. The simplifier identified concrete cleanup +hazards and duplicate teardown. I would use both again for this bounded work. +Their initial findings were code-reading claims; executable evidence below +supplies the behavioral verification. A final read-only review also passed 17 +constructor/alias probes and found no additional concrete regression. + +| ID | Original claim | Technical verdict, evidence and destination | Final action | +| --- | --- | --- | --- | +| R1 | Length-sensitive typed-array subclasses receive a buffer and throw. | Confirmed. Strict one-argument and legacy-constructor cells fail intermediate source. Core detachment owner preserves existing construction semantics; native constructors preserve range. | fixed-now | +| R2 | Foreign standard typed arrays fail because backing buffers are flattened. | Confirmed. Foreign buffer-first/view-first graph checks and native iframe witnesses cover the missing realm axis. | fixed-now | +| R3 | Shared-buffer typed arrays regress. | Confirmed. Scalar edits, assigned views, both alias orders, detached bytes and prototype checks pass; concurrent shared-memory writes are explicitly excluded. | fixed-now | +| R4 | Capture lacks native receiving evidence. | Confirmed test gap. Real handler/storage holds and caller mutation reach the native value oracle in all profiles. | fixed-now | +| R5 | Forced-close abort lacks native in-flight evidence. | Confirmed test gap. Chromium native storage clearing closes/aborts loading and five admitted writes; actual close counters and native outcomes are asserted. | fixed-now | +| R6 | Managed onBlocked forwarding has no witness. | Confirmed test gap. Independent blocked reach plus diagnostic observation rejects omitted forwarding. | fixed-now | +| S1 | Preserve numeric-length subclass construction without more machinery. | Same product defect as R1; exact one-argument compatibility retained. Native binary branches earn their weight through alias/range laws. | duplicate of R1 | +| S2 | A terminated worker callable can hang cleanup. | Confirmed fixture gap. Cleanup becomes idempotent after termination; other commands reject immediately. Native recreation history uses the path. | fixed-now | +| S3 | Partial host/worker construction leaks resources. | Confirmed fixture gap. Failed host setup cleans Collections and closes the descriptor; failed worker setup terminates the worker. Aggregated errors retain the primary failure. | fixed-now | +| S4 | Forced-close fixture releases its gate twice unnecessarily. | Confirmed cleanup simplification. Redundant release removed, disposer preserved. | fixed-now | +| S5 | CDP detach can be bypassed on assertion failure. | Confirmed fixture gap. Register detach with existing failure-preserving cleanup immediately. | fixed-now | + +Adjacent findings from R1-R3 were not discarded: exact argument count, prototypal +subclasses, buffer-first traversal, shared backing identity and spoofed ordinary +data tags all have executable controls. A later native capture admission failure +also releases its gate through `finally`. + +Loss accounting: **11 raw items = 10 fixed-now + 1 duplicate**. No confirmed-open, +refuted, stale, deferred or unresolved design-decision items remain in this +follow-up review. This is bounded closure, not a universal correctness claim. + +## Calibration receipts + +All temporary production substitutions were restored in `finally`. The runner +used Vitest with coverage disabled and at most two workers. + +- Original `32240d53` proxy against the corrected `withChangeTracking` driver: + 11 assertion failures, 3 passing controls, 77 unrelated skipped cells. +- Import `modified: item` instead of `structuredClone(item)`: six captured-value + assertion failures. The actual delayed native put is reached. +- Remove native close registration: six error-status assertion failures; + idle/loading distinguish errors already exposed by failed reads. +- Remove managed onBlocked forwarding: one diagnostic-list assertion failure + after an independently observed native blocked event; blocker cleanup still + permits terminal settlement. +- Original global mutation key: 32 typed-key payload assertion failures. The + final grammar also crosses both author orders; unchanged same-key laws remain. + +An earlier binary RED draft observed raw `getChanges` before detachment. It was +not valid evidence for callback-return capture and is excluded. The replacement +calibration above uses `withChangeTracking`, the actual detachment boundary. +The temporary logs are named `/tmp/pr1179-calibrate-{original-copy,import-capture,native-close,blocked-forwarding}.txt`; +the named edits and selectors make these finite checks directly reproducible. + +## Guide conformance and code weight + +ORC-001–003: approved contracts, independent authored rows/value descriptions, +and adjacent literate prose separate model, grammar, driver and comparison. +ORC-004/007: additions are finite enumerations; existing fixed/random campaigns +and seed/path replay are unchanged. ORC-005/006: native gates/events establish +reach, and the assertion kills above establish sensitivity. ORC-008/009: no new +lifecycle state machine; canonical native transaction, Collection status and +settlement terms remain. ORC-010: teardown retains primary errors, including +worker replacement and CDP cleanup. ORC-011/013: native structuredClone, authored +byte/graph checks, provider probes, both traversal orders, strict subclasses and +pending-versus-terminal neighbors challenge plausible wrong designs. ORC-012: +this versioned record identifies source and calibrations. ORC-014: native value, +worker and name receiving runs in all three engines; forced close is explicitly +Chromium-only, and the raw WebKit failure has a separate successful provider. + +Production adds binary snapshot support and preserves prior custom construction; +it adds no reconnect, retry queue, fallback or database epoch. Adapter capture, +closure and diagnostics use existing control flow. Tests and explanatory code +carry most of the growth. Exact final weights and complete merged-revision +validation are recorded in the receipt below. + + +## Final merged-revision receipt + +The normal merge `3944198461443af22b52c7e7e063b672624ebd00` includes +`origin/main` at `48065980e`. The follow-up `36cdc3b89` only removes a now-unsafe +private-member map entry, renames two test generator variables and formats prose. +No published history was rewritten. + +- Complete core suite: **8,717 cases, 250 files**, runtime and configured type + checks green. The earlier run exposed 36 test root-directory errors and one + unrelated helper return-type mismatch. A test-specific root and the actual + operation return-type union repair those checks without changing assertions. +- Complete adapter suite: **2,467 cases, 15 files**, no type errors. Standalone + source/test/browser-driver typechecking also passes. +- Complete native suite: **247 passed, 14 skipped, 261 total**. The skipped cases + are exactly the seven Chromium-only forced-close controls in each other engine. + Persistent WebKit passes the same rich-value corpus, including both Blob shapes. +- Fresh db-ivm, db and adapter ESM/CommonJS/declaration builds pass. After the + private-member map cleanup, rebuilt core passes **290 distribution cases**, + the minified public API check (100 error names, metadata, queries and updates), + **16 package-consumer cases** and **three packed native-browser cases**. +- Changed-file lint, formatting, whitespace and the 382-name private-member map + check pass. The map drops `detach` because the new tests use Playwright's public + method with that name. Dependency builds finish before final lint/consumer runs. + +Commands use direct local Node entry points for Vitest, Vite, TypeScript and +Playwright. Vitest runs use coverage disabled and at most two workers. The +configured registry denies locked Rollup 4.64.0 with HTTP 403; local builds use +cached 4.59.0 while the lockfile keeps 4.64.0. These are local results, not a claim +that `pnpm test:pr` or remote CI passed. + +Before merging unrelated main changes, this follow-up's production delta is +**75 added / 13 removed lines (net +62)**. Tests and receiving drivers add +**1,215 / remove 22** lines; documentation adds **392 / removes 132** lines; +configuration adds **18 / removes 5**. The final formatting, map cleanup, receipt +and changesets are additional supporting changes. Binary native compatibility +accounts for 50 net production lines; the typed-key repair is net-neutral, and +adapter capture/closure/diagnostics add 12. No second lifecycle or recovery +machinery was introduced. + + +## CI fixture follow-up from `1c46068cf` + +CI's lint job ran without dependency builds. The adapter's main TypeScript +configuration resolved `@tanstack/db` through absent `dist` declarations, while +its test configuration alone had source paths. ESLint consequently lost Collection +types and misclassified six required assertions as unnecessary. Temporarily +removing both dependency builds locally reproduced all six errors and the unused +suppression warning. Moving the shared source paths to the main configuration +makes the same clean-checkout lint pass without changing the oracle assertions. +The test configuration inherits those paths. Rebuilt published declarations still +import the package name, as confirmed by the portable consumer checks. + +The browser CI lane also exposed packing's reliance on the host npm version. +`npm pack --ignore-scripts` ran fractional-indexing's lifecycle build under that +runner and failed before any browser consumer could execute. The fixture now +uses workspace-pinned pnpm and an explicit `ignore-scripts=true` option for every +package. This removes the separate external-dependency command and preserves +already-built artifacts. No package lifecycle rebuild is part of the witness. + +`tests/pack-hooks.test.ts` independently authors prepack, prepare and postpack +hooks that leave a marker and rewrite a built file. Suppressed packing must +leave no marker and preserve that file. An enabled native control proves each +hook is reachable. A temporary hooks-enabled helper fails all three suppression +assertions, and the repaired helper passes. These checks live in the package +lane, so ordinary runtime tests still do not pack dependencies. + +Validation: clean-checkout and built-checkout package lint, standalone typecheck, +package build, all 1,623 retirement-oracle cases, 19 package cases, and packed +browser consumers in Chromium/Firefox/WebKit pass. Formatting and whitespace +checks pass. Product code and oracle expectations are unchanged. The earlier +full-suite results retain their original revision boundaries. diff --git a/docs/contributing/oracle-reviews/2026-10-06-indexeddb-donor-port.md b/docs/contributing/oracle-reviews/2026-10-06-indexeddb-donor-port.md new file mode 100644 index 0000000000..cae9da0146 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-06-indexeddb-donor-port.md @@ -0,0 +1,128 @@ +# IndexedDB donor coverage port + +Reviewed implementation: `c4441632c7ac595d11c57061f5c5f9a3ae556de9`. +Production baseline: `fd3c05c818d76489438224e24d4e935751596dc0`. +This record concerns DP08, DP09, DP10 and DP17 from the +[sighted donor survey](2026-10-06-indexeddb-donor-survey.md). +It preserves the survey's original dispositions as historical evidence. + +## Result and receiving scope + +No production changes or new product policy were required. The port extends the +existing per-store and persistence models and adds a separate distribution smoke +lane. The shared rich-value definition is `structured-clone-oracle.ts`; its +controlled receiving companion is `persistence-values-oracle.test.ts`. The native +driver is `e2e/coverage-browser.ts`, with comparisons in `value-oracle.spec.ts`. +Drivers are not counted as additional independent models. + +| Finding | Executed law and bound | Limit | +| --- | --- | --- | +| DP08 | Two/three competing first native opens; same/independent absent names; complete identical declarations; schema, held restore/readiness, disjoint writes, convergence, fresh restore and later upgrade. Native engines receive three-open histories. | Opens settle before writes; no callback-order promise or union of conflicting declarations. No new liveness guarantee. | +| DP09 | Seven authored native value shapes: Date, odd ArrayBuffer, offset Uint8Array/DataView, BigInt64Array, Blob and nested array. Unrelated scalar update, peer, export/import, fresh descriptor; insert/import rejection at first/middle/last nested function; raw row/version rollback and valid suffix. | No post-submission mutation, custom prototypes, cycles, Map/Set, detached/shared/resizable buffers or schema transforms in this companion. | +| DP10 | Six legal database/store substitutions, prefix neighbor and anchor; scalar typed keys in separate transactions, update/import/clear, peer/public/durable/restore and untouched version values. | Native receiving covers prototype/Unicode creation and ordinary writes, not the entire controlled renaming/import/clear matrix. | +| DP17 | Tarball ESM/CJS consumers plus missing-export controls; Vite with no aliases/config rescue; native browser execution of persist/reopen. | Installed dependency closure is pinned with tarball overrides. This does not prove registry range selection, old runtimes, all bundlers or every export. | + +The new finite grammars intentionally extend existing models without multiplying +all value shapes by the entire lifecycle state space. Existing fixed/random +history campaigns and direct replay remain unchanged. The TLA+ lifecycle models +are unchanged because names, payload fidelity and package resolution add no +lifecycle or authority policy. + +## Sensitivity and test-first evidence + +[`review-evidence/donor-port`](https://github.com/TanStack/db/tree/main/review-evidence/donor-port/) contains the +replay script, source hash, classifications and complete receipts. + +- `name-prefix`: change exact metadata-store matching to `startsWith`. + Six assertion failures at metadata ownership after replacement; a neighbor's + entries disappear. This is neither timeout nor setup failure. +- `premature-readiness`: publish readiness before persisted restore. + Four assertions reject `ready` while native reads remain held/pending. +- `json-values`: JSON-convert accepted rows before native persistence. + The Date history rejects the peer's string value at the first insert checkpoint. +- Two resolver controls remove one installed tarball entry each. ESM/CJS loaders + fail with the missing-entry path; workspace aliases cannot rescue either path. +- Value-observer controls accept each correctly constructed corpus value and + reject flattened shapes, lost view offsets, altered backing bytes and omissions. + +All production mutants were restored. These establish sensitivity to the named +wrong designs, not universal coverage. There is no newly repaired source bug. + +The first legal-name draft inserted numeric `0` and string `"0"` in one Collection +transaction and reproduced the already tracked HC005 core payload collision. +Both ordinary and unusual names lost the numeric row. The final name grammar uses +separate transactions to isolate routing; HC005 stays open with its existing core +owner rather than being represented as fixed by this port. + +## Native provider distinction + +Chromium and Firefox receive all seven preservation shapes. The local Playwright +WebKit provider rejects Blob preparation with native `UnknownError: Error preparing +Blob/File data to be stored in object store`. A direct native transaction without +adapter code reproduces it. The executable raw-provider witness preserves this +classification. + +For the two Blob-containing WebKit cells, the test requires that exact native +failure, matching adapter rejection, empty public/durable/export rows, and a +successful Date suffix. These are executed rejection tests, not skipped tests or +Blob-preservation evidence. If native Blob storage succeeds on another platform, +the same test executes the full preservation history. A Blob-capable WebKit +receiving witness is still needed for that claim; the coverage map names it. + +## Guide audit + +| Requirement | Evidence | +| --- | --- | +| ORC-001 | Existing persistence, schema, readiness and per-store contracts; each owner states its finite domain and exclusions. No recovery/ownership policy is inferred from a donor. | +| ORC-002 | Authored per-store arrays and tagged value descriptions never derive expected rows from production storage or version classifiers. Production receives detached scalar inputs or fresh native values. | +| ORC-003 | Opening and adjacent prose distinguish law, value model, finite grammar, real API driver and checkpoint in each oracle/companion. | +| ORC-004 | No new random generator: bounded enumeration is explicit. Removing unusual names loses dictionary/routing cases; removing independent names loses isolation; removing native holds loses premature-readiness reach; removing offsets/types loses fidelity. Empty/reserved stores and conflicting declarations remain separate negative controls. | +| ORC-005 | Native open admission counts precede settlement; zero old versions prove absent names; held native reads precede readiness. Public/peer/raw/export/restore observations occur at stated cuts. Package consumers execute real loaders and storage. | +| ORC-006 | Three production mutants fail by assertion at the named cuts; value and packed resolver controls remain executable. | +| ORC-007 | Not triggered for new finite enumerations. Existing generated persistence and cross-tab campaigns remain registered and unchanged. | +| ORC-008 | No lifecycle states added. Value tags, content, offsets and backing bytes remain because an untouched-field observation distinguishes them; object identity is deliberately outside the law. | +| ORC-009 | Collection status/readiness, persisted restore, public snapshot, native transaction and settlement retain glossary meanings. Value descriptions are test-only observations, not product states. | +| ORC-010 | Controlled harness retains primary failures and cleanup errors. Native runner aggregates cleanup errors with the primary cause. Tarball fixtures own/dispose temporary directories. No shrinker or failure classifier was changed. | +| ORC-011 | Native constructors and independent typed/byte observations reject shared JSON assumptions. Direct native Blob admission distinguishes provider rejection from adapter corruption. Legal-name substitution preserves authored meaning. | +| ORC-012 | This versioned record identifies the reviewed implementation and reports each applicable obligation, calibration and open boundary. No universal bug-class closure is claimed. | +| ORC-013 | Prefix neighbor versus exact-name metadata; held versus completed restore; offset versus flattened view; native rejected Blob versus accepted Date; existing versus missing packed export are distinguishing neighbors. | +| ORC-014 | Native first opens, held restore and cross-page values run in Chromium/Firefox/WebKit. WebKit Blob preservation and the wider native naming matrix are explicitly unclaimed, with owners in the coverage map. | + +## Validation + +- 2,446 adapter runtime/type cases passed; 15 files, no type errors. +- 192 native browser cases passed across Chromium, Firefox and WebKit. Of the + 36 added cases, 19 prove rich-value preservation, two prove the documented + native Blob rejection/suffix, 12 prove first-open readiness/schema, and three + execute packed browser bundles. +- 16 package-consumer cases passed: 12 existing declaration checks, two packed + runtime examples and two missing-export controls. +- Full test-driver typecheck, changed-file ESLint, formatting and whitespace + checks passed. No production code changed, so no core rerun is claimed. + +An attempted dependency refresh hit the configured registry's unavailable +packages. Tests used the available cached Vite/Rollup installation; source, +manifest and lockfile were not changed to bypass it. A dependency declaration +removed by its prepare script was restored from the local package cache; external +packing now uses `npm pack --ignore-scripts`. No suite rebuilds another suite's +input. The native CI build list now includes the adapter before packed-browser +consumption. + +## What remains + +- Product decisions: abnormal native closure (DP02), mutable-value capture and + ownership (DP12), and worker-host support (DP13). These have not been silently + assigned recovery behavior. +- Optional new APIs: blocked-operation diagnostics, atomic storage read-modify-write, + and durability hints. Existing accepted contracts remain unchanged. +- Provider evidence: WebKit Blob preservation and the wider native naming matrix. +- Existing open boundaries, including HC005 and the previously recorded lifecycle/ + suspension/crash limits, remain in the coverage map. This port does not retire them. + + +## Approved follow-up + +The later [follow-up audit](2026-10-06-indexeddb-donor-followup.md) records the +approved closure/capture/worker contracts, optional blocked diagnostics, core +typed-key repair, and completed native naming and persistent-WebKit Blob evidence. +The earlier findings above retain their original revision and evidence boundary. diff --git a/docs/contributing/oracle-reviews/2026-10-06-indexeddb-donor-survey.md b/docs/contributing/oracle-reviews/2026-10-06-indexeddb-donor-survey.md new file mode 100644 index 0000000000..acbb039871 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-06-indexeddb-donor-survey.md @@ -0,0 +1,338 @@ +# Sighted donor perturbation: established IndexedDB wrappers + +2026-10-06. Target: TanStack DB PR #1179, IndexedDB adapter. Source inspection began on the pending refinement and was confirmed against pushed `e6a2b8b48`. The parent subsequently merged origin/main; it reports no IndexedDB source changes in that merge. This report does not claim to audit that merge. + +## Method and evidence boundary + +This is a **sighted, home-adjacent donor survey**, as requested. It is not blind distant-domain donor transfer. The researcher knew the target and read its glossary, oracle guide, coverage map, IndexedDB ORACLE.md, retirement/deletion-queue owners, native receiving suite, wrapper and adapter. The field-lab donor-perturb instrument requires fit judgments and nearby failed transfers; those appear for every inventory item below. No separate researchers, Field Log, or product changes were used. + +The selected repositories are mature, widely used examples of four different abstractions: a database/query wrapper, a native-shaped Promise wrapper, a minimal key/value wrapper, and a multi-backend storage wrapper. GitHub metadata retrieved during this survey reported: + +| Donor | Created | Stars at survey | Inspected package version | Immutable source revision | +| --- | --- | ---: | --- | --- | +| Dexie | 2014-02-26 | 14,626 | 4.4.6 | [`9282725a40bb659dca8e5971970243ef48238811`](https://github.com/dexie/Dexie.js/tree/9282725a40bb659dca8e5971970243ef48238811) | +| idb | 2015-08-06 | 7,417 | 8.0.4 | [`654c746bef13f9fe7f5871e03ac58015cebace5a`](https://github.com/jakearchibald/idb/tree/654c746bef13f9fe7f5871e03ac58015cebace5a) | +| idb-keyval | 2016-08-11 | 3,248 | 6.3.0 | [`17a69a1165bef486d88950cb47d3913744f038ac`](https://github.com/jakearchibald/idb-keyval/tree/17a69a1165bef486d88950cb47d3913744f038ac) | +| localForage | 2013-10-31 | 25,810 | 1.10.0 | [`fb78c77ee583fb727a558c439b5174aca38c5924`](https://github.com/localForage/localForage/tree/fb78c77ee583fb727a558c439b5174aca38c5924) | + +Package versions identify the checked-out source; they are not claims that these exact commits were published. Stars establish selection context, not correctness. Donor suites were read, **not run**. Two small source probes were run with Node 24.19.0 and the target's fake-indexeddb 6.2.5: one target wrapper probe and one donor negative-transfer probe. No browser result is inferred from those probes. Regression names below are verified source labels; the linked issue discussions were not separately audited. + +“Complete inventory” below means every retained mechanism from this bounded source survey, including covered and rejected transfers. It does not mean every donor test, historical issue, browser behavior, or possible bug has been examined. Source clones and probes are retained in `/tmp/pr1179-donors/`. + +## Result + +One target defect was measured: application assignment to a native transaction's `oncomplete` or `onabort` erases the wrapper's settlement observer and leaves its Promise pending after the native transaction ends. The most valuable additional tests are richer clone-preserving row values, simultaneous first database opens, user-controlled names, and canceled-versus-uncanceled native request errors. Native abnormal closure, nested input identity, same-key cross-Collection updates, suspension/lost notifications, and post-durable failures were already declared open boundaries; the donors strengthen their motivation but do not make them new discoveries. + +The source-linked inventory uses these dispositions: + +- **Measured bug:** executed target counterexample under the stated provider. +- **Coverage addition:** current law appears sufficient; add a distinguishing history/checkpoint, then determine RED/GREEN. +- **Existing boundary:** already tracked as unproved or undecided; donor does not close it. +- **Covered:** an identified target owner already receives the relevant mechanism within its declared bounds. +- **Design proposal:** would change the API, recovery policy, or product semantics; no automatic implementation recommendation. +- **Rejected transfer:** donor behavior belongs to another contract. + +## Full retained inventory + +### DP01 — Native observer composition preserves wrapper settlement + +**Source fact.** idb implements `tx.done` with additive `complete`, `error`, and `abort` listeners, removes them on settlement, and caches the Promise independently of application event-handler properties ([implementation][I-observers]). Its public contract says `tx.done` is the transaction-completion signal ([contract][I-done]). This is verified code/contract evidence; idb's inspected `done` test only checks that the Promise exists, so it is not credited as a comprehensive donor law test. + +**Target and result — measured bug, [fit: solid].** `executeTransaction` at `wrapper.ts:290–299` assigns `transaction.oncomplete` and `transaction.onabort`, then passes the same transaction to the public callback. The callback can legitimately assign either property. The target Promise remains pending after actual native completion/abort and after the application's observer ran. The parent independently reproduced the probe. + +| Native outcome | Application observation method | Observer calls | Target caller at post-event checkpoint | +| --- | --- | ---: | --- | +| complete | addEventListener | 1 | fulfilled with callback result 42 | +| complete | oncomplete assignment | 1 | pending | +| abort | addEventListener | 1 | rejected with aborted transaction | +| abort | onabort assignment | 1 | pending | + +The checkpoint awaits an independent native terminal listener and one subsequent event-loop turn; it does not infer native completion from a timeout. The Promise has no remaining completion observer in the failing variant. + +**Receiving law/owner.** Extend the wrapper owner: callback result AND native completion remain separately necessary and sufficient even when application code registers ordinary native observers. Cross complete/abort, callback resolve/reject, property assignment/reassignment/nulling and additive registration. Observe the native event, callback result, caller result/error identity, durable rows, and observer count. Keep the independent native observer outside the property under challenge. An internal-observer-property mutant must fail at caller settlement. + +**Negative control.** Assigning an application observer does not grant permission to suppress all event listeners with deliberate `stopImmediatePropagation`, or to demand success after abort. Do not copy idb's immediate rejection on every bubbling `error` without examining DP05. + +Reproduce: `node --experimental-strip-types /tmp/pr1179-donors/wrapper-listener-probe.mjs`. Receipt: `/tmp/pr1179-donors/wrapper-listener-probe.log`. No product file was changed. + +### DP02 — Abnormal native closure is its own event + +**Source fact.** idb documents `terminated()` for abnormal browser closure and explicitly distinguishes ordinary `db.close()` ([API][I-open]). Dexie's native `onclose` path identifies regression #2186: its public open-state used to remain true after the close event; the repair routes it through closure with auto-open permitted ([source][D-close]). idb-keyval invalidates its cached connection Promise on native `close`; its source comment cites Safari closure, which is implementation rationale rather than a measured Safari reproduction in this survey ([source][K-store]). + +**Target — existing boundary and contract extension, [fit: solid] for detecting the event; [fit: reach] for automatic reopen.** Managed closure presently covers explicit descriptor close and `versionchange` from upgrade/delete. It does not register native `close`. The coverage map already names abnormal native termination separately. The donor supplies a concrete fourth event dimension, not a newly unlisted defect. + +**Receiving witness.** If the immediate-error/retained-snapshot contract is extended to native termination, add idle/ready/loading/admitted-write/accepted-sync prefixes. First use the provider's actual forced-close facility, not mere event dispatch; it must abort native work and close admission. Check actual commit/abort outcomes, no late read publication, complete error suffix, rejected late startup and caller-time rows. Then acquire an honest native receiving witness or retain an explicit controlled-provider limit. + +**Negative control.** Native `close` is not emitted by ordinary `db.close()`. Simulating a `close` event on a still-open database does not establish forced termination semantics. Do not transfer Dexie's or idb-keyval's automatic reopen: TanStack's approved managed-close contract requires new descriptors/Collections. + +### DP03 — A pending blocker can be observable without becoming failure + +**Source fact.** idb exposes separate `blocked`, `blocking`, and terminal outcomes; tests check version numbers and distinguish delayed blocker release from immediate auto-close ([tests][I-blocked]). localForage logs a blocked deletion separately from native success ([source][L-delete]). + +**Target — covered settlement law; optional design proposal, [fit: solid].** Wrapper and deletion-queue owners already prove blocked requests stay pending and receipts require native success. The current open/delete APIs expose no blocked diagnostic callback. A user cannot distinguish a blocked request from other pending native work through that Promise alone. + +**Possible affordance.** Add an optional diagnostic hook with operation/name/oldVersion/newVersion only if desired. Its oracle must prove hook delivery does not resolve/reject/cancel the operation and hook failures do not claim native success. + +**Negative control.** A blocked event is not a timeout or lock-acquisition failure. It does not authorize an in-memory fallback, cancellation, or reopening loop. + +### DP04 — Request progress, callback settlement, and native completion are different cuts + +**Source fact.** idb documents `tx.done` and warns against awaiting unrelated asynchronous work inside a native transaction ([completion][I-done], [lifetime][I-lifetime]). Dexie separately implements and tests an explicit `waitFor` capability ([tests][D-wait]). + +**Target — covered, [fit: solid].** The wrapper owner already crosses request success then abort, callback delay after native completion, and readwrite late callback rejection with durable rows retained. Retirement owners observe native commit and source confirmation at caller success. No new oracle owner is justified for these same laws. + +**Remaining useful handoff.** The wrapper's arbitrary-async-callback cases are controlled-provider evidence; a small real-browser pair with only IDB awaits versus a deliberately unrelated task would establish that exact lifetime premise. Existing ordinary native adapter tests do not prove every low-level callback path. + +**Negative control.** Do not promise rollback after a callback rejects following native commit. Do not import Dexie's keepalive as if native async callbacks already keep a transaction active. + +### DP05 — A handled request error need not be an aborted transaction + +**Source fact.** Dexie's `eventError-transaction-catch` and `eventError-request-catch` tests distinguish an uncaught duplicate-unique request, which aborts, from a caught request error followed by a successful sibling request and committed transaction ([tests][D-errors]). The transfer is the event-versus-outcome distinction, not Dexie's Promise interception machinery. + +**Target — coverage addition, [fit: solid].** The low-level public callback receives native requests/stores. A request `error` handler can call `preventDefault()`, permitting native continuation; merely attaching a Promise catch has different native semantics. The current wrapper correctly waits for terminal abort/complete rather than rejecting on each bubbled request error, but inspected tests do not distinguish these two native histories. DP01's repair should preserve that distinction. + +**Receiving witness.** Wrapper owner: establish a unique index using the supported low-level upgrade API, queue a failing add and valid neighbor, cross native request-error cancellation versus no cancellation, and callback success versus rejection. Compare the exact committed subset in the canceled-error case, full rollback in the abort case, caller receipt and native terminal event. A mutant that rejects on every transaction `error` should fail the committed case. + +**Negative control.** Catching a rejected Promise is not automatically native `preventDefault`. Adapter-managed batch writes still promise all-or-nothing and intentionally expose no request-error interception. Do not add partial acceptance there. + +### DP06 — Atomic batch law must survive synchronous clone errors + +**Source fact.** idb-keyval advertises atomic `setMany` and queues puts in one native transaction ([source][K-batch]). Its inspected tests cover ordinary and empty batches but contain no corresponding synchronous-clone rollback witness ([tests][K-tests]). Dexie's bulk failure regressions intentionally preserve successful rows outside an enclosing all-or-nothing transaction, including failure positions on either side of an internal >50-item path ([regressions][D-bulk]). + +**Target — covered, [fit: solid] for failure-position grammar; rejected transfer for partial success.** Target settlement already covers first/middle/last clone failure in automatic/manual/import batches, durable rows and versions, no success notification, an ordinary successful suffix and restore. Request-admission counts protect bulk queuing. This is stronger relevant evidence than “the donor uses one transaction.” + +**Executed negative-transfer control.** Against pinned idb-keyval source + fake-IDB, `setMany([['prefix', valid], ['bad', function]])` rejected `DataCloneError` but a subsequent read found the valid prefix. The synchronous exception escapes before a transaction observer/abort is installed. This survey does not claim a native-browser reproduction. Reproduce with `/tmp/pr1179-donors/keyval-atomicity-probe.mjs`; receipt has the same basename `.log`. + +**Negative control.** Never transplant Dexie's documented partial-bulk success into TanStack import/acceptance. Never treat idb-keyval's atomic documentation or one-transaction structure as proof of clone-error rollback. Donor internal thresholds do not justify a 50/51 target test unless target production has that threshold. + +### DP07 — Failed opening cannot leave partial schema or poison later use + +**Source fact.** Dexie tests an exception after an earlier migration and reopens to verify the original version, plus failed population leaving no created database ([tests][D-upgrade-errors]). idb-keyval clears the cached opening Promise on rejection ([source][K-store]). + +**Target — covered in the applicable API, [fit: solid].** Wrapper failed-upgrade/lower-version histories already compare original rows/schema, reject at native error, and permit later successful use. `createIndexedDB` does not own a cached open Promise, so cached-rejection recovery is not another state to add. + +**Negative control.** The donor's multiple content-migration callbacks are not a promised createIndexedDB migration API. Neither retrying a `VersionError` with a different version nor deleting a failed database is a neutral repair. + +### DP08 — Simultaneous first initialization is different from concurrent writes + +**Source fact.** localForage has a specific test starting four same-store instances before initialization and issuing independent writes immediately ([test][L-concurrent]). This is separate from its test of concurrent operations on already created instances. + +**Target — coverage addition, [fit: solid].** The inspected target harness creates its first database and then additional descriptors sequentially. Existing cross-tab histories begin from established descriptors. Add two/three simultaneous `createIndexedDB` calls against an absent name with the same version and complete store declaration; immediately start their Collections and author disjoint writes. + +**Receiving witness.** Compatibility owner plus a native page/worker receiving case: verify all opens settle, every descriptor has the complete requested native schema, each Collection reaches readiness only after its establishing read, no orphan handle blocks the later managed upgrade, all disjoint writes survive, and a fresh restore matches authored rows. Cross different database names as the independence control. + +**Negative control.** Two same-version initializers requesting different store sets do not promise a union: only the opener that creates/upgrades the database can create stores. The documented “declare all stores up front” rule remains authoritative. Do not silently add version bump/retry machinery to make conflicting declarations work. + +### DP09 — Preserve structured-clone value types through an unrelated row update + +**Source fact.** Dexie regression #1890 stores a nested `BigInt64Array`, changes an unrelated timestamp, and asserts the value remains a `BigInt64Array` afterward ([test][D-values]). localForage explicitly tests ArrayBuffer, odd-length buffers, Blob and typed arrays ([tests][L-values]). idb tests NaN as a value and Date-bearing objects ([tests][I-values]). + +**Target — coverage addition, [fit: solid] for documented accepted cloneable values.** Main target grammars use bounded scalar fields; the schema-transform example checks Date in export/import but not unchanged rich fields through automatic update, peer invalidation and fresh Collection restore. This leaves a common serialization/proxy boundary largely unexercised without requiring a mutable-input identity policy. + +**Receiving witness.** Add a small independent authored value corpus (Date, ArrayBuffer, offset typed-array/DataView, BigInt64Array when available, Blob, nested arrays; Map/Set only if the Collection value contract admits them). Write once, update only a scalar sibling, receive through another descriptor, export, clear/import via valid schema input, then reopen. Assert type/bytes/date timestamp and unchanged content at every cut, not JSON equality or object identity. Pair each with a genuinely uncloneable function at the same nested position and require atomic rejection. + +**Negative control.** Native structured cloning does not preserve custom class prototypes, arbitrary identity, functions, or accessors as live behavior. localForage's undefined-to-null normalization is a cross-driver contract and must not be copied. Worker/page transport serialization must not erase the value distinctions before comparison. + +### DP10 — Ordinary user-controlled names are a grammar dimension + +**Source fact.** Dexie regression #1920 uses an index named `constructor` and checks both open and actual query/write use ([test][D-names]). Its exact failure concerns schema dictionaries, not TanStack's implementation. + +**Target — coverage addition, [fit: plausible].** The target public store names are nonempty strings except `_versions`; it builds a plain-object store record and compound metadata keys. Existing cases use ordinary names. Add a metamorphic renaming law for legal database/store names such as `constructor`, `__proto__`, `toString`, Unicode, delimiters and lexically adjacent names; preserve the same authored row/metadata isolation law through import/clear and peer delivery. + +**Receiving witness.** Compatibility owner should prove these names actually reach native creation, store-record lookup, version-prefix traversal and notification routing. Include more than one store so an overbroad prefix scan is observable. + +**Negative control.** `_versions` remains reserved and empty names remain rejected. Dexie's support for index/compound-key syntax is not transferable to the Collection configuration. This survey did not reproduce a target name bug. + +### DP11 — Update serialization is not atomic read-modify-write across Collections + +**Source fact.** idb-keyval `update` reads and writes in one readwrite transaction. Its test runs three concurrent increments and expects 3; its README distinguishes this from separate get/set calls ([implementation/test][K-update], [explanation][K-update-doc]). + +**Target — existing boundary; potential design proposal, [fit: solid] for the distinction.** The approved local-order law preserves mutation order inside one Collection. It does not make two stale Collection snapshots increment a shared counter atomically. Whole-row persistence after arbitrary user handlers cannot inherit idb-keyval's read-inside-the-storage-transaction semantics. + +**Possible affordance.** Decide whether to offer a separate storage-level atomic mutation API, explicit compare-and-swap, or only document whole-row conflict behavior. An oracle would need native serialization of concurrent same-key updates and legal results computed from one coherent order, not acceptance of each field against a different possible order. + +**Negative control.** Do not change ordinary Collection update to run arbitrary user code in an IndexedDB transaction, silently merge whole rows, or claim cross-Collection atomicity from the single-Collection acceptance queue. Disjoint-key preservation is already covered. + +### DP12 — Caller-owned returned objects can poison cached observations + +**Source fact.** Dexie regression #2309 mutates a returned object in place, puts it, and requires another live-query emission and a new reference under two cache modes ([test][D-alias]). + +**Target — existing boundary/design decision, [fit: plausible].** The target coverage map already leaves nested mutable input identity open. This donor names a concrete failure mechanism: if expected and observed values share a mutable reference, the mutation can disappear from comparisons or suppress notification. Current detached scalar model rows do not settle the richer ownership policy. + +**Receiving witness after policy.** Distinguish an authored input, a public Collection value, export output and native read clone. Mutate one after submission/observation; compare independently frozen authored values at the promised capture cut, subsequent durable values and every public event. Cross mutation before native `put` versus after it. Preserve both aliasing and detached controls. + +**Negative control.** A library promising immutable Collection values need not adopt Dexie's supported mutate-and-put flow. Do not simply demand new references on every no-op update; that changes event/identity semantics. + +### DP13 — Worker execution needs its own host receiving witness + +**Source fact.** localForage tests actual dedicated-worker and service-worker persistence and same-origin iframe use ([worker][L-worker], [service worker][L-serviceworker], [iframe][L-iframe]). One service-worker registration case is explicitly skipped. Dexie also has a dedicated-worker regression #76 ([test][D-worker]). + +**Target — coverage addition or support-policy clarification, [fit: plausible].** The wrapper resolves globalThis.indexedDB and the compatibility owner simulates no ambient globals/custom factory. Native receiving cases currently use same-origin pages. If worker use is supported, transfer the same persistence/notification/managed-close laws into a real dedicated worker and a page. Start with explicit writes and peer restore; service-worker restart and lifetime are a separate extension. + +**Negative control.** Simulating `window` absence in Vitest is not a real worker witness. Same-origin iframe success does not establish cross-origin or partitioned-storage sharing. Do not copy service-worker background-delivery promises from an active-worker unit test. + +### DP14 — Store and descriptor isolation must survive reuse and removal + +**Source fact.** localForage verifies same-key values in separate instances, own-store-only clear/iteration, and drop/recreate behavior ([scope tests][L-isolation], [drop tests][L-drop]). idb-keyval repeats operations against default/custom stores ([tests][K-tests]). + +**Target — covered within the current API, [fit: solid].** Target compatibility/persistence already distinguish database/store/Collection identity, typed keys, shared descriptors across DbClients, cleanup of one sync run, untouched versions and per-Collection manual acceptance. Administrative deletion deliberately removes the whole database; source snapshots remain on errored old Collections and fresh storage is empty. + +**Negative control.** localForage `dropInstance(storeName)` is not TanStack administrative delete-by-name. Do not reintroduce Collection deletion authority or erase sibling stores to imitate the donor's convenience API. + +### DP15 — Reconnection and backend fallback are choices, not mandatory robustness + +**Source fact.** localForage retries native transaction creation once for absent/InvalidState/NotFound connections and may upgrade a missing store; multiple instances receive the replacement connection ([source][L-reconnect]). Its 1.5 changelog records an engine selection change making existing Safari data appear missing because reads moved from WebSQL to IndexedDB ([history][L-history]). + +**Target — rejected transfer, [fit: reach].** Approved behavior is sticky error after managed connection closure, fresh descriptors/Collections for reuse, and no timeout/in-memory fallback. Automatic schema repair, restart or alternate backend introduces a new source of truth and contradicts that policy. Donors show why these policies must be explicit. + +**Negative control.** A blocked open is not evidence IndexedDB is unavailable. An empty new backend is not evidence the original database was empty. Failed ordinary writes must not resolve “persisted” through an implicit volatile fallback. + +### DP16 — Native commit and crash durability are separate claims + +**Source fact.** Dexie exposes a transaction-durability option and tests native `transaction.durability` for default/strict/relaxed when Chromium supports it ([tests][D-durability]). Those assertions check option propagation, not physical power-loss survival. + +**Target — existing boundary plus optional affordance, [fit: solid] for the distinction.** Target `isPersisted` evidence is native transaction completion; physical crash is explicitly outside the oracle. A configurable native durability hint could be useful, but is a separate API decision, compatibility check and performance tradeoff. + +**Receiving witness if added.** Check actual native transaction option propagation per write path and supported engine, preserving the same atomicity/settlement oracle. Keep crash testing separate. + +**Negative control.** `strict` is not a proof against quota, eviction, site-data clearing or device failure. Neither a native commit event nor a synthetic page close demonstrates physical durability. + +### DP17 — Package consumers need runtime evidence distinct from declarations + +**Source fact.** localForage's changelog records adding `module` in 1.8.0 and reverting it in 1.8.1 because it broke builds; dedicated Browserify/Webpack runners are retained ([history][L-history], [browserify runner][L-browserify], [webpack runner][L-webpack]). + +**Target — declared packaging boundary, [fit: solid].** The target's 12 consumer cases compile ESM/CJS declarations through symlinked builds, explicitly excluding tarball completeness. They do not execute the installed package in a consuming app. Do not discount those type tests; add the different observation if claiming publish readiness. + +**Receiving witness.** Pack the candidate, install into a disposable consumer, run minimal ESM import and CJS require against a supplied fake factory, and include one actual browser-bundler import. Assert public exports and simple persistence, no source aliases or workspace rescue. Keep supported module-resolution tests separately. + +**Negative control.** A root build or TypeScript noEmit success does not prove runtime exports or tarball files. No need to import every historical bundler merely because localForage did. + +### DP18 — Native error taxonomy must remain inspectable + +**Source fact.** idb-keyval asserts named native failures for invalid clone inputs; idb passes request errors through. Dexie deliberately has its own typed hierarchy ([keyval tests][K-errors], [idb implementation][I-observers], [Dexie error handling][D-errors]). + +**Target — covered, [fit: solid].** Current wrapper tests preserve exact native `cause`, including cross-realm synchronous exceptions, request-error identity and admission paths. The maintainer explicitly chose removal of unused exported specialized errors. More donor exception classes are not an uncovered target requirement. + +**Negative control.** String-matching one provider's error text or requiring every DOMException to satisfy the local realm's `instanceof Error` weakens portability. Do not copy Dexie's hierarchy against the approved target API decision. + +## Recommended order + +1. Repair DP01 through the wrapper owner. Its measured history fits the existing settlement contract, so it does not require a new recovery design. Preserve DP05's terminal-event distinction during the repair. +2. Add a small law-based coverage extension for DP05, DP08, DP09 and DP10. These change history/value dimensions, not the adapter architecture. Keep failures on the old source/mutants separate from green additions that only close an evidence gap. +3. Clarify native abnormal-close behavior (DP02), mutable-value ownership (DP12), and worker support (DP13) before borrowing donor recovery/identity behavior. These are existing or newly explicit boundaries, not excuses to assert they work. +4. Treat blocked diagnostics, atomic storage updates, durability hints and new migration/reconnect APIs as optional design proposals. Mature wrappers demonstrate their usefulness and costs; they do not authorize those changes here. +5. Add the disposable packed runtime consumer when claiming package release completeness (DP17), without replacing the existing declaration lane. + +No donor mechanism found here closes the known lost-notification/suspension or post-durability-send-failure boundaries. Those remain under their current owners. No claim about every possible cross-tab failure follows from this finite survey. + +## Immutable source index + +[I-observers]: https://github.com/jakearchibald/idb/blob/654c746bef13f9fe7f5871e03ac58015cebace5a/src/wrap-idb-value.ts#L69-L94 +[I-done]: https://github.com/jakearchibald/idb/blob/654c746bef13f9fe7f5871e03ac58015cebace5a/README.md#L239-L252 +[I-open]: https://github.com/jakearchibald/idb/blob/654c746bef13f9fe7f5871e03ac58015cebace5a/README.md#L74-L129 +[I-blocked]: https://github.com/jakearchibald/idb/blob/654c746bef13f9fe7f5871e03ac58015cebace5a/test/open.ts#L118-L168 +[I-lifetime]: https://github.com/jakearchibald/idb/blob/654c746bef13f9fe7f5871e03ac58015cebace5a/README.md#L171-L198 +[I-values]: https://github.com/jakearchibald/idb/blob/654c746bef13f9fe7f5871e03ac58015cebace5a/test/main.ts#L147-L200 +[K-store]: https://github.com/jakearchibald/idb-keyval/blob/17a69a1165bef486d88950cb47d3913744f038ac/src/index.ts#L12-L37 +[K-batch]: https://github.com/jakearchibald/idb-keyval/blob/17a69a1165bef486d88950cb47d3913744f038ac/src/index.ts#L84-L100 +[K-tests]: https://github.com/jakearchibald/idb-keyval/blob/17a69a1165bef486d88950cb47d3913744f038ac/test/index.ts#L329-L433 +[K-update]: https://github.com/jakearchibald/idb-keyval/blob/17a69a1165bef486d88950cb47d3913744f038ac/test/index.ts#L435-L484 +[K-update-doc]: https://github.com/jakearchibald/idb-keyval/blob/17a69a1165bef486d88950cb47d3913744f038ac/README.md#L131-L163 +[K-errors]: https://github.com/jakearchibald/idb-keyval/blob/17a69a1165bef486d88950cb47d3913744f038ac/test/index.ts#L120-L172 +[D-close]: https://github.com/dexie/Dexie.js/blob/9282725a40bb659dca8e5971970243ef48238811/src/classes/dexie/dexie-open.ts#L140-L150 +[D-wait]: https://github.com/dexie/Dexie.js/blob/9282725a40bb659dca8e5971970243ef48238811/test/tests-transaction.js#L812-L909 +[D-errors]: https://github.com/dexie/Dexie.js/blob/9282725a40bb659dca8e5971970243ef48238811/test/tests-exception-handling.js#L48-L106 +[D-upgrade-errors]: https://github.com/dexie/Dexie.js/blob/9282725a40bb659dca8e5971970243ef48238811/test/tests-exception-handling.js#L179-L252 +[D-bulk]: https://github.com/dexie/Dexie.js/blob/9282725a40bb659dca8e5971970243ef48238811/test/tests-live-query.js#L809-L879 +[D-values]: https://github.com/dexie/Dexie.js/blob/9282725a40bb659dca8e5971970243ef48238811/test/tests-misc.js#L513-L537 +[D-names]: https://github.com/dexie/Dexie.js/blob/9282725a40bb659dca8e5971970243ef48238811/test/tests-misc.js#L539-L559 +[D-alias]: https://github.com/dexie/Dexie.js/blob/9282725a40bb659dca8e5971970243ef48238811/test/tests-live-query.js#L881-L920 +[D-worker]: https://github.com/dexie/Dexie.js/blob/9282725a40bb659dca8e5971970243ef48238811/test/tests-open.js#L245-L321 +[D-durability]: https://github.com/dexie/Dexie.js/blob/9282725a40bb659dca8e5971970243ef48238811/test/tests-chrome-transaction-durability.js#L14-L85 +[L-delete]: https://github.com/localForage/localForage/blob/fb78c77ee583fb727a558c439b5174aca38c5924/src/drivers/indexeddb.js#L960-L1022 +[L-reconnect]: https://github.com/localForage/localForage/blob/fb78c77ee583fb727a558c439b5174aca38c5924/src/drivers/indexeddb.js#L316-L402 +[L-history]: https://github.com/localForage/localForage/blob/fb78c77ee583fb727a558c439b5174aca38c5924/CHANGELOG.md#L1-L72 +[L-concurrent]: https://github.com/localForage/localForage/blob/fb78c77ee583fb727a558c439b5174aca38c5924/test/test.api.js#L1517-L1590 +[L-values]: https://github.com/localForage/localForage/blob/fb78c77ee583fb727a558c439b5174aca38c5924/test/test.datatypes.js#L325-L636 +[L-worker]: https://github.com/localForage/localForage/blob/fb78c77ee583fb727a558c439b5174aca38c5924/test/test.webworkers.js +[L-serviceworker]: https://github.com/localForage/localForage/blob/fb78c77ee583fb727a558c439b5174aca38c5924/test/test.serviceworkers.js +[L-iframe]: https://github.com/localForage/localForage/blob/fb78c77ee583fb727a558c439b5174aca38c5924/test/test.iframes.js +[L-isolation]: https://github.com/localForage/localForage/blob/fb78c77ee583fb727a558c439b5174aca38c5924/test/test.api.js#L984-L1034 +[L-drop]: https://github.com/localForage/localForage/blob/fb78c77ee583fb727a558c439b5174aca38c5924/test/test.api.js#L1938-L2119 +[L-browserify]: https://github.com/localForage/localForage/blob/fb78c77ee583fb727a558c439b5174aca38c5924/test/runner.browserify.js +[L-webpack]: https://github.com/localForage/localForage/blob/fb78c77ee583fb727a558c439b5174aca38c5924/test/runner.webpack.js + +## Parent evaluation and implementation disposition + +The source survey above is preserved against its frozen target. It supplied +primary-source mechanisms and explicit limits, independently reproduced the +home defect, and challenged a donor's advertised atomicity rather than treating +maturity as correctness. This is strong, useful sighted research; it is not an +exhaustive audit or a blind selection experiment. I would use this reviewer for +another bounded source comparison. Donor suites were not executed. + +| ID | Parent disposition | Evidence or receiving destination | +| --- | --- | --- | +| DP01 | fixed-now | New wrapper-settlement oracle: 12 original-source assertion failures, 8 controls; additive native listeners preserve wrapper and application observers. | +| DP02 | design-decision | Native abnormal close remains a separate existing ownership boundary; no automatic reopen adopted. | +| DP03 | accepted-design | Blocked calls remain pending; diagnostics are an optional API proposal. | +| DP04 | already-covered | Existing wrapper callback/native outcome cases pass; broader browser callback-lifetime evidence remains explicitly bounded. | +| DP05 | fixed-now (test gap) | Canceled and uncanceled native request errors now reach the independent settlement model; rejecting every request error fails the committed-neighbor assertion. | +| DP06 | already-covered | First/middle/last rollback and durable-prefix checks; donor negative-control probe preserved. | +| DP07 | already-covered | Failed-upgrade retained schema/rows and successful suffix checks. | +| DP08 | confirmed-open coverage gap | Compatibility owner: simultaneous initial opens with identical complete declarations; native receiving witness needed. No product counterexample reported. | +| DP09 | confirmed-open coverage gap | Persistence owner: independent structured-clone type/byte corpus across unrelated updates, peers and restore. No product counterexample reported. | +| DP10 | confirmed-open coverage gap | Compatibility owner: metamorphic legal database/store renaming and metadata isolation. No product counterexample reported. | +| DP11 | accepted-design | Approved ordering is within one Collection; atomic cross-Collection read-modify-write would be a new API. | +| DP12 | design-decision | Rich mutable-value ownership remains separate from detached scalar oracle expectations. | +| DP13 | design-decision | Worker support and a native worker receiving lane require an explicit host scope. | +| DP14 | already-covered | Existing store, descriptor, DbClient, version and administrative-deletion isolation laws. | +| DP15 | refuted transfer | Reconnect/backend fallback conflicts with the approved sticky managed-closure contract. | +| DP16 | accepted-design | Native commit is the current receipt; physical-crash durability and hints are separate claims. | +| DP17 | confirmed-open coverage gap | Package consumer owner: disposable packed ESM/CJS runtime and browser-bundler consumption. Current 12 cases are declaration evidence. | +| DP18 | already-covered | Exact native cause identity and approved removal of unused errors. | + +Accounting: 18 = 2 fixed-now + 5 already-covered + 3 accepted-design + +3 design-decision + 4 confirmed-open coverage gaps + 1 refuted transfer. +There are no deferred items. The four new coverage proposals have not been +implemented or claimed green in this research task. No new affordance or recovery +policy was adopted. This distinguishes completed donor research from completion +of all possible follow-up work it identified. + +`wrapper-settlement-oracle.test.ts` contains 20 observer histories and two +request-error neighbors. Registration syntax is absent from its two-fact outcome +model. Each case checks actual terminal events, independently read durable rows, +caller state after event continuations, callback release and application observer +calls. Callback-rejection identity and late rejection after commit retain the +existing wrapper owner. The repair adds two net production lines and no state. +The obsolete utility-deletion paragraph in ORACLE.md was also corrected to the +already implemented administrative contract. + +The [donor evidence directory](https://github.com/TanStack/db/blob/main/review-evidence/donor-survey/README.md) +retains probes and RED/GREEN receipts. The provider limit for the new observer +matrix is fake-IDB; the existing native browser suite is a separate regression +check, not a claim that all 22 new histories were run in browsers. + +Final regression checks after the observer repair: 2,428 adapter runtime/type +cases, all 156 native browser cases, changed-file lint, formatting and whitespace +checks passed. The earlier merged-core 300 cases and 12 declaration consumers +remain separate receipts; this repair changes neither core nor exported types. + + +## Receiving update: obvious coverage port + +The prior disposition table is the frozen research result. The subsequent +[port audit](2026-10-06-indexeddb-donor-port.md) records implementation of DP08, +DP09, DP10 and DP17, with three assertion-killed production mutants and packed +resolver controls. DP09's native WebKit Blob-preservation cells remain unproved: +the local raw provider rejects Blob preparation, so those cells now establish +truthful rejection and a healthy suffix. No new recovery or ownership policy was +adopted. DP02, DP12 and DP13 still require the stated product decisions. + + +## Approved follow-up + +The later [follow-up audit](2026-10-06-indexeddb-donor-followup.md) records the +approved closure/capture/worker contracts, optional blocked diagnostics, core +typed-key repair, and completed native naming and persistent-WebKit Blob evidence. +The earlier findings above retain their original revision and evidence boundary. diff --git a/docs/contributing/oracle-reviews/2026-10-06-indexeddb-tla-refinement.md b/docs/contributing/oracle-reviews/2026-10-06-indexeddb-tla-refinement.md new file mode 100644 index 0000000000..95f64a11c4 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-06-indexeddb-tla-refinement.md @@ -0,0 +1,173 @@ +# IndexedDB TLA+ refinement and loss audit + +The maintainer requested executable translation, a loss audit, assertion-level +RED against production, and implementation. This record supersedes the open +XH-07 design disposition in the October 5 law audit. The selected contract is +**finish admitted writes and preserve accepted sync work, while closed managed +Collections remain errored**. Database deletion is administrative by name and has +no Collection row-publication authority. No database identity is persisted. + +## Frozen sources and oracle owners + +Formal sources, unchanged from the executed TLC run, are +`review-evidence/deletion-tla/retirement_oracle.tla` and +`review-evidence/deletion-tla/deletion_queue_oracle.tla`. The finish graph had +333,984 distinct states; the deletion graph had 22. The original 21 TLC +configurations include deliberate domain faults, challenged stronger claims, and +checker controls. Their hashes/results remain with those artifacts. + +Three executable owners translate those laws: + +- `packages/indexeddb-db-collection/tests/retirement-oracle.test.ts`: independent + authored row fold, native outcomes, per-Collection confirmation, caller-time + source snapshots, native-success observation and complete retired status traces. +- `packages/indexeddb-db-collection/tests/deletion-queue-oracle.test.ts`: native + name targeting and deletion receipt authority, separately observed from caller + delivery, retained public rows and fresh storage. +- `packages/db/tests/truncate-readiness-oracle.test.ts`: independent replacement + and readiness intents across loading/ready/error, held optimistic work, and + repeated/reentrant replacements. Default truncate still recovers readiness. + +The native receiving owner is +`packages/indexeddb-db-collection/e2e/cross-tab-oracle.spec.ts`. The driver records +actual IDB transaction completion/abort and source rows at the caller callback. + +## Loss audit and disposition + +The first oracle draft was frozen before two fresh scanners received one formal +source apiece, with sibling sources hidden. Their unedited source reports and +frozen inputs are retained under `review-evidence/deletion-refinement/`. The +following dispositions are subsequent implementation work, not scanner claims. + +| Recovered distinction | Resolution | +| --- | --- | +| R1: joint final observations lose caller-time truth | Capture base rows and native state inside each caller continuation. Successful settlement requires an already observed native commit. | +| R2: separate scenarios lose mixed operation phases | Cross two different Collections' deciding/rejected/native-active/accepted/settled prefixes, all mutation paths and three closure reasons. Mixed active work also commits or aborts; the separate active-pair matrix crosses both native orders and all outcome pairs. | +| R3: rejection only after closure | Add open-connection rejection prefixes for insert/update/delete, including accepted or active siblings. Utility paths explicitly have no asynchronous user-handler decision. | +| R4: no-ready is weaker than always-error | Record the full status suffix beginning at retirement, including late startup. Every later state must be error. | +| R5: native admission before error notification | Reenter from a public error-status listener with a utility call and late Collection startup. Neither may admit native work. | +| R6: eventual rows can hide false publication | Record authoritative base at each pair-publication event and compare it against native commitment. Keep caller-time source and raw durable checks. Add initial restore overlapped with an admitted local insert. | +| L1: late invocation does not test queued name targeting | Issue delete A / open B / delete B while A still exists. B receives the second native deletion. Keep this queue-target projection separate from the model's combined settled Recreate abstraction. | +| L2: delayed callbacks mask premature native success | Record native success independently from callback gates; check both while unmanaged blockers remain. Transfer the transaction-only hold to real engines because fake-indexeddb violates it. | +| L3: release-before-announcement omitted | Add a prefix completing both old obligations before deletion, and retain ordinary unblocked managed upgrade/delete paths. Native release order is also crossed independently. | +| L4: receipt order and transient observations omitted | Add old receipt after B's native success but before B's caller, retain the reverse receipt order, and record every status/publication on A and B. | +| R/L calibration omission | Execute ten production mutants; each fails an assertion. Original-source and native RED evidence is retained separately. | + +These repairs preserve every modeled product law and give each transition a +production path/checkpoint. They do **not** enumerate every interleaving in the +333,984-state graph. The suite declares its finite history language instead of +claiming that a transition checklist or random green exhausts that graph. + +## Formal-to-executable mapping + +| Formal action or law | Executable receiving observation | +| --- | --- | +| Close(upgrade/delete/explicit) | Managed factory versionchange or public descriptor close; all affected status observations become error before application reentry can admit work. | +| FinishRead; ReadAuthority | Held initial, replacement and targeted native reads finish after closure without source publication or ready. Startup/read crossed with admitted local write separates the obligations. | +| RegisterLate / FinishLateStartup | Actual `preload()` on a closed descriptor rejects, emits error, and admits no native transaction. | +| Decide / Admit; AdmissionAuthority | Real automatic handlers resolve/reject before/after closure; utility rejection and reentrant late startup are separate public paths. Native entry recording counts successful transaction admission. | +| FinishNative; NativeAdmissionAccounting | Actual queued IDB transactions, both admission orders, FIFO commits and independently aborted queued writes. Each accepted entry reaches one terminal native observation. The oracle does not duplicate the adapter's queue. | +| Confirm / FinishHandler | Ordinary manual acceptance leaves base queued while the handler persists; closure preserves it. Utility replacement publishes committed rows without changing error status. | +| SettleCaller; TruthfulSettlement | Capture source at callback entry. Success requires commit; rejected operations never contribute a committed durable effect. Native abort disposition and abort-event delivery are distinct (below). | +| AcceptedWorkIsAccountedFor / AcceptedWorkPrecedesCaller / FinishPolicyConfirms | Queued base before handler completion, exact authored base at caller success, settled public rows and independent raw storage; mixed siblings cannot discard another owner's accepted work. | +| OnlyCommittedWritesPublish | Every observed base publication must correspond to that operation's native commit; optimistic public rows are allowed before it. | +| ClosedConnectionsDoNotBecomeReady | Entire error suffix, not merely the final status, for ordinary/replacement/read/late-startup paths. Core positive controls preserve normal readiness recovery. | +| AnnounceDelete / FinishOldTransaction / ReleaseBlocker / FinishDelete | Native versionchange, held actual transaction, actual unmanaged handles, independently recorded native success; both release orders received in three engines. | +| Recreate | Native open/seed/Collection restore plus unmanaged B blocker. Queue-target projection separately covers both delete invocations while A exists without imposing artificial restore scheduling. | +| DeliverReceipt / CallerWaitsForNative | Actual wrapper callback can be delayed after native success; pending caller and native completion are independent checks. Both receipt orders and intermediate cuts are retained. | +| RecreatedRowsSurviveOldReceipt / ClosedConnectionsStayErrored | Exact B public and raw rows while its delete remains blocked; every A/B status and row-publication event; retained old snapshots after success and fresh empty restore. | +| TypeOK | TypeScript types and validated finite inputs provide model integrity. Ghost lifetime identifiers and internal formal queue fields are not added to production. | +| Conditional progress | Cooperative gates are released/rejected and all caller outcomes awaited. Held uncooperative decisions stay pending. No finite test or timeout proves unconditional browser liveness. | + +The rejected stronger claims stay rejected: accepted publication may occur after +close; suppressing new confirmations cannot preserve finish-policy success +observations; delete-by-name is not bound to an old descriptor lifetime; arbitrary +uncooperative handlers/blockers need not finish. They are not missing features. + +## Two abstraction corrections discovered by execution + +1. A successful native `abort()` makes commit impossible before its asynchronous + abort event is delivered. The wrapper's callback rejection may precede that + event. The formal aborted disposition maps to irreversible rollback, not an + event-delivery deadline. The oracle requires native commit before success, + excludes commit on rejection, and independently awaits the final abort event + and unchanged durable rows. This correction follows the formal truth law and + IDB semantics; it does not accept a committed write reported as failed. +2. fake-indexeddb 6.2.5's `waitForOthersClosedDelete` treats close-pending handles + as closed. The strengthened oracle observed native deletion success while its + transaction was still active after the unmanaged blocker closed. The failing + provider trace is preserved. The law remains unchanged and is enforced in + Chromium, Firefox and WebKit for both release orders. Controlled tests retain + the unmanaged blocker until native work ends. No production workaround was + added for the nonconforming test provider. + +## RED, implementation, and calibration + +Before production edits, 884 adapter cases failed at intended assertions; the +core readiness matrix had 12 failures and 42 positive controls. Chromium also +failed the admitted-import/explicit-close readiness assertion. Later audit +expansions increase the history language; the original RED counts are not claims +that every added history was run against the old adapter. + +Implementation removes `utils.deleteDatabase` and `database-deleted` transport +messages. The exported low-level administrative function remains. Managed +connections record closure before notifying current Collections. Read callbacks +are fenced by connection closure, while admitted write confirmation and accepted +core sync work survive. Core adds `truncate({ markReady: false })`, with normal +recovery as the default and the last replacement supplying a batch's intent. +The docs, types, test scripts and changesets carry the same contract. + +The final replayable calibration runner injects and restores these faults: +missing closure notification, late-read publication, truncate readiness, +late-startup readiness, admission after close, notification before native close, +discarding accepted work, rejection after native commit, premature success, +and admitting the obsolete deletion protocol. All ten fail assertions rather +than setup, parser or timeout errors. The native-ignore-blocker formal control +is a provider-contract fault; its real receiving law is tested in browsers and +its fake-provider violation is reported above. The original stale-receipt bug +was reproduced before removal of its publication path, in controlled and all +three native engines; no stored identity was needed to remove that authority. + +## Validation and bounds + +Final validation receipts are retained under `review-evidence/deletion-refinement`. +The adapter suite, related core publication/settlement tests, external declaration +consumers, source typecheck, lint, formatting and native engine results are listed +in `validation.json`. The browser baseline passed 138 cases; its 18 clear/import +abort cases used an overstrong event-at-rejection assertion. All 18 passed after +the abstraction correction above, giving 156 covered native cases across the +three engines. Tests for both transaction/blocker release orders passed in each. + +Vitest initially repeated one stale source diagnostic from its shared incremental +cache although standalone TypeScript passed. Clearing only its generated +`tsconfig.tmp.tsbuildinfo` produced a clean 24-case type-only run. No source type +contract was weakened to silence it. + +Relative to pushed `34eb98b16`, production changes add 12 net core lines and +remove one net adapter line: **11 net production lines**. Tests, executable +models and documentation are counted separately. This includes the prior pending +adapter metadata cleanup; no retry queue, global deletion coordinator or persisted +database epoch is introduced. + +XH-07 is closed for the declared administrative deletion and managed closure +language. The original 15-item ledger has no deferred item: XH-06's contract was +implemented and documented, XH-07's authority was removed and tested, and the +other thirteen retain their prior fixes and expanded laws. Cleanup/restart of +pending mutations, post-durable auxiliary failures, arbitrary same-key ordering +between Collections, lost notifications, quota/eviction and physical crash remain +separate pre-existing boundaries, not claims made by these formal models. + +## Main integration verification + +The implementation was committed as `e6a2b8b48`. GitHub then reported a conflict +with current main, so the published branch received a normal merge of +`65992aacda530fde89f4c8ffa803b60cd708c425`. The only textual conflict was the core oracle +script; both branches' registrations remain. Main's sync-entry readiness-error +change merged alongside the explicit truncate readiness intent. + +The merged tree passed all 2,406 adapter runtime/type cases, 300 related core +cases including the sync-entry reentrancy owner, all 156 native browser cases in +one run, and all 12 published-declaration consumer tests after fresh builds. +Formatting and changed-core lint also passed. `validation.json` retains the +merge-parent identities, final source hashes and separate receipts without +rewriting the earlier RED or calibration evidence. diff --git a/docs/contributing/oracle-reviews/2026-10-06-nested-begin-metadata-rebuild.md b/docs/contributing/oracle-reviews/2026-10-06-nested-begin-metadata-rebuild.md new file mode 100644 index 0000000000..d5485f3339 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-06-nested-begin-metadata-rebuild.md @@ -0,0 +1,168 @@ +# Metadata rebuild after a nested transaction + +Base revision: `65992aacd` (`main`). Found while triaging state mutation round 4. + +## Law + +Row metadata in one sync transaction follows last write wins, by the rules in +`collection-row-metadata-composition-oracle.test.ts`. The rules apply to the +write that applies. When the Collection reclassifies an insert, metadata +follows the new classification. + +## The failure + +A transaction begun inside an open transaction can commit first. The open +transaction then applies after it, and the Collection rebuilds the open +transaction against the new projection. The rebuild reclassifies each insert: +an insert of a present key with an equal value is a re-insert, and a re-insert +of an absent key is an insert. + +```ts +sync.begin() // T1, row 1 is present +sync.metadata.row.set(1, { m: `w0` }) +sync.write({ type: `insert`, value: { id: 1, v: 0 } }) // equal re-insert +sync.begin() // T2 +sync.write({ type: `delete`, key: 1 }) +sync.commit() // T2 applies first, so T1's insert is now an insert +sync.commit() // T1 +sync.metadata.row.get(1) // main: { m: `w0` }; expected: undefined +``` + +The reverse direction also fails. From an absent row, T1 sets metadata and +inserts the row. T2 inserts the same row with metadata and commits first. +T1's insert becomes a re-insert, which keeps the set value. `main` reads +`undefined`. + +**Cause.** `rebuildAutomaticRowMetadataWrites` skipped every key with an +explicit write. It kept the write that was current when T1 wrote, which no +longer follows from T1's operations. + +**Repair.** Each explicit write records how many operations came before it. +The rebuild clears the key's writes, restores the last explicit write, and +replays the automatic write of each later operation. Hydrated metadata +records a position after every hydrated row, so it still holds. + +## Why the oracles missed it + +The round 3 record called RB2, a mutant that keeps stale automatic writes in +the rebuild, equivalent within legal histories. The follow-up record then said +that #2030 made it unreachable, because only the open last transaction can be +canceled. Both records missed nested transactions. The `invalidationError` +comment on `PendingSyncedTransaction` names the history: a later transaction +begun inside an open one commits first. The metadata oracle's rebuilt lane +changes the projection only through an earlier transaction, which cannot +reclassify an insert that is still open. + +## Witness + +A nested lane in the metadata composition oracle opens a transaction, T1. T1 +writes up to three of `set`, `unset`, `truncate`, and an insert, update, or +delete of one fixed row value, each with or without metadata. Then a nested +write applies before T1 commits: + +- From a present row, a nested transaction deletes the row. +- From an absent row, a nested transaction inserts the same row with its own + metadata. +- From an absent row, a hydration seed inserts the same row with its own + metadata. The Collection places a seed ahead of the first open transaction. + +A nested transaction can also call `metadata.row.set` after its row write. A +history is legal when T1's writes are legal both where T1 writes them and +where they apply. + +The model reclassifies T1's inserts by the row's presence where they apply, +and folds T1's writes over the value that the nested write leaves. It reads no +production state. The lane runs 1,925 histories: 275 for each of seven +variants. + +| Revision | Nested lane (of 1,925) | +| ----------------------------------------------------- | --------------------------------------------------------------- | +| `main` | 35 fail: all three nested writes, with and without a nested set | +| this fix | pass | +| explicit write always wins (M1) | 279 fail; the rebuilt lane also fails | +| keep stale automatic writes (M2, RB2) | 21 fail | +| explicit position recorded as 0 (M4) | 267 fail; the rebuilt lane also fails | +| hydrated metadata recorded at position 0 | 7 fail, all hydration seeds | +| explicit write recorded in the first open transaction | 818 fail, all with a nested set | +| truncate keeps explicit positions | 63 fail; the immediate, held, and rebuilt lanes also fail | + +The hydration mutant also fails `DbClient > hydrates pending collection rows +when the collection materializes`. + +## Review outcomes + +A medium code review found no correctness bug and seven items. A simplifier +pass found one item, the same as item 5. + +1. `explicitRowMetadataWrites` was optional, though every constructor supplies + it. Fixed: the field is required, and two test fixtures now supply it. +2. `metadata.row.set` still writes to a transaction that a replay has + invalidated, while `write` ignores it. This predates the fix, and the commit + discards the transaction. Open: it belongs with round 4's SY8 survivor, + writes after invalidation, in the round 4 gaps change. +3. Hydration could carry metadata on its operations instead of a parallel + map. Not adopted: an insert without metadata would then clear metadata + that a source kept for an absent key, which changes hydration. +4. An ordered log of metadata writes would avoid positions. Not adopted: a + transaction only appends operations, and a truncate clears both lists + together. +5. Hydration and the new helper spelled out `PendingMetadataWrite`. Fixed. +6. The exported type was not formatted. Fixed. +7. The nested lane had no timeout, unlike the other lanes. Fixed. + +## Second review + +A second medium code review, of #2048 at `9e34d6c94`, found no correctness +bug and seven items. + +1. `metadata.row.set` writes to an invalidated transaction. The same as item + 2 above. Open, with round 4's SY8. +2. T1 never wrote an update, a delete, or a truncate. Fixed: the lane + generates them where they are legal in both places. +3. No lane reached a hydration seed ahead of an open transaction. A probe + showed that `main` reads `undefined` where last write wins expects the + explicit set. Fixed: the seed is a nested write. +4. A nested transaction made no explicit metadata call, so a mutant that + writes to the first open transaction survived. Fixed: the nested set. +5. The rebuild rescans every operation of every queued transaction. `main` + has the same loop, and this change adds no rebuild. No change. +6. Hydration builds its explicit map in a second pass. A one-loop version + added seven production lines to save one copy per chunk. Not adopted. +7. The fix adds production lines against the bug-fix budget. A position-only + map still needs the explicit value, so no smaller design was found. + +## ORC outcomes + +- **ORC-001: met.** The contract is the metadata oracle's opening prose. Its + limits now state that the rules follow the reclassified write. +- **ORC-002: met.** `reclassify` derives each insert's kind from the row's + presence. It does not call `classifyProjectedInsert`. +- **ORC-003: met.** The lane's grammar, model step, and driver step each have + prose beside them. +- **ORC-004: met.** The lane is exhaustive within its bound. A count control + checks 1,925 histories, named witnesses cover each variant, and two + exclusions check the legality rule. Every row write uses one value, so no + reclassified insert is a duplicate. +- **ORC-005: met.** The driver writes through a real Collection's sync API and + reads `metadata.row.get`. +- **ORC-006: met.** See the table above. +- **ORC-007: not applicable.** The lane is exhaustive. It has no random + campaign. +- **ORC-008: not applicable.** The model gains no state. +- **ORC-009: met.** "Nested transaction" means a sync transaction begun while + an earlier one is open. It is the history that `invalidationError` names. +- **ORC-010: met.** The driver cleans up each Collection in a `finally` + block. +- **ORC-011: not applicable.** No reviewer named a shared fault. +- **ORC-012: met by this record.** +- **ORC-013: met.** Each mutant in the table fails a distinct part of the + lane. The hydration and first-open-transaction mutants fail only the seed + and nested-set variants. +- **ORC-014: not applicable.** + +## Limits + +The lane covers one key. A nested truncate, a nested update, or a nested +write to another key is not generated. A nested insert with a different value +makes the open transaction a duplicate. The sync reentrancy oracle owns that +invalidation. diff --git a/docs/contributing/oracle-reviews/2026-10-06-state-mutation-round-4.md b/docs/contributing/oracle-reviews/2026-10-06-state-mutation-round-4.md new file mode 100644 index 0000000000..4171a92d80 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-06-state-mutation-round-4.md @@ -0,0 +1,193 @@ +# State stack mutation round 4 + +Revisions: + +- Mutation campaign: the 53 mutants ran on `de8d0f1bf` (`main` after #2040). +- Witnesses: the first versions ran on `2376eb581` (`main` after #2048). The + first review's fixes ran on `d57be3d6f`, and its merge of `main` on + `5a24b9025`. +- Second review: its fixes are in `7bc58cfab`, on `5a24b9025`. +- Third review: its fixes are in the commit that adds this line, on + `7bc58cfab`. The X7 results ran on that commit. + +This branch changes no production code. + +## Method + +Round 4 applied 53 plausible maintenance mistakes to unchanged `main`: 23 in +`state.ts`, 15 in `sync.ts`, 6 in `mutations.ts`, 4 in `lifecycle.ts`, 3 in +`sync-receipt.ts`, and one each in the lazy `state` getters of +`live-query-observer.ts` and `live-query-window-controller.ts`. Each mutant +ran against the full `@tanstack/db` suite with `--bail=1`. Of the 53, 44 +failed the suite and 9 survived. Each survivor received a probe that runs on +`main`, a code argument, or full-suite instrumentation. + +Triage also found a bug on `main`. A sync transaction begun inside an open +one could commit first and make the rebuild lose last-write-wins row +metadata. #2048 fixes it, and its record corrects the round 3 claim that the +history was unreachable. + +## Survivors + +| Mutant | Change | Verdict | +| --- | --- | --- | +| SY8 | `write` stages writes into a transaction that a replay invalidated | Gap. Closed here. | +| SY13 | A stale sync run's `commit` returns `false` | Gap. Closed here. | +| X3 | A receipt with no acceptance moment counts as accepted at once | Gap. Closed here. | +| X4 | The observer snapshot builds a new `state` map on every read | Gap. Closed here. | +| X5 | The window snapshot copies `state` on every read | Gap. Closed here. | +| ST27 | Dropping a held row keeps its pending local origin | Equivalent. The branch is unreachable. See below. | +| ST16 | Recompute copies `rowOrigins` instead of keeping a reference | Equivalent by argument. Recompute writes no origin before it reads the reference. | +| ST17 | The origin snapshot stores `undefined` for keys without an origin | Equivalent. Every reader treats a missing origin as `remote`. | +| SY18 | A missing `loadSubset` returns `false` | Equivalent. The caller resolves a non-promise result the same way. | + +## Gaps closed here + +**Writes after invalidation (SY8).** After a replay invalidates the open +transaction, `write` returns early. Without that guard, a late write enters +the pending projection, and a newer transaction classifies its own inserts +against it. A probe on `main` showed the effect. A late insert of key 9 made +a newer, valid insert of key 9 throw `DuplicateKeySyncError`, and row 9 was +lost. Two witnesses in `collection-state-retention-oracle.property.test.ts` +write a late insert and a late delete. The newer transaction must see only +the accepted row. Both fail under SY8. + +**A stale run's commit (SY13).** The retention oracle's lifecycle driver +committed from an old run only while that run was still current. Now the old +run also writes and commits a row after cleanup ends it, at two points: after +a plain `restart`, and while a reentrant restart's new transaction is still +open. The commit must return `true`, and the row must not appear. The fixed +and random retention campaigns fail under SY13. A mutant that routes a stale +run's write into the new run's open transaction fails both pinned +`afterOldReturn` histories, because the row appears. After a plain restart no +transaction is open, so there a stale write fails only by throwing. + +**Acceptance of an abandoned receipt (X3).** A commit that core never +accepts, because its signal was aborted or a replay invalidated it, returns a +bare receipt with no acceptance moment. `whenSyncAccepted` must then wait for +that receipt, which rejects. The optimistic-history oracle's aborted batches +and the retention oracle's invalidation replay now check that the acceptance +moment rejects too. Under X3, the invalidation replay, the open-at-settlement +histories, and the fixed and random optimistic-history campaigns fail. X3 also +reaches every adapter that calls `whenSyncAccepted` on a core receipt: +SQLite persistence, Query DB, Electric, and PowerSync. Under X3 those callers +would treat an abandoned write as accepted. This review did not run their +suites under X3. The core witnesses above own the fallback rule. + +**State identity (X4, X5).** Before #2043, `state` was a field of the +snapshot. The lazy getter must keep that identity: one snapshot has one +`state` map. Consumers that compare by identity, such as a React dependency +list, rely on it. The snapshot oracle now checks that a second read of `data` +and `state` returns the first read's objects. Five of its six tests fail +under X4. The sixth is the calibration test, which replaces the `state` +getter. `data` is a plain field today, so its identity check has no +demonstrated kill. It guards a future lazy `data`. A witness in +`live-query-window-controller.test.ts` checks the window snapshot and fails +under both X4 and X5. + +The second review found X6, which shares one map per observer and refills it +on every read. Each read then looks right on its own, so X6 passed. Now the +oracle saves every map it reads and checks each one again after all reads. +Snapshots with different rows must also have different maps. The window +witness fetches a page and checks that the newer revision has its own map and +the older map is unchanged. Under X6, five of the six snapshot tests and the +window witness fail. + +A third review found X7, a single cache entry on the observer. It builds a +new map whenever a different snapshot is read, so a later read of an earlier +snapshot returns a new map with the same rows. X7 passed every check above. +Now the oracle reads each snapshot again after all reads and requires the map +that its first read returned. The window witness reads the older snapshot +again after the newer one. Under X7, the same six tests fail. + +## ST27: an unreachable branch + +`recomputeOptimisticState` keeps a completed optimistic row only while a +committed, queued sync transaction touches its key. It drops a held row whose +key no longer has such a transaction, and ST27 skips the origin delete in that +branch. A committed transaction leaves the queue only by applying, and the +commit path then clears the held row and the pending local origin for each of +its keys. A cleanup reset clears both maps. So no reached history takes the +branch. Full-suite instrumentation agrees. A log write in the branch fired 0 +times across 249 files and 8,578 tests. A log write at the loop entry fired 55 +times in two of those files. The branch is a code-weight candidate. Its +removal belongs with the cuts, with this argument as its evidence. + +## Other finding + +The window snapshot's `state` is the observer's `state`, so it includes the +peek-ahead row that `data` omits. `useLiveInfiniteQuery` in React returns that +map. This predates #2043. This review does not decide whether `state` should +match `data`. + +## Review + +A medium code review found eight items. Each is fixed here. + +1. A commit hook's ESLint run removed a type assertion from the snapshot + oracle, and `tsc` then failed. The setup now passes type arguments. +2. The record said X3 cannot reach the persistence packages. It can reach + every adapter that calls `whenSyncAccepted`. Corrected above. +3. The window witness did not clean up in a `finally` block. Fixed. +4. The stale-run row check could not fail on its own after a plain restart. + The driver now also writes from the ended run while a new transaction is + open, and a routing mutant fails there. +5. The `data` identity check has no demonstrated kill. Stated above. +6. Reentrant restarts never reached the stale commit. Fixed with item 4. +7. The snapshot oracle has six tests, not five. Corrected. +8. The SY8 witnesses did not check their premise. They now check that the + open transaction is invalid before the late write. + +## Second review + +A second code review, of `5a24b9025`, found two items. + +1. The record named the tested revision as "this branch on `2376eb581`", + but later commits changed the witnesses. Fixed: the revisions above name + each run. +2. X6, a map shared across snapshots, passed the identity checks. Fixed: the + saved-map and cross-revision checks under "State identity" above. + +## Third review + +A third review, of `7bc58cfab`, found one item. The saved-map checks did not +read a snapshot again after another snapshot was read, so X7 passed. Fixed: +the identity checks under "State identity" above. + +CodeRabbit then asked for the same recheck of `data`. The oracle now saves +each first read's `data` too and requires it on the later read. `data` is a +plain field today, so this check has no demonstrated kill. + +## ORC outcomes + +- **ORC-001: met.** SY8 follows the comment on the guard: a commit receipt + owns the invalidation, and later writes cannot revive it. SY13 follows + the stale-run rule in the retention oracle's opening prose, which now + states the commit result. X3 follows the `whenSyncAccepted` contract. X4 + and X5 follow the snapshot oracle's point-in-time contract, which now + states identity. +- **ORC-002: met.** The witnesses expect fixed outcomes or use the existing + independent models. +- **ORC-003: met.** Each change has prose beside its code. +- **ORC-004: met.** The `restart` and `reentrantRestart` actions are in the + existing generated grammar. Every plain restart and every `afterOldReturn` + reentrant restart reaches the stale commit. An `insideListener` reentrant + restart commits the new run inside the listener, so no open transaction + remains for a stale write to reach. The other changes add observations, + not grammar. +- **ORC-005: met.** Each witness drives a real Collection, observer, or + window controller and observes public results. +- **ORC-006: met.** Each closed mutant fails the tests named above. +- **ORC-007: met.** The retention and optimistic-history oracles run fixed + and random campaigns with seed and path replay. +- **ORC-008: not applicable.** No model gained state. +- **ORC-009: met.** "Acceptance moment" is the value `whenSyncAccepted` + returns. +- **ORC-010: met.** Each witness cleans up its Collection, live query, or + controller in a `finally` block. +- **ORC-011: not applicable.** +- **ORC-012: met by this record.** +- **ORC-013: met.** The SY8 witnesses cover both directions of the leak: one + makes a valid insert throw, and one lets a duplicate insert pass. Each + first checks that the replay already invalidated the open transaction. +- **ORC-014: not applicable.** diff --git a/docs/contributing/oracle-reviews/2026-10-07-deferred-acquisition-review.md b/docs/contributing/oracle-reviews/2026-10-07-deferred-acquisition-review.md new file mode 100644 index 0000000000..943c3a6cc2 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-07-deferred-acquisition-review.md @@ -0,0 +1,67 @@ +# Deferred acquisition review, 2026-10-07 + +Scope: PR #2060 (`issue-2023-network-on-subscriber`) at `19588268c`, reviewed by +an external high-effort code review. The repair commit that adds this record +sits on top of that revision. Owner: +`packages/db/tests/live-query-deferred-acquisition-oracle.test.ts`. + +## Law + +Normative law 15 in `packages/db/src/query/live/ARCHITECTURE.md`. Authority: +the maintainer's decision that a live query starts no network work until it +has a subscriber or a preload, with preload counting and eager sources +included. This review sharpened the formulation: the subscriber must ask for +data, a subscriber that survives cleanup still counts, and a read that waits +for readiness counts as a preload. + +## Findings and evidence + +| Finding | Kind | Evidence | Outcome | +| --- | --- | --- | --- | +| A source status change restarts deferred demand | Defect | New `peer` command: a peer that starts an idle on-demand source made the live query acquire, 4 cases RED at the acquisition-count assertion | Guard in `restartDetachedDemands`; removing it alone fails the same 4 | +| A truncate reloads deferred demand | Defect | New `truncate` command: 8 cases RED at the acquisition-count assertion | Guard in `handleTruncate`; removing it alone fails the same 8 | +| Cleanup resets the flag while an acquiring subscriber survives | Defect and formulation | Pinned block: 4 on-demand cases RED, no acquisition after restart, status stuck at `loading` | Cleanup keeps the flag when a surviving subscription does not defer. "Always reset" fails the block; "any surviving subscriber" fails the deferring-survivor control | +| A deferring subscription raises the source's subscriber count | Concept question | A Query Collection probe: the count went 0 to 1, but no refetch happened | No change; harm not reproduced. Whether `subscriberCount` should count deferring subscriptions stays open | +| A resume that throws leaves later sources deferred | Mechanism confirmed | Probe: the subscribe throws the first source's error, the live query enters `error`, and the second source stays idle | No change. The live query has already failed, the deferral is per subscription so another reader starts the source itself, and the remaining trigger is a throwing listener, a contract breach | +| `toArrayWhenReady()` / `stateWhenReady()` never resume | Defect | Pinned block: 2 cases RED, no acquisition after the read | The early-return branch marks a preload | +| The first commit is loading under the async resume | Accepted design | Maintainer decision: loading first unless the collection is preloaded | No change | +| The changeset is `patch` | Accepted design | Maintainer decision | No change | +| A stale comment promises a ready first commit | Maintainability | Source inspection, React and Svelte | Comments rewritten | +| The deferral expression is duplicated | Maintainability | Source inspection | One helper | + +`preloaded-collection-first-paint` previously rejected the "preload does not +count" mutant only by timing out. It now bounds the preload, and the mutant +fails the assertion `the preload settles` in React, Vue, and Svelte. + +The mutants above were designed after reading the tests, so they are a +self-review of this author's coverage, not an independent mutant gap hunt. + +## Second review, at `c2fe18d21` + +A medium code review raised ten findings. Each claim was probed on that +revision before any change. + +| Finding | Kind | Evidence | Outcome | +| --- | --- | --- | --- | +| A deferring subscription raises the source's subscriber count, so a Query Collection refetches | Defect, open | With the reviewer's precondition (the last direct subscriber left first), a live query with `startSync: true` and no subscriber raised the count from 0 to 1 and the Query Collection fetched again. The first review's probe had no prior subscriber | Maintainer decision: they count only subscribers that ask for data, while garbage collection still counts every subscription. A pinned oracle block and a Query Collection test fail without the change; ten tests that read `subscriberCount` to watch an abandoned render or orphan hold its source now observe the hold another way: db tests read the retention count, and React tests give the source a 1 ms `gcTime` and check that it survives while held and is reclaimed after | +| A resume that throws strands the other sources and makes `preload()` throw | Defect | Pinned block: `preload()` threw synchronously instead of returning a rejected promise | Listeners all run before the first error is rethrown; the preload entry points reject. Each half fails the block alone | +| A failed resume clears the subscription's deferring flag | Defect by source inspection | Observable only when the failing source is itself a live query that is later cleaned up; no runtime witness | The source starts before the flag clears | +| A truncate with retained stale rows stalls publication | Refuted at the precondition | Retained stale rows come only from source cleanup, which puts the dependent live query in terminal error first | No change | +| A cleaned-up Suspense collection gets no in-render preload | Pre-existing defect | The rerender showed no rows on this PR and on `main` | The in-render preload covers `cleaned-up`; the new React witness fails without it | +| The first-value helper accepts any not-ready value | Weak assertion | Source inspection | The not-ready value is now exact: `loading` when the hook started the collection, `idle` for a supplied window it adjusts first | +| `hasSubscriberOrPreload()` falls back to `true` | Refuted: reachable | A non-null assertion failed 8 scheduler tests that drive the builder without its Collection | Fallback kept, with a comment naming where it is reached | +| Pooled `preload()` leaves the partition deferring | Refuted at the precondition | The partition unsubscribes when its source's cleanup starts, so no survivor is miscounted | No change | +| The change manager special-cases live-query Collections | Design | Source inspection | No change; a config-level capability would add surface for one caller | +| A test comment is out of order | Maintainability | Source inspection | Fixed | + +## Third review, at `9fb2d810a` + +| Finding | Kind | Evidence | Outcome | +| --- | --- | --- | --- | +| A failed source start cannot be retried in the same sync run | Refuted under the accepted contract | Pinned block: the failed start puts the source and its live query in `error`; cleanup and a new preload start the source again and load it | No change; the block keeps the supported recovery path | +| A Suspense render could hang after a failed start | Refuted at the premise | The live query does enter `error`, so `useLiveSuspenseQuery` surfaces it | No change | +| A subscriber or preload during a source's `subscribe()` is missed | Defect | A handler on the outer live query's own status never ran inside the window. A status handler on an inner live query did: subscribing to the inner query starts it, and a handler that preloads or subscribes to the outer query left it at `loading` with no acquisition. Pinned block, both requests RED | After registering its listener, the source subscription resumes at once when a request already arrived | +| A ready read returns partial local rows | Pre-existing contract | Both reads resolve at once when the Collection holds rows; they now also request the rest | No change; waiting instead would change every Collection's ready reads | +| A preload rejected during cleanup leaves the flag set | Refuted at the precondition | A live query's cleanup completes before the next call, so the preload was accepted and the restart acquired nothing extra | No change | +| Pooled `preload()` does not resume the partition | Refuted | Pooled views serve eager sources only, whose demand never detaches; the oracle's "preload after build" cases pass | No change | +| Patch changeset, layering, the `?? true` fallback | Already decided or recorded above | | No change | diff --git a/docs/contributing/oracle-reviews/2026-10-07-localstorage-port.md b/docs/contributing/oracle-reviews/2026-10-07-localstorage-port.md new file mode 100644 index 0000000000..b7c1a46c22 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-07-localstorage-port.md @@ -0,0 +1,134 @@ +# LocalStorage oracle port audit + +Reviewed implementation commit: `1c31dfe1ac897143743d222aebcf16de23261504`. +This record reviews the two new executable owners and the LocalStorage source +in that commit. A later documentation-only commit may carry this record. + +## Laws and evidence + +The LocalStorage guide promises direct persistence and peer synchronization. +Collection settlement and cleanup supply the shared boundaries. The order +owner folds accepted whole-row effects in mutation author order. Its finite +grammar crosses same/disjoint keys, both handler completion orders, all four +decision pairs, and update followed by delete. It compares caller settlement, +public rows, durable rows, and fresh restore. The original adapter fails both +second-handler-first update histories before it reaches the final comparison: +the later write reaches storage before the earlier decision. The new owner +rejects that wrong design at the held durable checkpoint. + +The peer owner folds authored rows with typed IDs. It holds storage-event +delivery across two serialized disjoint writes. The original adapter loses the +first row for both numeric/string orders at the durable checkpoint. The same +owner checks manual acceptance with two initialized Collections sharing an ID; +the original adapter writes both rows into both stores. A controlled listener +host exposes the original cleanup leak at cleanup settlement and checks one +listener after restart. A pending local insert beside an accepted peer insert +checks that confirmation does not publish a duplicate source insert. That +check failed with the original insert confirmation after the mutation-order +change made the overlap reachable. All five peer cases pass in the reviewed +implementation. The production paths are `localStorageCollectionOptions`, +ordinary Collection mutation methods, `acceptMutations`, storage-event +delivery, cleanup, and restart. + +## Oracle guide audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Both files state the public authority, promised law, checkpoint, and limits before mechanics. | +| ORC-002 | Expected rows come from authored scalar rows and accepted decisions. Neither model reads the adapter cache, stored values, or its classifier. | +| ORC-003 | Opening prose and nearby comments distinguish contract, model, finite history grammar, production driver, and refinement comparison. | +| ORC-004 | Not triggered: both grammars use finite enumeration rather than an important generated property. Same/disjoint keys, opposite completion orders, four decisions, and typed-key orders define their stated bounds. | +| ORC-005 | Each case invokes the real Collection adapter and compares promised public or durable observations at named held, settlement, event-delivery, restart, and restore checkpoints. | +| ORC-006 | Original-production RED runs reject out-of-order persistence, overwritten peer rows, equal-ID ownership, and leaked listeners at their intended assertions. The old confirmation also fails the pending-insert receiving case. | +| ORC-007 | Not triggered: neither owner claims a generated property. | +| ORC-008 | Not triggered: each expected result is a stateless fold of authored rows and decisions. | +| ORC-009 | Model typed IDs map to Collection row keys; authored arrays combine accepted source and public rows only at settled checkpoints. The pending-insert case observes them separately before settlement. | +| ORC-010 | Both owners use `withHistoryCleanup`; a primary mismatch and any cleanup failure remain distinguishable. No shrinking occurs. | +| ORC-011 | No plausible shared semantic fault requiring a second formulation was identified. Fresh restore provides a separate public observation, while authored rows remain the sole expected source. | +| ORC-012 | This record is tied to the reviewed implementation commit and accounts for each applicable guide requirement. | +| ORC-013 | Second-handler-first, delayed peer event, equal-ID Collections, and cleanup/restart each distinguish a plausible weaker boundary rule. | +| ORC-014 | No native-browser scheduling claim is made. The controlled host establishes adapter response to delivered storage events, not whether or when a browser delivers them. | + +The registered `@tanstack/db` oracle campaign passed 67 files and 4,539 tests. +The affected LocalStorage suites passed 83 tests with typechecking; targeted +ESLint and the package build passed. These results cover serialized writes in +the controlled host. Simultaneous cross-tab read-modify-write races cannot be +made atomic with localStorage. Manual acceptance before the first sync run still +uses the existing ID fallback because the adapter does not yet have a Collection +reference. Arbitrary long histories and native browser event scheduling remain +outside these owners, as recorded in the coverage map. + +## Prep review follow-up + +The prep review found that a write-time storage or parser read failure was +treated as an empty snapshot. The two controlled failure histories reject that +implementation at the caller-settlement checkpoint: `isPersisted` fulfilled +while the previously durable row was replaced. The repaired adapter rejects the +write, retains the durable row, accepts a later independent write, and restores +both accepted rows in a fresh Collection. The same histories deliver a storage +event under a failed read and check that the public snapshot remains intact. +At that reviewed commit, startup remained best-effort; an absent storage key +was the only empty snapshot for a new write or event. The medium-review +follow-up below changes startup to fail on an unreadable snapshot. + +The final `@tanstack/db` oracle campaign passed 67 files and 4,542 tests. +The full package suite passed 254 files and 9,103 tests with no type errors. +The package build, lint check, and changed-file formatting check passed. + +The prep reviewer also identified a nested-handler dependency cycle. A +controlled probe admitted a second automatic write from the first handler, +then awaited the second receipt. Both transactions stayed `persisting` and +storage stayed empty until a separate signal released the first handler. The +mutation-order contract already chosen for this adapter makes that await +impossible to satisfy. The public guide names the limit. A neighboring oracle +history admits a nested write without awaiting it, then checks the held storage +cut and both ordered snapshots after release. + +## Medium-effort review follow-up + +Review intake head: `cd4b707beb533e278697981b7bcf05519c1b43b0`. +Reviewed repair head: `b94fea53c20b4ff89d891898d6792175cbc53f8a`. +The reviewer reported eight code-reading findings and ran no verification. +The user approved three contract decisions: fail malformed persisted restore +without changing stored bytes; make manual acceptance awaitable and ordered; +and publish removal of accepted rows when `clearStorage()` returns. + +| ID | Technical verdict and evidence | Disposition and durable value | +| --- | --- | --- | +| M1 | Confirmed: a malformed value yielded a ready, empty, permanently unwritable Collection. Legacy arrays, empty strings, and one invalid row beside a valid row reproduced it. | `fixed-now`. Startup now rejects before readiness, preserves exact bytes, and can restart after explicit repair or clear. The peer oracle owns malformed and transient-read histories. | +| M2 | Confirmed dependency cycle when an earlier handler awaits a later same-Collection persistence receipt. A never-settling handler also holds the ordered slot. | `accepted-design`. Strict mutation order was approved earlier; the guide names the cycle and the order oracle checks legal nested fire-and-forget admission. No timeout or cancellation recovery contract exists. | +| M3 | Confirmed: manual acceptance could settle and write a newer value ahead of a held automatic handler, then the older value replaced it. | `fixed-now`. `acceptMutations()` returns `Promise` and enters the same write order; callers await it. The order oracle crosses same/disjoint keys and both earlier-handler decisions. | +| M4 | Confirmed: handler-free insert, update, and delete returned before synchronous Storage was updated. | `fixed-now`. The order oracle checks each direct return boundary and a later handler-free write held behind an earlier handler. | +| M5 | The reported throws occur, but `null` is the Storage API's missing-key value. An existing empty string is malformed data; `undefined` violates the declared `StorageApi` type. | `refuted` as a bug. The peer oracle distinguishes null from empty content and requires failed writes to preserve the empty bytes. Treating empty as absent would lose data. | +| M6 | Confirmed: a later failed handler's receipt and optimistic rollback waited for an unrelated earlier handler. | `fixed-now`. The order oracle checks prompt failure and rollback at the held-handler checkpoint for same and disjoint keys. | +| M7 | Confirmed: `clearStorage()` removed durable rows but left them publicly visible, including after a later write. Direct same-tab raw Storage edits also have no browser event. | `fixed-now` for the supported utility; it now publishes removal locally, with a peer delivery and fresh-restore witness. Direct raw edits are outside the adapter's event contract and the guide says so. A pending optimistic mutation remains visible and may later persist under the Collection's existing contract. | +| M8 | Confirmed work count: a present-key write parses one full snapshot and stringifies another. No latency threshold or measured user regression was supplied. | `accepted-design`. Each write rereads storage to preserve intervening peer rows and reject read failures. Sharing a parsed snapshot across writes without detecting peer changes would break that law; no cache state was added. | + +The primary order oracle derives expected rows from authored operations and +application decisions; it does not read the adapter's queue. Its driver reaches +the direct-call, held-handler, transaction-receipt, durable, public, and fresh- +restore checkpoints. The primary peer oracle classifies absent, valid, and +malformed bytes independently of the adapter parser and controls peer event +delivery. It checks startup status and byte preservation, clear publication, +peer publication, later settlement, and restore. These are bounded finite +histories, not a claim about simultaneous cross-tab read-modify-write races, +native event timing, or arbitrary long histories. The coverage map retains +those limits and the pre-sync manual identity fallback. + +The original implementation failed M1 at the startup-readiness assertion, M3 +at manual settlement while the predecessor was held, M4 at direct call return, +M6 at prompt failure settlement, and M7 at public/durable agreement after +clear. The repaired oracles pass 29 order and 15 peer cases, including nearby +accepted/rejected and same/disjoint regimes. The full `@tanstack/db` suite +passed 254 files and 9,119 tests with no type errors. The package build, +changed-file ESLint, Prettier, and whitespace checks passed. The docs link +checker reported 12 links outside `/docs` that already exist in the intake +head; this follow-up introduced none of them. + +The review was high-signal for an unverified medium-effort pass: five real +product bugs, one useful invalid-data distinction, and two genuine trade-offs. +Its suggested missing-key fallback and shared-read optimization would weaken +data safety. Recommendation: retain this reviewer for code-reading discovery, +with executable verification required before accepting fixes. Loss audit: +eight raw items equal five `fixed-now`, two `accepted-design`, and one +`refuted`; no item is deferred or omitted. diff --git a/docs/contributing/oracle-reviews/2026-10-08-localstorage-boundary-audit.md b/docs/contributing/oracle-reviews/2026-10-08-localstorage-boundary-audit.md new file mode 100644 index 0000000000..17b450e8cb --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-08-localstorage-boundary-audit.md @@ -0,0 +1,51 @@ +# LocalStorage boundary audit: native writer values and peer publication + +This self-review challenged the intersection of the LocalStorage guide's two +promises: an active same-tab Collection receives a peer's successful write at +the persistence receipt, and a default-JSON writer retains its authored JavaScript +value until that Collection restores. The primary executable owner is +`packages/db/tests/local-storage-peer-oracle.test.ts`; the mutation-order owner +remains `local-storage-order-oracle.test.ts`. + +## Confirmed defect and repair + +An active Collection authored a row containing `Date`. A same-tab peer then +inserted a disjoint row. The first Collection received that new row, but its own +`Date` became an ISO string without restoring. The previous Date witness stopped +at the first writer receipt; the peer histories used JSON-native values. Neither +crossed these two legal operations. + +The peer oracle now compares the writer, peer, durable bytes, and fresh restore +at the second write's receipt. It also edits the Date row through the peer to +prove that a real same-key change still reaches the original writer. On +`256fbd0d9b258ee60ec489c1d171c8b89abb4f16`, the new disjoint-write witness +failed at the writer's public Date observation: it saw the ISO string. After the +repair, it passes. The repair keeps a same-token default-JSON row in its +authored representation when its serialized content is unchanged; it still +publishes changed content and every new-version row. Custom-parser comparisons +retain their previous behavior. + +The violated law is bounded here to an active default-JSON writer, two +Collections sharing one Storage object and key, an authored Date field, a +disjoint successful peer write, and the second persistence receipt. The +same-key control and existing parser-normalization histories reject the +plausible overcorrection of ignoring all peer content changes. Other native +values, arbitrary `toJSON` side effects, many-operation histories, native +browser scheduling, distinct Storage wrappers, and simultaneous cross-tab +read-modify-write races are not established by this controlled host. + +## Negative control + +A separate peer-oracle witness installs a writer subscriber that throws only +after Storage contains the accepted row. The callback ran, and the persistence +receipt still fulfilled. The writer, peer, durable bytes, and fresh restore +contained the row. This experiment did not reveal a product defect; it checks +that application observation cannot revoke an accepted Storage write. + +## Verification and code weight + +The focused LocalStorage owners and examples passed 141 tests with no type +errors. The full database runtime suite passed 235 files and 11,393 tests. +Standalone source and test TypeScript checks, changed-file lint, package build, +and formatting passed. The production repair adds eight lines and removes one; +the oracle and contract record are counted separately. diff --git a/docs/contributing/oracle-reviews/2026-10-08-localstorage-external-review.md b/docs/contributing/oracle-reviews/2026-10-08-localstorage-external-review.md new file mode 100644 index 0000000000..80372daf1c --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-08-localstorage-external-review.md @@ -0,0 +1,123 @@ +# LocalStorage external review of PR #2074 + +Reviewed PR head: `8abbf5b14d601ce0e636de36fbc210e60da4f12e`. +The external review supplied ten code-reading findings and no executed tests. +This record keeps their source order. A later repair commit must name its own +head and recheck affected evidence before changing these dispositions. + +## Finding ledger + +| ID | Claim | Evidence on the reviewed path | Disposition and durable value | +| --- | --- | --- | --- | +| R1 | Un-awaited manual acceptance can settle a transaction before its queued Storage write, and a later error becomes unhandled. The changeset is patch despite an async API change. | With a held automatic predecessor, the manual transaction reached `completed` while storage was `null`. A later quota fault reached an `unhandledRejection` listener after the earlier handler returned. | **Design decision.** The approved new contract requires awaiting `acceptMutations`; old callers cannot obtain a correct receipt without doing so. Decide whether to preserve old callers through transaction-level tracking or release this incompatible contract as a minor version. The general mutation guide's example now uses `await` for both LocalOnly and LocalStorage. | +| R2 | A collection-wide queue blocks unrelated keys; use per-key chains. | The order oracle's disjoint-key held-handler history reaches and asserts this wait. | **Accepted design.** The user approved mutation order across the Collection, including manual writes. A per-key queue would weaken its receipt-order law. The remaining head-of-line cost is explicit, including a handler that never settles. | +| R3 | A handler awaiting a nested same-Collection persistence receipt deadlocks. | A controlled probe held the outer handler on the inner receipt: both receipts remained pending and storage stayed empty until an external escape let the outer handler return. | **Accepted design.** The order oracle checks the legal fire-and-forget neighbor. The guide excludes this dependency cycle under the approved order law. A fail-fast contract would need a new decision and a reliable way to identify the Promise dependency. | +| R4 | Direct `acceptMutations` throws while queued acceptance rejects, so `.catch()` is timing-dependent. | The new order-oracle direct-fault case failed on reviewed production at its no-synchronous-throw assertion; the queued counterpart passed. | **Fixed in working repair.** `acceptMutations` is async, so both Storage faults and pre-reservation validation faults reject its returned Promise. The same oracle cases pass and preserve exact error identity for Storage faults. | +| R5 | An empty stored string errors at startup and blocks writes. | The peer oracle checks `''` against a missing `null` key. It requires startup error and unchanged bytes for `''`, then permits a clear/restart and normal first write from `null`. | **Refuted as a bug.** This is the approved malformed-data law. Treating existing empty bytes as absence would silently erase data. | +| R6 | Each write parses and stringifies the full snapshot. | Five direct writes with a counting parser produced four full parses after the initially absent key and five full-snapshot stringifications. | **Accepted design.** The peer oracle kills the old cache-only writer that lost unseen peer rows. Without an external-change signal, reusing a parsed snapshot would reopen that loss. No latency threshold or measured regression was supplied. | +| R7 | Reusing one options object for two Collections can silently drop a manual mutation. | `createCollection(options)` twice, followed by two preloads and manual acceptance for the first Collection, yielded a completed transaction and no durable row. The second preload replaced the shared sync's Collection reference. | **Design decision.** Decide whether direct options-object reuse must materialize independent adapter state or fail explicitly. The existing equal-ID ownership oracle rules out restoring the old ID fallback, which wrote mutations to unrelated stores. | +| R8 | A same-tab Collection sharing a storage key may retain a stale public snapshot. | Two Collections from separate options shared one Storage object/key. After the first persisted an insert, durable storage and its own public snapshot had the row; the second public snapshot remained empty without an event. | **Design decision.** Decide whether same-tab peers synchronize automatically or the API limits each tab to one active Collection per storage key. The existing peer oracle's event-delivery assertion does not reach this no-event history. | +| R9 | Confirming an insert as a sync update can replace a peer row; partial mode might merge stale fields. | The peer oracle reaches a peer insert on the same key while a local insert is pending. The later accepted local whole row wins in storage and both public snapshots without duplicate-key failure. `rowUpdateMode` is `full`. | **Accepted design for current mode.** The mechanism is real; its implied current bug is not. Partial row mode is speculative and would need a separate contract and witness before adoption. | +| R10 | `reservedWrites` duplicates queue state; a missed decrement could disable the synchronous path. | No missed decrement was reproduced on reviewed production. A working repair removes the counter, then checks a third handler-free mutation at direct return after a held queue drains. Removing the new tail clear as a hostile mutant leaves `b.value` at `0` instead of `4` and fails the intended assertion. | **Fixed in working repair as simplification.** The state count disappears. The mutant failure is a direct-return assertion, and the restored implementation passes. | + +The review's prose repeated R2, R3, and R5 from its earlier pass; those claims +remain accounted for above. Its earlier corrupt-data finding was not repeated. +No item is deferred. R1, R7, and R8 remain open product decisions; a green +test run cannot close them. + +## Law coverage and enforcement + +The order owner is +[`local-storage-order-oracle.test.ts`](../../../packages/db/tests/local-storage-order-oracle.test.ts). +Its independent authored-row fold judges accepted automatic and awaited manual +effects. Finite histories vary same/disjoint keys, both handler completion +orders, accepted/rejected decisions, update/delete, a legal nested +fire-and-forget write, and a held automatic predecessor before manual +acceptance. The production driver uses real Collection mutations and manual +transactions. It compares direct-return Storage effects, held-handler +settlement and durable state, final public/durable rows, and fresh restore. +The new manual error-channel cases add direct and queued Storage faults and a +validation fault. The reviewed direct path failed before returning a Promise; +the working repair passes all three. A hostile tail-clear mutant fails the +post-drain direct-return assertion; the working repair passes it. + +The peer owner is +[`local-storage-peer-oracle.test.ts`](../../../packages/db/tests/local-storage-peer-oracle.test.ts). +It models absent, valid, and malformed stored snapshots independently of the +adapter parser. The production driver controls peer event delivery, startup, +clear, and a pending same-key local insert beside a peer insert. It compares +startup status and bytes, held optimistic and durable rows, event-delivered +public rows, and fresh restore. The empty-string, null-key, and pending-insert +witnesses pass on the reviewed head. Native browser event scheduling, direct +options reuse, and same-tab no-event peer publication remain outside this +owner's claimed grammar. The coverage map must retain those limits until the +R7 and R8 decisions are implemented or explicitly bounded. + +The manual error law is fully encoded and enforced within the finite direct, +queued, and validation-fault grammar. It does not assert that an un-awaited +caller receives a correct transaction receipt. That is R1's separate contract +choice. The accepted queue law is enforced for the named finite schedules; +arbitrary long histories and a handler that never settles are not claimed. + +## Oracle guide audit for the working repair + +| Requirement | Evidence or limit | +| --- | --- | +| ORC-001 | Opening prose names direct-return and manual-Promise laws and excludes the nested-await cycle. The public guide and declared `Promise` API are the authorities. | +| ORC-002 | The authored-row fold does not use production queue or Storage cache logic. The error law compares the exact injected Error object and unchanged durable rows. | +| ORC-003 | Contract, pure fold, finite history grammar, real Collection driver, and direct/held/final comparisons remain distinguishable beside the code. | +| ORC-004 | Not triggered: these are finite enumerations and fixed controlled faults, not an important generated property. | +| ORC-005 | The driver reaches public `acceptMutations`, mutation methods, transaction receipts, and Storage effects at the named cuts. | +| ORC-006 | Reviewed production failed direct manual-error assertion. Queued error was an adjacent passing control. The tail-clear mutant failed direct-return assertion, not setup or timeout. | +| ORC-007 | Not triggered: no generated property was added or changed. | +| ORC-008 | Not triggered: the independent authored-row fold did not gain state. The queue simplification removes production state only. | +| ORC-009 | The fold's authored edit is a public Collection mutation; its accepted decision maps to mutation-handler outcome. Storage slot is production-only and does not enter the model. | +| ORC-010 | `withHistoryCleanup` retains the primary comparison while releasing held promises and Collection resources. No shrinking occurs. | +| ORC-011 | No named shared semantic classifier fault needs a second formulation for these error and retirement cuts. | +| ORC-012 | This record accounts for every applicable requirement and keeps the unresolved R1/R7/R8 laws visible. | +| ORC-013 | Direct versus queued error paths and the post-drain direct call distinguish plausible wrong boundaries. | +| ORC-014 | No real-browser event delivery is claimed. R8's controlled no-event probe needs a receiving browser witness if same-tab synchronization becomes a product law. | + +The five-file LocalStorage run passed 119 runtime tests and 14 type tests with +no type errors after the working repair. The package build and changed-file +ESLint passed. These results do not settle the three open design decisions. + +## Follow-up on approved laws + +The preceding dispositions describe the review of `8abbf5b14`. The user then +approved two laws on pushed head `ad707b841a438253159a8baa3771b0a901964839`: +an un-awaited LocalStorage `acceptMutations()` must still hold the manual +transaction's persistence receipt until its write settles, and reuse of one +options object's mutable adapter state must fail at Collection construction. +The working repair is a normal follow-up to that pushed head. R8 remains an +open product decision; the other findings retain their earlier dispositions. + +| Source item | New evidence and current disposition | +| --- | --- | +| R1 | **Fixed in working repair.** The order oracle crosses direct or queued un-awaited acceptance with successful or failed Storage writes. It compares `commit()`, `isPersisted`, exact errors, durable rows, and public rows at settlement. The reviewed implementation failed three of four single-write histories at receipt or error checkpoints; direct success passed as a control. A transaction with two un-awaited acceptances checks that its receipt waits for both. LocalStorage now registers each accepted write as transaction commit work, and `Transaction.commit()` waits for all registered work. The old un-awaited call pattern remains valid, so the changeset stays patch. | +| R7 | **Fixed in working repair.** The peer oracle varies direct or shallow-copy reuse and whether the first Collection has preloaded. All four histories reject the second construction before it can replace the first Collection's owner; the first still persists its authored row. Direct reuse failed on the reviewed implementation. Both shallow-copy histories failed the first working guard at their admission assertion, so the guard now survives a shallow copy. Fresh options and per-client descriptor materialization remain supported. | +| R8 | **Open product decision.** The same-tab no-event public-snapshot gap still needs either a synchronization law or an explicit one-Collection-per-key contract. The user asked about implementation difficulty; a registry keyed by Storage object and key is moderate adapter work, with optimistic state, failed writes, cleanup, and restart as the critical histories. Cross-tab read-modify-write atomicity remains a separate limit. | + +CodeRabbit review `5458818828` on `ad707b841` supplied one actionable inline +comment, `4220736088`, labeled major. **CR1** duplicates R1: an un-awaited +manual acceptance could let a transaction report persistence before a queued +write and leave a later failure unhandled. It proposed transaction-level +tracking or an incompatible release. The controlled R1 probe confirmed the +claim; the approved law selects transaction-level tracking. Its static analysis +did not run the test suite. There was one raw inline finding and no additional +footnote or suggestion. The task-local lossless ledger retains its full claim. + +The new receipt and admission histories extend the order and peer owners, +respectively. Their expected outcomes come from the approved receipt and +ownership laws, not from the adapter queue or claim hook. The admission +history first exposed a false-green shallow-copy path in the working repair. +The affected five-file run passes 149 tests with no type errors after that +repair. A full-package attempt passed 11,577 tests but failed to transform one +changed oracle because its callback used `await` without `async`; the focused +run passed after the syntax correction. This is not a green full-package run. +The coverage map retains the distinct R8 limit and the cross-tab atomicity +limit. No item was silently deferred. + +Final recheck after the syntax correction and shallow-copy repair: all 269 +`@tanstack/db` test files passed, with 11,616 tests and no type errors. The +package build, changed-file ESLint, Prettier, and whitespace checks passed. diff --git a/docs/contributing/oracle-reviews/2026-10-08-localstorage-final-review.md b/docs/contributing/oracle-reviews/2026-10-08-localstorage-final-review.md new file mode 100644 index 0000000000..7d3a69c68f --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-08-localstorage-final-review.md @@ -0,0 +1,113 @@ +# LocalStorage oracle follow-up review, 2026-10-08 + +Reviewed PR #2074 at `347e28207815e4a64a7a495e07e6b45bc2c97f12`. +This record reconciles four independent oracle reviews: receipt ownership, +peer synchronization, queue liveness, and parser recovery. The append-only +intake with the original claims and execution notes is +`/private/tmp/pr2074-final-review-ledger.md` in the review workspace. + +## Reviewer assessment + +The reviewers found eight actionable product or oracle defects and separated +three approved limits from defects. Their strongest observations used public +persistence receipts, method-return cuts, fresh restore, and a deliberately +hostile transaction-work mutant. The reports identified the missing history +dimension or assertion for each confirmed bug. One proposed custom Storage +return-value concern was correctly rejected because `StorageApi.getItem` +returns `string | null`. The default-JSON Date observation was real, but its +suggested normalization would change an established writer contract. Overall: +high technical accuracy, high analytical depth, strong signal-to-noise and +prioritization, and mostly precise repairs. **Hire recommendation: yes.** + +## Lossless finding ledger + +| ID | Claim and technical verdict | PR action | Durable value | +| --- | --- | --- | --- | +| R1 | Confirmed: a preloaded or cleaned-up direct owner could make module-level acceptance of a DbClient mutation fulfill without writing. P1. | `fixed-now`: route by recognized Collection utility and object identity, independent of direct sync state. | Order oracle covers idle, ready, cleaned-up direct owners; receipt, durable, public and fresh-restore cuts. | +| R2 | Confirmed: a peer write from a restore subscriber could be missed before same-tab registration and then overwritten by mirror initialization. P1. | `fixed-now`: seed mirror and subscribe before callback-capable restore commit. | Peer oracle checks nested receipt, ready receiver, and later update. | +| R3 | Confirmed: a custom parser could normalize an authored row without changing its version token, leaving the writer stale at a fulfilled receipt. P1. | `fixed-now`: read back custom-parser writes and confirm authored data from the stored form. | Peer oracle covers insert, update, peer, durable and fresh restore. | +| R4 | Confirmed: a synchronous handler failure left an empty queue slot for a microtask, violating the handler-free direct-write method-return promise. P2. | `fixed-now`: retire immediately when that slot has no predecessor. | Order oracle compares Storage at method return and after both receipts. | +| R5 | Confirmed oracle false green: the DbClient receipt witness accepted before `mutationFn`, outside work registration. P1 for test integrity. | `fixed-now`: accept inside `mutationFn` and check while its predecessor remains held, after registration. | An unregistered-work mutant fails the held-receipt assertion. | +| R6 | Confirmed pre-existing bug: encoded storage key and row `getKey` could disagree, allowing a deleted public row to reappear on restore. P2. | `fixed-now`: reject the malformed snapshot before readiness. | Peer oracle includes mismatched-key startup and preserves bytes; guide states key identity. | +| R7 | Confirmed pre-existing shape gap: a null version token was accepted. P2. | `fixed-now`: require a string token. | Peer oracle includes malformed startup. | +| R8 | Confirmed pre-existing shape gap: a null row event advanced the mirror without publication, hiding a same-token repair. P2. | `fixed-now`: validate object row data before mirror advancement. | Peer oracle checks invalid event, repair and zero `begin` calls for malformed startup. | +| R9 | Confirmed representation difference: default JSON keeps a writer-authored `Date`, while peers, durable bytes and restore hold an ISO string. | `accepted-design`: preserve the established native writer value; do not apply R3's custom-parser repair to default JSON. | Date limit witness in peer oracle and explicit guide text. | +| R10 | `refuted`: `undefined` for an absent key violates the typed `StorageApi.getItem(): string \| null` contract. | No production change. | Retain null-versus-empty sentinel coverage; malformed empty bytes still fail startup. | +| R11 | Confirmed dependency cycle when a handler awaits a later same-Collection receipt; head-of-line blocking follows from the single write order. | `accepted-design`: the public guide excludes this await cycle and permits fire-and-forget nested admission. | Existing nested-admission and held-predecessor order histories remain; changing queue policy needs a new contract. | +| R12 | Confirmed lack of cross-tab compare-and-swap and lack of coordination between distinct custom Storage wrappers. | `accepted-design`: both are explicit guide and coverage limits. | Keep as separate native coordination proposal; no claim of cross-tab atomicity. | + +No item is deferred, and no design decision remains open. The original twelve +items equal eight fixed, three accepted designs and one refutation. + +## Laws, gaps and enforcement + +**Manual receipt and owner law (R1, R5).** The guide requires +`acceptMutations()` called inside `mutationFn` to join the actual Collection's +ordered write queue, and persistence receipts to wait for the durable write. +The order oracle previously omitted direct-owner lifecycle states and invoked +acceptance outside `mutationFn`. Its independent authored-row fold now reaches +idle, ready and cleaned-up direct owners with a DbClient materialization. The +driver compares durable bytes, active Collection rows and fresh restore after +the manual receipt. A separate held-predecessor cut observes the receipt after +work registration; disabling `registerTransactionCommitWork` makes that exact +assertion fail (`fulfilled` instead of `pending`). The original production +fails the ready and cleaned-up histories with empty durable Storage. These +assertions enforce the law for single-owner acceptance through the tested +direct and materialized paths. Multi-owner acceptance in one utility call is +outside this grammar; the API rejects mixing recognized owners. + +**Peer agreement law (R2, R3).** The guide promises that active Collections +sharing one Storage object and key see an accepted same-tab write without a +browser event. The peer oracle's independent authored-row fold does not use +the adapter mirror. It now admits a peer write from nonempty restore +publication, then checks readiness, nested receipt, a later update, durable +bytes and both public snapshots. It also normalizes an authored row using a +custom parser while holding the version token fixed; insert and update +receipts compare writer, peer, durable and fresh restore. The original code +fails the receiver and writer comparisons respectively; the repaired code +passes. Default JSON rich values are an explicit exception: the writer retains +the authored native value until restore, and the Date witness checks that +limit. The finite histories do not prove arbitrary subscriber reentry or +simultaneous cross-tab writes. + +**Direct-write and valid-snapshot laws (R4, R6–R8).** The guide promises a +handler-free write before method return when no earlier write can still use +Storage. The order oracle now places a synchronous failed handler immediately +before a handler-free insert and compares durable bytes at the return cut; +the old queue fails that assertion and the repair passes. The neighboring +settled cut and ordinary no-predecessor cases protect the conditional premise. +For restore and event delivery, only a valid whole snapshot can advance the +mirror or readiness. The peer oracle's absent/valid/malformed classifier now +includes null data, null token and key/row disagreement, while the driver +checks startup error, intact bytes, zero sync `begin`, invalid-event retention +and same-token repair. The old implementation accepts or advances these +malformed cases; the repair passes. This is shape and identity validation, not +arbitrary application-schema validation. Invalid writer key extractors and +direct raw same-tab edits remain outside this adapter's guarantees. + +## RED and GREEN evidence + +Oracle changes preceded production changes. On the original production, +the expanded order and peer owners ran 83 tests: 73 passed and nine intended +new comparisons failed. One additional default-Storage setup error came from +running the root Vitest invocation without the package's jsdom environment; +it is not counted as a product failure. The failing checkpoints were the +ready/cleaned manual durable receipts, restore-time peer receipt, parser- +normalized writer receipt, direct-write method return, malformed key and token +startup cases, invalid-event repair and null-row startup `begin` count. +The R5 hostile mutant was removed after it failed the repaired held-receipt +comparison; `transaction-commit-work.ts` has no diff. + +With production repaired, the package-configured LocalStorage run passed four +files and 142 tests with no type errors. This includes the existing CRUD and +persistence-failure tests, plus the two primary oracles. Full-suite and build +results are separate from the RED evidence above. The full package runtime +suite passed 270 files and 11,665 tests. Standalone TypeScript checks for both +the package and test project, package build, changed-file lint, formatting, +generated error docs, and ESM/CommonJS production error checks passed. +Vitest's integrated type-check phase still reports an overload diagnostic in +unchanged `src/collection/index.ts:961` and exits nonzero. Its type-check-only +path reports the same diagnostic in a clean pre-fix review copy, while both +standalone TypeScript checks pass. This is an existing tool discrepancy, not a +green integrated gate. The docs link checker reports nine existing links that +point outside `/docs`; this record adds none. diff --git a/docs/contributing/oracle-reviews/2026-10-08-localstorage-peer-review.md b/docs/contributing/oracle-reviews/2026-10-08-localstorage-peer-review.md new file mode 100644 index 0000000000..2a38196b5a --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-08-localstorage-peer-review.md @@ -0,0 +1,89 @@ +# LocalStorage peer and materialization review + +Reviewed PR head: `ee0a03fd7ca1737e9f4bfd151c06243a92eec7d5`. +This review supplied ten findings, two carry-over observations, and three +CodeRabbit comments. The full intake and per-item evidence remain in the +task-local ledger at `/private/tmp/pr2074-ee0-review-ledger.md`. This record +keeps the durable laws, repair results, and limits beside their oracle owners. + +## Findings + +| ID | Verdict on reviewed head | Follow-up | +| --- | --- | --- | +| U1 | Confirmed: an outer refresh restored an older mirror after a nested peer write. | Move mirror promotion before `commit()`. A three-Collection history failed at the later update with `DuplicateKeySyncError`, then passed. | +| U2 | Confirmed: a throwing receiver rejected an already durable writer and stopped later receivers. | Isolate and report each receiver error. The failing-parser peer history now preserves the writer receipt and later peer snapshot. | +| U3 | Confirmed: module-level options accepted a DbClient mutation through a separate write queue. | Route acceptance to the materialized Collection that owns the mutation. The held automatic/manual history failed at the intermediate durable checkpoint, then passed through settlement and fresh restore. A before-preload witness passes. Unrelated equal-ID Collections with either the same or a different storage key now reject the wrong utility; the same-key case failed before the final ownership refinement. | +| U4 | True dependency cycle under the approved Collection-wide order. | Accepted design. A handler must not await a later same-Collection persistence receipt; the legal fire-and-forget neighbor remains covered. | +| U5 | Confirmed: a local confirmation error after `setItem` skipped peer notification. | Publish in `finally` after durability. A one-shot writer key-extraction fault now leaves the compatible peer at the durable snapshot while the writer receipt rejects. Recovery of that invalid writer is outside this law. | +| U6 | Confirmed: stored-row validation opened a sync transaction before failing startup. | Validate before retaining callbacks or calling `begin()`. The failed-run trigger stays inert; a valid restart restores rows. | +| U7 | The options claim remains consumed after cleanup or a failed construction attempt. | Accepted design under the approved one-options/one-Collection law. Fresh options are required for a new Collection. | +| U8a | A shallow spread carries the claim. | Accepted design. The copied options still contain the same mutable adapter state and must reject reuse. | +| U8b | The claim threw plain `Error`. | Use `LocalStorageCollectionError`; four direct/spread, pre/post-preload witnesses failed before and pass after. | +| U9 | Confirmed: a nonempty key with one writer and three peers caused five full parses per write. | Reuse the exact saved Map for the default JSON writer only while Storage still contains the same bytes. The writer now parses once; each peer parses once. A custom parser still reads back, and a nested newer write forces a fresh read. | +| U10 | Confirmed: clear refreshed the writer twice. | Publish once to the active listener set. The two-Collection clear history failed at three reads and passes at two, with both public snapshots empty at return. | +| F11 | Already fixed: un-awaited manual acceptance holds the transaction receipt through direct or queued success/failure. | Keep the prior order oracle; U3 added its missing DbClient route. | +| F12 | Empty stored bytes still fail strict restore. | Accepted malformed-data law: only `null` means absent; explicit clear permits repair. | +| CR1 | CodeRabbit's inline parser-failure report repeats U2. | The per-receiver warning preserves its separate error-reporting suggestion. | +| CR2 | CodeRabbit found awkward prose. | Changed “an already ready peer” to “a peer that was already ready.” | +| CR3 | CodeRabbit gave positive comments on existing oracle ranges. | No requested change or additional law. | + +## Law coverage + +The [peer oracle](../../../packages/db/tests/local-storage-peer-oracle.test.ts) +models authored durable rows independently of the adapter's version cache and +parser. It now reaches a receiver that fails validation, a synchronous nested +manual write during peer publication followed by a later update, a failure in +the writer after `setItem`, a failed startup validation with an adapter `begin` +counter, one writer plus three parsers, custom parser normalization, and clear +with two active receivers. It checks the exact writer or peer receipt, clear +return, later update, startup failure, and restart checkpoints. The pre-repair +implementation failed each new triggering witness at its intended comparison; +the repaired implementation passes them with the existing neighboring cases. + +The [order oracle](../../../packages/db/tests/local-storage-order-oracle.test.ts) +folds accepted whole-row edits in author order. Its new DbClient history +materializes module-level options, holds an automatic update, and calls the +module-level `acceptMutations()` for a later manual update. It checks that +durable state and the manual receipt remain held before releasing the first +handler, then compares public rows, durable rows, and fresh restore. On the +reviewed head the later value reached Storage early. Routing to the owning +Collection's utility made the same history pass. A second history calls the +module-level utility before explicit preload and verifies the later restore. +The peer owner challenges the routing rule with an unrelated equal-ID Collection +using either the same or a different Storage key. It asserts rejection before +either store changes. The same-key case resolved successfully under the earlier +storage/key-only binding, so it killed that plausible wrong repair. The final +route recognizes only acceptance utilities created by this options object's +DbClient materialization factory. + +The prior receipt and malformed-data witnesses also passed in the complete +peer/order run. The final focused run passed 87 peer, order, and error-code +tests with no type errors; the full package run passed 270 files and 11,653 +tests with no type errors. Package build, changed-file lint, formatting, and +the production-error bundle check passed. A default-JSON writer's one-parse +bound is deliberately conditional: each custom parser reads its own bytes, +and a writer reads back when another write changes Storage before refresh. +Neither owner claims atomic simultaneous cross-tab read-modify-write, +cross-wrapper Storage identity, arbitrary long histories, or recovery from a +writer's invalid key extractor. Manual acceptance without a mutation Collection +owner remains outside these histories. + +## Oracle guide check + +| Requirement | Evidence or limit | +| --- | --- | +| ORC-001–003 | The owner prose states the receipt, startup, and work laws, identifies the approved order and one-options decisions, keeps authored-row and read-count models independent of adapter queues, and places each finite history and checkpoint beside its driver. | +| ORC-004, ORC-007 | These additions use controlled finite histories, not an important generated property. | +| ORC-005 | The witnesses call real Collection mutations, module-level options utilities, DbClient materialization, clear, preload/restart, and same-tab publication; the assertions read public rows, durable bytes, receipts, or adapter `begin` at named cuts. | +| ORC-006, ORC-013 | Each triggering witness failed on reviewed production. The normal receiver, custom parser, legal fire-and-forget nesting, and later update distinguish the repairs from overbroad shortcuts. The original parser-work path failed the intended count, not setup or timeout. | +| ORC-008–009 | The authored-row model retains only accepted rows; Storage version keys and queues stay production-only. The read-count model is a separate work observation. Glossary terms identify sync run, persisted restore, public snapshot, and receipt. | +| ORC-010 | `withHistoryCleanup` retains primary assertion failures while releasing Collections and controlled gates. | +| ORC-011 | No shared semantic classifier was introduced. The custom-parser and nested-write neighbors provide a second challenge to the saved-snapshot shortcut. | +| ORC-012, ORC-014 | The table above accounts for all raw items. The controlled Storage host and jsdom default-Storage witness establish the same-tab premise; native multi-window scheduling remains unclaimed. | + +The reviewer identified real reentrancy, isolation, ordering, lifecycle, and +work gaps without running the tests. Its proposed mirror move and listener +isolation were directionally correct. The repeated deadlock and options reuse +claims describe explicit product limits, while the typed error was a useful +small correction. This is a strong review with a good signal-to-noise ratio; +the reviewer should still distinguish accepted design limits from new bugs. diff --git a/docs/contributing/oracle-reviews/2026-10-08-localstorage-round-two.md b/docs/contributing/oracle-reviews/2026-10-08-localstorage-round-two.md new file mode 100644 index 0000000000..6f31c7421c --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-08-localstorage-round-two.md @@ -0,0 +1,147 @@ +# LocalStorage second oracle review, 2026-10-08 + +The four independent reviews examined PR #2074 at +`9344612bed911a364af6c9c3f9b023487cf6979a`. The normal merge of +`origin/main` produced `d5205b792e34f0f6f0282b7811a5c7de712ae0cd`; +all changed oracle histories were first run against that merged implementation. +The repaired implementation reviewed here is +`1f103c8504d135c1fa35215efba9d7ebc5db92e2` (the versioned record itself is a subsequent documentation-only +commit). +The append-only raw intake is `/private/tmp/pr2074-round2-review-ledger.md`. + +## Reviewer assessment and loss audit + +These reviews were accurate and useful. Three reviewers independently found +the same parser-normalization gap, which raises confidence but counts as one +product cause. The queue reviewer separated accepted local work from a later +API failure rather than incorrectly demanding cancellation. The key findings +also distinguished a serializable NaN-derived key from a nonfinite value that +JSON cannot round-trip. Proposed fixes were mostly narrow; canonical-key +rejection was stronger than the existing legacy compatibility contract. +Technical depth and signal-to-noise were high. **Hire recommendation: yes.** + +| ID | Technical verdict and evidence | PR action | Durable value | +| --- | --- | --- | --- | +| R2-RC1 | Confirmed: unchanged-token normalization of an untouched row left writer and peer stale at the fulfilled receipt. | Fixed in peer oracle and content comparison. | Paired changed/unchanged token histories. | +| R2-PE1 | Same confirmed cause, independently probed. | Duplicate of R2-RC1. | Retain independent receipt evidence. | +| R2-PA1 | Confirmed: postwrite custom-parser read fault rejected a durable write. | Fixed by validating serialized custom-parser bytes before `setItem`. | Prewrite and postwrite fault-position histories. | +| R2-Q1 | Confirmed: `mutationFn` failure settled before its previously accepted queued write. The later write itself is allowed by the mutations guide. | Fixed in transaction settlement and order oracle. | Held and released receipt cuts with durable, public and restore observations. | +| R2-PA2 | Confirmed: `n:1` and `n:01` silently collapsed into one Map identity. | Fixed by rejecting duplicate decoded identities before readiness. | Malformed snapshot grammar retains legacy single-key support. | +| R2-PE2 | Confirmed: a serializable row deriving numeric NaN as its Collection key wrote successfully but could not restore. | Fixed with SameValueZero identity validation. | NaN, finite number, and string controls. | +| R2-PE3 | Confirmed wording gap: guide implied legacy unprefixed keys were invalid. | Fixed guide wording. | Explicit compatibility boundary. | +| R2-RC2 | Confirmed review evidence gap for the previous changed head. | Fixed with this versioned exact-implementation review. | ORC-012 outcomes below. | +| R2-PA3 | Same confirmed normalization cause, independently probed. | Duplicate of R2-RC1. | Retain the parser-focused probe. | +| R2-Q2 | Confirmed: default JSON changed nonfinite numeric row IDs to null after a fulfilled receipt. | Fixed by checking the restored key before writing. | NaN and both infinity cases. | +| R2-Q3 | Confirmed oracle fixture admitted manual work outside `mutationFn`; a legal direct nested insertion reached the same peer reentry and passed. | Fixed the driver; no product change for this item. | Protect legal callback reentry. | +| R2-Q4 | Same ORC-012 gap as R2-RC2. | Duplicate of R2-RC2. | Retain independent evidence audit. | + +All twelve raw items are accounted for: nine fixed and three duplicates. +No item is deferred or awaiting a design decision. The separate negative +controls remain accepted: fire-and-forget nested automatic work after an outer +failure is independently admitted; awaiting a later same-Collection write in +an earlier handler is an order dependency cycle; simultaneous cross-tab writes +and distinct custom Storage wrapper objects have no atomic coordination law. + +## Law coverage and calibration + +**Durable/public agreement (R2-RC1, PE1, PA3, PA1).** The LocalStorage guide +promises active same-tab peers agree with a successful stored snapshot at the +fulfilled persistence receipt, apart from the stated default-JSON native-value +writer exception and failed reads. The independent authored-row fold predicts +the normalized prior row and new row. The peer owner varies whether the parser +changes the prior token. The unchanged-token case failed at the writer/peer +receipt on `d5205b7`; the changed-token control passed. A separate one-shot +postwrite `getItem` fault failed the successful-receipt assertion while durable +bytes already held the row; the prewrite-fault control still rejects without +writing. The repaired code parses and validates custom-parser bytes before +the durable write, then uses that snapshot for confirmation and publication. +The finite law covers the named parser, one write, active same-object peers, +receipt and fresh restore. Arbitrary parser side effects and native browser +scheduling remain outside this fixture's claim. + +**Key identity and restorable receipts (R2-PA2, PE2, Q2).** One stored entry +must represent one Collection key, and a fulfilled write must permit a fresh +restore of its row. The malformed classifier now rejects two encodings that +decode to one number before readiness. The typed-key driver accepts a +serializable string that derives NaN, plus finite number and string controls; +fresh restore failed on `d5205b7` and now passes. The default-JSON writer +driver tries NaN and both infinities as row IDs; on `d5205b7` each fulfilled +and wrote un-restorable data, while the oracle expected rejection and unchanged +storage. The repaired writer checks each changed row's JSON key round trip +before `setItem`. A single legacy unprefixed string key remains accepted; +duplicate identity is invalid regardless of spelling. The fixed finite inputs +do not prove every user-defined key extractor or serialization shape. + +**Accepted-work settlement (R2-Q1).** The mutations guide permits local work +accepted before a later API error to persist. The transaction receipt cannot +settle until that registered work finishes. The order owner's independent fold +contains both accepted rows. Its driver holds an earlier automatic handler, +calls un-awaited manual acceptance inside `mutationFn`, then throws. At the +held cut, both commit and persistence receipt were incorrectly rejected on +`d5205b7`; now both remain pending. After release, both report the original +API error, while durable, public, and fresh-restore rows show the accepted +write. The nearby successful and storage-fault manual histories protect the +other settlement paths. This does not make multiple Storage keys atomic. + +**Legal reentry (R2-Q3).** The peer owner now inserts directly from the +subscriber callback. This is an admitted production path, unlike calling +manual acceptance before commit. Its nested receipt and later update compare +writer, peer, and durable rows. The legal replacement passed before the +production repair, so this item was a driver correction rather than a product +RED case. + +## Oracle guide audit + +- **ORC-001:** The guide and oracle opening prose state receipt, key, and + restoration laws and the cross-tab, parser, and native-value limits. +- **ORC-002:** Expected rows come from authored operations or a malformed + snapshot classifier, never the adapter mirror, version cache, or decoder. +- **ORC-003:** Opening prose, model helpers, bounded histories, production + calls, and receipt/restore assertions remain adjacent in both owners. +- **ORC-004:** The finite token-change, typed-key, fault-position, and held + predecessor axes reconstruct the reported cases and controls. Removing the + unchanged-token, NaN, postwrite, or failure-after-acceptance member loses + its corresponding wrong design. Invalid malformed bytes are rejected. No + unbounded generated-history claim is made. +- **ORC-005:** Real Collection insert, manual transaction, preload, same-tab + publication, and fresh restore paths reach the named public observations. +- **ORC-006:** The merged pre-fix implementation failed the intended new + assertions; the legal nested-reentry replacement passed as a negative + control. Failures were assertions, not setup errors or timeouts. +- **ORC-007:** Not applicable: the changed tests are fixed finite histories, + not an important random generated property. +- **ORC-008:** Not applicable: no state was added to or removed from the + independent authored-row fold. +- **ORC-009:** The fold's accepted row maps to a completed Storage write; + pending handler and receipt are production timing concepts observed by + controlled gates, not hidden model state. +- **ORC-010:** `withHistoryCleanup` releases handlers and Collections after + assertions while preserving the primary failure. +- **ORC-011:** Durable-byte inspection and fresh Collection restore are + separate formulations of the receipt result. They differ from active peer + publication; no additional shared-fault hypothesis remains for this scope. +- **ORC-012:** This record reports each applicable requirement and explicit + non-applicability, with the law boundaries and original/adjacent witnesses. +- **ORC-013:** Changed and unchanged tokens, prewrite and postwrite faults, + serializable versus lossy nonfinite keys, and held versus released work + distinguish the reusable conditional laws from plausible wrong boundaries. +- **ORC-014:** The controlled `StorageApi` preserves native `getItem` and + `setItem` return semantics. These findings are scoped to that API seam; + cross-tab native scheduling is not inferred from the in-memory event host. + +## Verification + +The initial oracle-only run on `d5205b7` had eight intended failures among +92 focused tests: same-token normalization, duplicate decoded key, NaN +restore, three lossy nonfinite IDs, postwrite fault, and early failure +settlement. The neighboring changed-token and legal nested-reentry controls +passed. After repair, the two primary oracle files passed all 92 initial +tests. A final legacy-key control increased the peer owner to 49 passing +tests; the broader LocalStorage run had passed 150 tests before that added +control. The full package runtime suite passed 11,390 tests before that added +control. Standalone source and test TypeScript, package build, changed-file +lint, formatting, generated error docs, and ESM/CommonJS production error +checks passed. Vitest's integrated typecheck still exits nonzero on the +unchanged `src/collection/index.ts:961` overload diagnostic, while both +standalone TypeScript checks pass; it is not reported as a green integrated +gate. CI results on the pushed head are tracked separately. diff --git a/docs/contributing/oracle-reviews/2026-10-08-localstorage-same-tab.md b/docs/contributing/oracle-reviews/2026-10-08-localstorage-same-tab.md new file mode 100644 index 0000000000..1afff76e97 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-08-localstorage-same-tab.md @@ -0,0 +1,93 @@ +# LocalStorage same-tab publication audit + +Reviewed implementation and oracle head: `5383fe9d1532bbd23805135420d462a85dca3586`. +The user approved same-tab synchronization for active LocalStorage Collections +that share one Storage object and key. This record supersedes the open R8 +product decision in the earlier external-review record; it does not rewrite +that historical review of `8abbf5b14`. + +## Law and loss audit + +The [peer oracle](../../../packages/db/tests/local-storage-peer-oracle.test.ts) +uses authored rows as its expected snapshot. Its production driver starts two +Collections with fresh options on one controlled Storage object/key and withholds +browser events. At each successful persistence receipt, durable rows and both +active public snapshots must equal the authored fold, apart from a pending +optimistic overlay. A failed Storage write must not add its row. A delayed +storage event must preserve the result. Cleanup ends the receiving sync run; +restart restores durable rows and receives later same-tab writes. + +| Path and history | Observation cut | Evidence | +| --- | --- | --- | +| Two disjoint automatic inserts, typed number/string keys | Each receipt, delayed event, fresh restore | The old event-only adapter failed at the first peer receipt for both key orders; the repaired adapter passes. | +| Automatic insert, manual same-key update, automatic update, automatic delete | Each receipt and delayed event | Both public snapshots and durable rows follow `1 → 2 → 3 → absent`. A mutant omitting delete notification fails at `automatic delete peer`. | +| `clearStorage()` after a shared row, then a new insert | Clear return, insert receipt, delayed event, fresh restore | A mutant omitting clear notification fails at `peer clear without event`. | +| Held local same-key insert, accepted peer insert, then local settlement | Peer and local receipts | The held optimistic row stays visible locally; the peer follows the accepted durable row at each receipt. | +| Rejected Storage write | Rejected receipt | Durable and both public snapshots retain only the prior accepted row. | +| Cleanup, peer write, restart, peer write | Restart restore and later receipt | The restarted Collection restores the first peer row and receives the second without an event. | +| Default `window.localStorage` in jsdom | First writer receipt | A second Collection created with fresh default options observes the row without a synthetic storage event. | + +The new no-event assertions failed seven of 22 controlled tests on parent +`ac2c9ed3b726355939528361c47ef833d3cdaea6` at public snapshot cuts. +The repaired head passes 23 of 23 peer-oracle tests. The full `@tanstack/db` +suite passed 270 files and 11,641 tests with no type errors on implementation +commit `c28b94ae4`; the later `5383fe9d1` commit only extends the already +passing update/delete oracle, which passed its focused run. Package build, +changed-file lint, formatting and whitespace checks passed. + +The production addition is 46 lines and removes three lines in +`local-storage.ts`. It is a Storage-object/key registry: each active sync run +registers its refresh callback, and a successful write or clear publishes to +that key. Cleanup unregisters. This state is needed because the browser does +not deliver a storage event to the tab that performed the write. No retry, +second write queue or persisted metadata was added. + +The bounded claim covers compatible active Collections that can read the stored +snapshot, one Storage object identity and one key. It does not establish +atomic simultaneous cross-tab read-modify-write, distinct custom Storage +wrappers over the same bytes, direct raw Storage edits in the writing tab, +native multi-window scheduling, arbitrary long histories, or a no-allocation +resource proof for the same-tab listener after cleanup. The peer oracle owns +the latter resource witness if a stable observable seam becomes available; +the code directly unregisters and the public restart path is covered. Parser +or source-application failure in a receiving peer remains under the existing +failed-read contract, not a successful-convergence claim. + +## Oracle guide audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | The opening prose names the approved same-tab receipt law and limits it by Storage identity, pending overlays, valid reads and non-atomic cross-tab writes. | +| ORC-002 | Expected rows are authored independently of version keys, parser output, cache and registry. | +| ORC-003 | Opening contract, authored-row model, finite histories, real Collection driver and named receipt/clear/event comparisons are visible beside the code. | +| ORC-004 | Not triggered: the extension uses finite enumerations and named histories, not an important generated property. Number/string typed keys are both exercised. | +| ORC-005 | The driver reaches automatic mutation methods, manual acceptance, `clearStorage()`, cleanup/restart and default DOM Storage; it compares public and durable observations at the promised cuts. | +| ORC-006 | The original event-only implementation failed seven intended snapshot comparisons. The skip-delete and skip-clear mutants failed their respective public checkpoints, not setup or timeout. | +| ORC-007 | Not triggered: no generated property was added or changed. | +| ORC-008 | No reference state was added: the independent fold retains only authored accepted rows. Pending intent is distinguished by a held production transaction and checked separately. | +| ORC-009 | “Authored row” is model-only accepted data; persistence receipt, sync run, public snapshot and cleanup use the glossary meanings. No production registry state enters the model. | +| ORC-010 | `withHistoryCleanup` releases held gates and Collections while preserving the primary assertion failure. No shrinking occurs. | +| ORC-011 | No separate shared-fault classifier is identified. The old implementation and two path-selective mutants challenge the primary authored-row account. | +| ORC-012 | This table records each applicable outcome and the remaining bounded cells above, tied to the reviewed head. | +| ORC-013 | No-event receipt versus delayed event distinguishes the old event-only rule. Update/delete and clear mutants demonstrate distinct consequences at their own cuts. | +| ORC-014 | jsdom's default DOM Storage receives the controlled no-event premise. Native browser scheduling is explicitly unclaimed. | + +## Readiness-boundary follow-up + +Reviewed source and oracle head: `69ef5690a7db8f10b692462c70fc1ad2d43159b2`. +The preceding audit's receipt histories began after both Collections preloaded. +A newly ready Collection can invoke application status listeners before its +sync function returns. One such listener started a write through a peer that +was already ready. On prior head `d28d748da`, the write reached durable Storage and +fulfilled its receipt while the newly ready Collection stayed empty: same-tab +registration followed `markReady()`. The new oracle's `newly ready peer at +receipt` comparison failed with `[]` instead of the authored row. + +The sync run now registers its same-tab listener before publishing readiness. +If `markReady()` or browser-listener registration throws, it removes that +listener before propagating the startup error. The new history passes, as do +all 24 peer-oracle tests and the full package suite: 270 files, 11,642 tests, +no type errors. Build, changed-file lint, formatting and whitespace checks +pass. This extends ORC-001's active-run boundary, ORC-005's production path, +ORC-006's old-implementation kill, ORC-010's startup cleanup, and ORC-013's +distinguishing readiness witness. The other outcomes and limits above remain. diff --git a/docs/contributing/oracle-reviews/2026-10-08-partial-update-aborted-batch.md b/docs/contributing/oracle-reviews/2026-10-08-partial-update-aborted-batch.md new file mode 100644 index 0000000000..ca322c58b3 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-08-partial-update-aborted-batch.md @@ -0,0 +1,79 @@ +# Partial-update history after an aborted source batch + +Reviewed revision: the commit that adds this record on +`fix-partial-update-retention`, based on `main` `f6aba314e`. + +## Finding + +The partial-update campaign of +`packages/db/tests/collection-state-retention-oracle.property.test.ts` +(`collection-state.optimistic-history-partial`) failed on `main` for the +random seed `-526998278`. It failed the same way on `fe284ccbd`. Shrunk +history: + +1. The source row is `{ id: 1, a: 0, b: 0, c: 0 }`. +2. An open sync batch deletes row 1, and then aborts before acceptance. +3. A partial sync update writes row 1 with `c: -1`. The partial lane omits + `c`. + +The Collection published `c: -1`. The model allowed only `c: 0`. + +## Which account failed + +The driver failed. Production and the model were correct. + +- The law: in the default `partial` row update mode, an update that omits a + field merges into the source row, so the held value of that field remains. + The authority is the oracle's opening prose and the Collection's + `rowUpdateMode: 'partial'` contract. +- The driver records the source's own rows (`sourceRows`) so that it can + write a partial update with `c` omitted. On an aborted open batch, it + restored `sourceKeys` but not `sourceRows`. Row 1 was therefore missing from + `sourceRows`, and the driver wrote the next update as a whole row with + `c: -1`. +- Production merged the whole row that it received. That is correct for the + write the driver sent. The model predicted the write that the history + described. The mismatch was in the driver's translation of the history. + +## Repair + +- An open batch keeps `rowsBefore` with `keysBefore`, and an abort restores + both. +- The driver throws `Partial update of unknown source row` if a partial update + finds no held source row. A future drift between `sourceKeys` and + `sourceRows` then fails as a driver error, not as a product mismatch. +- A pinned case, `merges a partial update after an aborted source delete`, + replays the shrunk history. It asserts that the open batch aborted and that + the partial update was a distinguishing one. + +## Evidence + +| Check | Original driver | Fixed driver | +| --- | --- | --- | +| Pinned case | Fails: `c: -1` published, `c: 0` allowed | Passes | +| Seed `-526998278` replay | Fails | Passes | +| Fixed and random partial campaigns | Pass (the fixed seed never reached this history) | Pass | + +A mutant that keeps `rowsBefore` but does not restore it fails the pinned +case and one of the two partial campaigns with the new driver error. + +No production code changed, so this change has no changeset. + +## ORC outcomes + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Applicable. The law and its authority are above. | +| ORC-002 | Applicable. The model is unchanged and does not read production. | +| ORC-003 | Applicable. The pinned case has a comment that states the law and the history. | +| ORC-004 | Applicable. The open/abort and partial dimensions already existed. The fixed seed did not combine an aborted delete with a later partial update, so the pinned case supplies that history. | +| ORC-005 | Applicable. The driver now sends the write that the history describes. The new invariant rejects an inconsistent driver state. | +| ORC-006 | Applicable. The original driver and the no-restore mutant both fail at the intended checkpoint. | +| ORC-007 | Applicable. The fixed and random campaigns are unchanged. The direct replay of the reported seed and path passes. | +| ORC-008 | Applicable. The driver's source state now has one consistent restore point for keys and rows. | +| ORC-009 | Not applicable. No model term changed. | +| ORC-010 | Not applicable. No cleanup or shrinking change. | +| ORC-011 | Not applicable. No shared-fault hypothesis. | +| ORC-012 | Applicable. This record. | +| ORC-013 | Not applicable. No threshold law. | +| ORC-014 | Not applicable. No controlled provider. | diff --git a/docs/contributing/oracle-reviews/2026-10-10-owner-overlap-audit.md b/docs/contributing/oracle-reviews/2026-10-10-owner-overlap-audit.md new file mode 100644 index 0000000000..75b9043ce8 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-10-owner-overlap-audit.md @@ -0,0 +1,139 @@ +# Oracle law ownership overlap review — 2026-10-10 + +Reviewed baseline: `4bd66cf8ee72cf2c318878995222415b0452b031`. +Carry-forward base: `f7ac2c63a3cabc864caf38b7a4e966088cfe73bb`. +This review focuses on prose and ownership, with the follow-up test-integrity +repairs described below. It is not a full ORC-012 conformance audit. + +## Scope and method + +At the reviewed baseline, we read the law, authority, model, history, +production-path, observation, and limit prose in all 194 tracked oracle-named +TypeScript and JavaScript paths under `packages/` and `examples/`. That +inventory contains 165 test, spec, or suite modules, four type-test files, +and 25 companion modules. We also read the two package `ORACLE.md` files and +the adjacent `where-prefilter-property-visibility.test.ts` generated-history +file. The baseline inventory is reproducible with: + +```sh +git ls-tree -r --name-only 4bd66cf8ee72cf2c318878995222415b0452b031 -- packages examples \ + | rg -i 'oracle' | rg '\.(ts|tsx|js|jsx)$' +``` + +Within that output, paths matching `\.test\.`, `\.spec\.`, or `suite` are +runner modules. The four `\.test-d\.ts` paths are type tests. The remaining +paths are companion modules. These disjoint groups contain 165, four, and 25 +paths respectively. + +Untracked review copies and historical review records were not counted as +current executable owners. For each apparent overlap we compared the promised +law, legal history, production path, and public observation checkpoint. Shared +words or a shared model are insufficient to call two checks duplicates. + +Between the reviewed baseline and the carry-forward base, three oracle-named +files were added and twelve existing oracle-named files changed. We read the +new files, the changed files' law and owner prose, and the affected coverage-map +entries. The additions are: + +- [Facade rollback](../../../packages/db/tests/query/bucket-facade-rollback-oracle.property.test.ts) + owns internal flush rollback and bounded row-read work. The existing + includes-Collection owner checks public facade lifecycle and events. +- [Alias shadowing](../../../packages/db/tests/query/includes-alias-shadowing-oracle.test.ts) + checks finite exact public keys and captured references. The scope-identity + grammar owns broader alpha-renaming histories. +- [Leader-close OPFS](../../../packages/browser-db-sqlite-persistence/e2e/leader-close-oracle.opfs.spec.ts) + receives manual source-backed recovery in real Chromium tabs. Controlled + coordinator and persistence models and the Electric SDK receiver have + different provider and observation boundaries. + +The twelve modified paths fall into three ownership groups: + +- Browser coordinator, per-Collection coordinator, persisted wrapper, and + SQLite core oracles add leader-close and exact-receipt histories. Controlled + routing, Collection publication, durable SQLite evidence, and the OPFS host + receiver remain separate boundaries. +- Includes recomputation, scope identity, and Collection-valued includes add + legal alias shadowing, exact public-key delegation, and a facade retry + witness. The alpha-renaming model, exact-key companion, facade rollback + owner, and public Collection receiver retain distinct checkpoints. +- Proxy native methods, proxy revert, deep equality, Electric recovery, and + oracle configuration add native-mutator/revert, persisted-snapshot comparison, + peer-notice ordering, and campaign registration checks. They do not create a + second owner for those laws. + +The complete 194-file prose review remains tied to its baseline. This +carry-forward review covers the changed ownership prose and affected entries; +it does not claim a full conformance audit of every newly added assertion. +None of these changes calls for merging the owners in the table below. + +## Ownership decisions + +| Shared claim or surface | Canonical owner and companion boundary | Decision | +| --- | --- | --- | +| Failed Collection replay keeps a public snapshot private | [Lifecycle publication](../../../packages/db/tests/collection-subscription-lifecycle-publication-oracle.property.test.ts) owns exact public rows and batches. [Replay](../../../packages/db/tests/collection-subscription-replay-oracle.property.test.ts) applies that law to replay generations and same-key source writes. | Keep both. Fresh-key and same-key post-failure witnesses reject different faults. | +| Exact subset demand and applied abort | [IR identity](../../../packages/db/tests/query/ir-stable-identity-oracle.test.ts) owns key construction; [load subset](../../../packages/db/tests/query/load-subset-oracle.property.test.ts) owns runtime sharing, retry, and readiness; [transaction refinement](../../../packages/db/tests/query/load-subset-transaction-refinement-oracle.test.ts) owns the abort cut around sync acceptance and publication. | Keep all three production cuts. Point the broader campaign to the precise boundary owner. | +| Ordered window rows | [Pagination](../../../packages/db/tests/query/pagination-oracle.property.test.ts) owns general ordered-window rows and publication. [Ordered work](../../../packages/db/tests/query/ordered-work-oracle.property.test.ts) checks the same rows while bounding eager indexed and joined work. [Query DB's cursor model](../../../packages/query-db-collection/tests/cursor-pagination/model-oracle.ts) owns opaque backend paging through a different adapter path. | Keep row checks at both paths so a work bound cannot pass by dropping rows. | +| Optimistic whole-row state and settlement | [Optimistic history](../../../packages/db/tests/optimistic-history-oracle.ts) supplies the shared base/intent/source-queue model. [Same-key composition](../../../packages/db/tests/optimistic-composition-oracle.test.ts) exhausts a three-update matrix; [transaction ownership](../../../packages/db/tests/transaction-ownership-oracle.property.test.ts) owns release across two Collections. Outcome and publication drivers reuse the history model. | Keep the distinct matrix and observation drivers; do not count each shared-model replay as a new law. | +| Correlated includes | [Includes](../../../packages/db/tests/query/includes-oracle.property.test.ts) owns broad nested recomputation. Query-shape, temporal demand, facade, publication, projection, and work owners exercise narrower architecture laws or checkpoints. | Keep the architecture's owner split. The focused input-boundary tests are regression witnesses, not another model owner. | +| D2 value identity and top-K | [Hash identity](../../../packages/db-ivm/tests/hash-identity-oracle.property.test.ts) owns identity. [Flat hash](../../../packages/db-ivm/tests/hash-oracle.property.test.ts) checks equal-hash consequences for flat equivalent constructions. The [TopKRelation checker](../../../packages/db-ivm/tests/operators/topk-relation-oracle.ts) requires unit weights for both numeric- and fractional-index output; ordinary top-K cases using it supply unit support, while ordinary top-K can retain multiplicity. | Clarify both scope statements; retain distinct hash graph, retry, and top-K production paths. | +| Provider and framework receiving | Core/controlled models own their laws; IndexedDB browser specs, SQLite/OPFS hosts, Electric SDK delivery, and backend Collection E2E suites receive named premises. Shared framework contracts own row/page semantics while React and Vue drivers observe their own render cuts. | Keep receiving witnesses. Do not credit a controlled seam as native-host or framework timing evidence. | +| Opaque pagination no-peek candidate | The cursor-pagination owners retain the shipped peek-ahead contract. [No-peek integration](../../../packages/query-db-collection/tests/cursor-pagination-oracle.no-peek.integration.test.ts) exercises a test-only candidate against the full-relation model. | List it as experimental, separate from shipping coverage. | + +The only filename correction is +`virtual-row-legacy-guard-oracle.test-d.ts` to +[virtual-row-legacy-guard.test-d.ts](../../../packages/db/tests/virtual-row-legacy-guard.test-d.ts). +It contains concrete type assertions, not an independent model or reusable +checker. The independent virtual-row +type-shape owner remains `query/virtual-row-fields-oracle.test-d.ts`. + +## Repairs made during this review + +- Named the primary and receiving owners in the replay, load-subset, + ordered-work, flat-hash, and notification oracle prose. +- Corrected the indexed-DB portfolio's controlled-versus-native scope and + three receiving-spec paths in the coverage map. +- Classified backend Collection E2E separately from framework conformance, + listed the live-query notification owner and the no-peek experiment, and + linked the same-key optimistic composition witness. +- Corrected stale Electric SDK receiving references and inaccurate prose about + LocalStorage same-tab authority, direct change-event settlement, row metadata, + and fractional top-K multiplicity. +- Removed collision-sensitive distinct-digest assertions from the flat-hash + and identity owners. The identity model still checks `equalHashValues` for + distinct values, including a forced digest collision and a digest-only + hostile mutant. +- Tightened the proxy-revert native-history checker: an equal-valued `f` may + appear in `getChanges()` only after a live write to `f`. A spurious-field + report is a hostile control for this assertion. + +No production code changed. Collision-sensitive controls and their generators +were removed; the proxy-revert checker gained an assertion. This review found +no pair with the same complete law, legal history, production path, and +observation cut that should be merged into one test file. + +## Validation + +At the reviewed baseline, Vitest discovered the renamed file and both type +assertions passed. The `@tanstack/db` package-wide typecheck exited with +unrelated missing `@playwright/test` and `fake-indexeddb` declarations and was +not rerun in the carry-forward worktree. Dependency installation there failed +with a private npm-proxy 403. Using the existing checkout's dependencies, +focused worktree runs passed: 91 flat-hash and hash-identity tests, four +hash-session replay tests, 61 legacy hash examples, and 48 proxy-revert tests. +The `@tanstack/db-ivm` typecheck passed. +The proxy run used a minimal Vitest configuration because +the package's normal setup requires the unavailable `fake-indexeddb` package. +The digest-only and spurious-field hostile controls both failed their intended +checker assertions. Prettier accepted every edited file, all new review links +resolved locally, and `git diff --check` passed. No provider E2E tests ran. + +## Remaining review work + +Several oracle-named files still need a clearer opening contract or authority +source. The clearest are `query/ir-stable-identity-oracle.test.ts`, the two +ordered-source-loader oracles, `query/derived-delete-reconciliation-oracle.test.ts`, +the two top-K batch/fractional files, and several SQLite and provider owners. +Those are literate-prose gaps, not evidence of duplicate ownership. Follow-up +review removed the collision-sensitive distinct-digest assertions. The identity +owner still checks distinct values through `equalHashValues`, including under +forced digest collisions. diff --git a/docs/contributing/oracle-reviews/2026-10-10-sqlite-persistence-gaps.md b/docs/contributing/oracle-reviews/2026-10-10-sqlite-persistence-gaps.md new file mode 100644 index 0000000000..f62c74b72c --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-10-sqlite-persistence-gaps.md @@ -0,0 +1,213 @@ +# Core SQLite persistence oracle gaps + +The initial reviewed code and oracle head was `dc42f42e9`; the final code and +oracle follow-up is `dc34b91f6`, based on `origin/main` at `f7ac2c63a`. +This record covers direct persisted-options ownership, an on-demand +full reload with no active subset demand, and two-source persisted readiness +across one source restart. The executable owners are +`packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts` and +`persisted-readiness-oracle.test.ts`. The coverage map states their remaining +limits. + +## Laws and distinguishing histories + +**One runtime, one Collection.** A persistence receipt belongs to the +Collection whose mutation produced it. Reusing one direct options descriptor, +or a spread copy that retains its closures, cannot transfer that ownership to a +second Collection. A second `createCollection` now rejects at admission. The +first Collection still writes visibly and durably. Cleanup does not make its +captured runtime reusable. Fresh options materialized for separate DbClients +remain legal. The old implementation let both second creations succeed; a +diagnostic history observed the first writer's durable receipt fulfill while +the row appeared in the second Collection. Both new rejection witnesses failed +before the repair and pass afterward. The invariant guard in `setCollection` +also rejects a manually copied sync closure that bypasses the descriptor hook. + +A follow-up oracle at `0b10fa44e` exercises the same-object and spread-copy +cases with a wrapped upstream sync source. After the second creation rejects, +the first source commits a row that remains public and durable. Temporarily +removing the sync-present claim made both cases fail at the intended admission +assertion; the restored code passes. This checks both option-construction +branches for this bounded ownership history. + +**Demand controls persisted row reads.** The public SQLite persistence guide +says an on-demand Collection loads rows for active query demand. A plausible +rival rule treated every coordinator full-reload notice as an implicit +unconstrained demand. With a stored row and no subset owner, that rival called +`loadSubset({})` and exposed the row. The new witness checks zero adapter row +loads and no unsolicited public row after a full-reload notice, then checks +that a later unconstrained demand loads the row. It also observes a collection +metadata read, so an ignored notification cannot make the zero-load assertion +pass. The corrected witness failed on `origin/main` at the row-load assertion. +Its first draft was falsely green because the coordinator fixture's default +Collection ID differed from the test Collection's ID; the driver now sends the +exact ID. + +The old source-precedence matrix had encoded the rival rule in its on-demand, +no-demand full-reload cells. Eighteen such cells now derive separate public and +durable snapshots. The public snapshot retains accepted source rows but does +not acquire unrelated persisted rows. Durable storage still retains those rows. +Insert, update, delete, truncate replay, and queued or serial source changes +all pass. Three older failure/metadata fixtures now establish an active subset +before expecting a full-reload row read. This preserves their original failure +and metadata laws under a legal premise. + +**Readiness belongs to the current sync run of every source.** The glossary and +readiness owner require all opted-in eager sources to complete persisted restore +in their current runs. A completed restore in source A's old run cannot satisfy +the joined query after A cleans up and restarts, even if source B then restores. +The new controlled history checks `loading` and an unsettled initial-render +wait while A's second read is held, then `ready` and joined rows after release. +It passed unchanged production. A temporary stale-`ready` wrong-answer control +failed at the intended comparison (`ready` versus `loading`) and was removed. +The old live query is not expected to survive source cleanup; the driver creates +a new query after restart. + +## Instrument outcomes and limits + +Law discovery separated a descriptor's per-Collection ownership from DbClient's +fresh materialization. It also separated a full-reload notification from an +active subset demand. The on-demand model has zero or one demand for this +witness; the existing matrix supplies adjacent source histories. A tension scan +found a different reset obligation: `collection:reset` truncates the public +snapshot, while the established accepted-source law requires a compatible +durable baseline reread to retain an earlier accepted row in the controlled +matrix. The repair therefore keeps reset's existing unconstrained fallback and +narrows the new no-demand law to ordinary full-reload notices. Avoiding the reset +read needs an authority decision and a model for post-reset row provenance. + +An adversarial enforcement check caught the wrong-ID false green before the +production edit. The corrected fixture reached the metadata-read boundary and +failed on the original unbounded row read. The original direct-reuse behavior +failed both ownership cases. These are assertion failures at the intended +checkpoints, not setup failures or timeouts. No native SQLite or cross-tab host +claim follows from these controlled adapters. Previously loaded rows after +demand retirement remained open at this review head. The follow-up below +closes the controlled peer-notice history. Reset without demand, atomic +multi-subset reload, and arbitrary provider schedules remain with the owners +named in the coverage map. + +## Oracle-guide audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | The ownership, demand, and current-run laws above cite the Collection receipt boundary, public guide, and glossary. Their scopes and omissions are explicit. | +| ORC-002 | Expected ownership is a one-owner admission rule; demand is a zero/one logical-owner rule; readiness uses the independent all-source outcome model. None imports the runtime's queue or classifier. | +| ORC-003 | Law prose sits beside the new histories and model distinctions. The tests separate legal setup, production entry, public observation, and comparison checkpoint. | +| ORC-004 | The new cases and changed matrix are finite enumerations, not a generated-history coverage claim. The existing generated campaigns are unchanged. | +| ORC-005 | Direct `createCollection`, coordinator notification with exact Collection ID, later subset load, and a restarted source through a new observer all ran. The checks observe rejection, rows, durable writes, adapter reads, and readiness at their named cuts. | +| ORC-006 | The original ownership and unbounded-read designs failed their new comparisons. The temporary stale-readiness result failed at `loading`; the test was restored before review. | +| ORC-007 | No important generated property was added or edited. Existing fixed/random campaigns and replay interfaces remain unchanged. | +| ORC-008 | One-owner claimed/unclaimed and zero/one demand distinctions each permit a different next creation or row read. The readiness model already distinguishes each source's current outcome; the new history adds no production-generation mirror. | +| ORC-009 | The tests use Collection, subset demand, sync run, persisted restore, and persisted readiness as in the glossary. `claimed` is the descriptor's model-only admission bit. | +| ORC-010 | Ownership and no-demand witnesses use `cleanupPersistedOracle`; readiness uses `checkWithCleanup`. Primary mismatches remain visible while cleanup attempts every resource. | +| ORC-011 | The named shared-fault risk was a notification treated as demand by both production and the old matrix. The public guide and a separate zero/one-demand formulation exposed it; the matrix now keeps public and durable snapshots distinct. | +| ORC-012 | This record binds the review to the code head above and gives the law, old failure, adjacent controls, verification, and open cells. | +| ORC-013 | Same-object versus spread-copy, zero versus one demand, and old versus current restore each distinguish a plausible wrong boundary at a public checkpoint. | +| ORC-014 | The coordinator and storage adapter are controlled. The claim ends at those premises; real host delivery and native SQLite generation are assigned to existing receiving owners. | + +Verification on the reviewed code: 707 persistence/readiness oracle tests passed, +with one pre-existing todo; 209 DB client and LocalStorage regression tests +passed. Standalone TypeScript checks for both packages, both package builds, +changed-file lint, formatting, and whitespace checks passed. Lint reported only +existing `require-await` warnings in the large persistence oracle. Production +code adds 38 lines and removes 6; oracle tests add 271 and remove 6, counted +separately from coverage documentation. + +The `0b10fa44e` follow-up passed the full core package suite: 1,228 tests, +two existing todos, and no type errors. Changed-file lint and formatting passed; +lint retained only existing `require-await` warnings. The sync-present +wrong-answer control failed both new runtime cases before it was removed. + +## Copied sync config and control ownership + +CodeRabbit's comment on `75e265ac3` identified a path outside the descriptor +claim: a caller can select the exposed wrapped `sync` field into a second +Collection without copying the claim symbol. Both wrapped sync functions called +`setSyncControls` before `setCollection` rejected that second owner. The +independent law is that rejected admission leaves the first owner's sync run, +controls, public rows, and durability path unchanged. + +The primary owner now runs that selected-field history through the local-only +loopback and upstream-sync paths. On the pre-fix code, the local-only first +owner's subsequent durable insert disappeared from its public Collection; an +upstream transaction opened before the rejected second start aborted at commit. +These were assertion and receipt failures after the rejection was reached. The +repair checks owner identity before registering the new controls, while keeping +registration before coordinator subscription for a valid owner. Both histories +then retain the first owner's public and durable row. Ordinary same-owner sync +restart remains covered by the existing lifecycle histories. Controlled +Collections and adapters do not prove behavior in every native host. + +After the repair, the full core package passed 1,231 tests with two existing +todos and no type errors. The package build, changed-file lint, formatting, +and whitespace checks passed; lint retained only existing warnings. The +repair is production-line neutral: it combines the two control/binding calls +into one guarded operation without adding lifecycle state. + +## Prep review follow-up at `0108e5f2a` + +The prep review found one invalid Electric oracle fixture and three persisted +runtime gaps. The following table preserves the claims in source order. + +| ID | Review claim and proposed action | Result | +| --- | --- | --- | +| S1 | Name the arguments to `reloadActiveSubsetsUnsafe` for readability. | Refuted as a useful change: it adds a new shape without reducing state or branches. | +| R1 | One Electric descriptor fixture reuses a persisted wrapper and fails CI. Create a fresh wrapper per Collection. | Fixed in the oracle fixture. | +| R2 | A row stays public after demand retirement and a peer full-reload deletion. Validate already public rows at the notice. | Fixed in production and the primary oracle. | +| R3 | A selected wrapped `sync` field can bind another Collection after cleanup. Retain the original owner identity. | Fixed in production and the primary oracle. | +| R4 | A constrained reacquisition still leaves a deleted public row visible. Validate its scope or cached rows against durable state. | Fixed by R2's immediate validation and asserted after reacquisition. | + +**R1, Electric fixture.** The Electric descriptor law permits the same Electric +descriptor to create independent Collections. The fixture reused one *persisted* +wrapper around that descriptor, which contradicted the wrapper's single-owner +law and failed CI. The oracle now creates a fresh persisted wrapper for each +Collection while reusing the Electric descriptor. Its first-only +acknowledgement assertion remains. The repaired 30-test descriptor suite and +630-test Electric package suite pass. + +**R3, selected sync ownership.** Cleanup ends a sync run but does not transfer +the runtime's Collection owner. A caller could copy only the wrapped `sync` +field, omitting the public claim hook, then bind another Collection after the +first cleaned up. The two new histories select that field in local-only and +upstream-sync modes. Both failed at second-owner admission before the repair. +They now pass: the second Collection rejects, and the first restarts and writes +publicly and durably. The permanent owner identity outlives cleanup. + +**R2 and R4, peer notice after demand retirement.** The user confirmed that +a row which remains public after a peer deletes it is wrong, even with no +active subset demand. Demand retirement stops future acquisition work. It does +not make already public rows exempt from peer changes. A full-reload notice +with no public rows still reads collection metadata only. If public rows exist, +the wrapper reads one durable snapshot and corrects only those public keys. +It deletes missing rows, updates changed rows, and removes obsolete row +metadata in one sync transaction with collection metadata. It does not expose +unrelated durable rows without demand. + +The primary oracle reaches initial zero demand, an unconstrained demand that +retires, peer deletion, and a later unconstrained demand. A second history +reaches peer deletion, row update, and row-metadata removal after demand +retirement, then acquires a constrained demand. Both histories failed on the +old metadata-only notification at the intended snapshot-read checkpoint. +The earlier no-demand history had no previously public row. That missing +history dimension let the stale-row behavior pass while the narrower law held. +The final code passes their notice and later-acquisition assertions. The +constrained continuation needs no second validation read. The existing +18-cell source-precedence matrix still passes: accepted source transactions +retain precedence over persisted reads. The full persistence-core suite passes +879 tests with one existing todo. Standalone TypeScript, the package build, +changed-file lint, formatting, and whitespace checks pass. Lint reports only +24 existing `require-await` warnings in the large oracle file. + +The controlled adapter proves this law for its emitted notices and the +observed Collection checkpoints. Browser and Electron delivery remain with +their host oracles. Arbitrary overlapping retired subsets, live host +scheduling, and performance with large durable snapshots remain outside these +bounded histories. A full snapshot is the available adapter read that proves +absence of an already public key; the wrapper publishes only previously +visible keys from that read. + +The loss audit accounts for all five source-order items: R1–R4 are fixed, and +S1 was not adopted. The fixed-history and controlled-adapter evidence closes +these review findings within their stated scope. Final-head CI and host-level +delivery remain separate PR readiness checks. diff --git a/docs/contributing/oracle-reviews/2026-10-12-pr-2096-external-review.md b/docs/contributing/oracle-reviews/2026-10-12-pr-2096-external-review.md new file mode 100644 index 0000000000..7cf405315a --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-12-pr-2096-external-review.md @@ -0,0 +1,110 @@ +# PR 2096 external review: persistence ownership and peer reloads + +The review targeted 6c63fc88fb8c609b41b4b774d5a06d28a5386c71. Ten findings were evaluated in source order. The primary executable owner is packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts; the Electric descriptor owner checks its receiving boundary. The scratch intake at /private/tmp/pr2096-review-ledger.md preserves the review's original wording and every disposition. + +## Laws, experiments, and repairs + +**A hydrated seed has source precedence.** A DbClient hydration seed is already public but is not yet a durable source confirmation. An absent or older SQLite row cannot retract or replace it. The old oracle checked ordinary row correction after demand retirement, but had no seed dimension across complete validation and active replacement. Nine new histories cross complete demand, no-demand peer notice, bounded peer notice, and absent, older, or equal durable data. Four of the first six failed against the reviewed implementation at the public-row comparison; equal-row controls passed. The active bounded cases then exposed the replacement path. Production now skips hydrated keys in complete validation and preserves them while replacing an active snapshot. + +A mixed history checks the other side of this rule: one protected seed survives while an ordinary row is replaced without stale fields, another is deleted, and a new row is admitted. A second peer notice presents an older durable version of the seed. The public seed remains unchanged. Temporarily disabling the active-replacement guard made this history fail on Stale cache versus Hydrated at the second notice. The initial version of that fixture did not start sync, so its missed notification was a setup failure and supplied no product RED evidence. + +**A failed construction does not claim an owner.** A persisted options descriptor may create one Collection. A synchronous constructor failure before owner binding leaves that descriptor available for a corrected retry. The previous ownership histories began with successful construction. The new invalid-then-corrected history failed on the reviewed head because retry reported the one-owner error. The Collection constructor boundary now releases the claim on failure if the persisted runtime has not bound a Collection. The successful retry writes publicly and durably. A successfully bound owner keeps its claim through cleanup; async startup failure after construction is a different lifecycle. + +**A complete notice cleans orphan row metadata.** A no-demand peer reload validates already public rows and collection metadata. Row metadata for a key outside the public base cannot survive complete validation merely because no row is public. The old history observed public rows but had no metadata-only orphan case. New zero- and one-public-row histories establish orphan metadata through a source transaction, deliver a full-reload notice, and inspect the sync metadata API at the settled checkpoint. Both failed on the reviewed head and pass after the no-demand path uses complete validation and clears orphan metadata. + +**The Electric ownership fixture must distinguish storage identity.** The former fixture used two different storage IDs, so its green receipt check said nothing about a shared persisted storage target. The owner now crosses shared and distinct storage IDs with separate persisted wrappers, confirms the read targets, rejects direct reuse of one wrapper, and retains the first-owner acknowledgement assertion. The descriptor suite passes. This was a test-grammar gap, not an Electric production bug. + +The runtime's three collection-metadata replacement loops now use one helper. The visible-key validation and full-snapshot equality check remain separate because they answer different questions. + +## Other findings and contract boundaries + +| ID | Disposition | Reason | +| --- | --- | --- | +| R1 | Fixed | Hydrated seeds survived the complete and active reload histories above. | +| R2 | Fixed after design decision | The user chose demand-scoped reset publication. With no active subset, reset validates previously public keys against the new durable state and does not add unrelated stored rows. Eager reset still restores the full baseline. The source-precedence matrix failed on all 18 on-demand reset histories before repair, then passed; three focused histories cover empty, updated, and deleted visible-key scopes. | +| R3 | Fixed | Constructor failure releases an unbound descriptor claim. | +| R4 | Refuted on supported path | Collection sync always supplies metadata controls. An equal metadata-bearing row under a real peer notice emitted no change batch. The proposed null-controls path is not a supported core entry. | +| R5 | Fixed | Complete no-demand validation removes orphan row metadata with zero or one public row. | +| R6 | Accepted design; documentation repaired | Direct options reuse is intentionally rejected under the one-owner law. These packages are pre-1.0, and the repository changeset workflow uses patch for ordinary improvements. The patch changeset now gives direct-call migration guidance and states that DbClient descriptors remain reusable. | +| R7 | Accepted work cost | The current atomic resume snapshot reads the full durable table to validate visible-key absence. There is no O(visible) read budget. A keyed adapter read needs a new atomic snapshot contract and real SQLite work-count witness. | +| R8 | Refuted as duplication | The descriptor claim covers direct same-object and spread-copy admission. The runtime guard covers selected wrapped-sync fields that omit the claim. Existing oracle histories exercise both. | +| R9 | Fixed in the shared portion | Collection-metadata replacement uses one helper. The two row comparisons retain distinct laws. | +| R10 | Fixed | The Electric fixture now crosses shared and distinct storage identities and direct wrapper reuse. | + +The reviewer found three confirmed correctness gaps, one lost fixture dimension, and a reset rule that needed a product decision. The user chose demand-scoped reset publication, and the follow-up fixes it. The performance and duplication observations were useful. The null-metadata and duplicate-guard fixes missed legal entry paths. The review has strong signal; a senior reviewer should cite the authoritative law and legal entry path before prescribing a repair. + +## Evidence boundary + +The controlled adapter and Collection histories establish public rows, change batches, metadata, durable writes, and constructor admission at named settled checkpoints. They do not prove arbitrary browser, Electron, or native SQLite peer scheduling. Zero-demand validation, including reset with previously visible rows, still reads O(table) rows through the current atomic snapshot API. Eager reset retains its full-baseline behavior. These limits are in the oracle coverage map. The #2065 review is separate and was still running during this evaluation. + +## Verification and weight + +The active-replacement seed mutant failed at the public-row assertion, and a +wrong-storage Electric fixture mutant failed at the shared-storage identity +assertion. These distinguish the repairs from two plausible false-green paths. +The new tests passed after both mutants were removed. + +The full persistence-core suite passed 1,000 tests with one existing todo. +The full Electric suite passed 681 tests. Standalone TypeScript checks for DB, +persistence core, and Electric passed. All three package builds passed. +Changed-file ESLint passed with zero errors and existing require-await warnings; +Prettier and whitespace checks passed. A full DB run before the constructor +helper extraction passed 11,744 tests and found one production-bundle check +that rejected a try block inside the exported createCollection function. The +rollback was moved to a helper. The bundle test and 190 related DB tests then +passed. The final full DB suite passed 11,745 tests across 242 files. + +The final production diff adds 23 net lines across DB and persistence core; +tests and contract documentation grow separately. The extra runtime logic is +confined to preserving seed ownership, releasing an unbound claim while +retaining the production bundle shape, and sharing metadata replacement. + +## Demand-scoped reset follow-up + +After the first review, the user chose demand-scoped schema-reset publication +for on-demand Collections. A reset changes the durable cache epoch; it does +not create a new subset demand. With no active subset, keys already public are +checked against the new durable state, while unrelated stored keys stay out +of the public Collection. Eager Collections still restore the full baseline. +This revises the previous reset expectation rather than changing the accepted +source-transaction law: source writes accepted before or during a persistence +read remain applicable in commit order. + +The independent source-precedence matrix now predicts public and durable rows +separately. Before production changed, all 18 on-demand reset histories failed +at the settled comparison: the wrapper added an unrelated stored row and made +an unconstrained subset read. The repair keeps previously public keys in place +until zero-demand validation corrects them, and requests a full baseline only +for eager reset. All 90 matrix runtime histories then passed. Three focused +reset histories compare the public snapshot after a controlled reset settles: +empty public state, an updated visible row, and a deleted visible row. Each +rejects the old broad-publication behavior through its public-row comparison. + +For ORC-001/002/003, the user decision and SQLite on-demand guide authorize +the law; the test comments state its limit, independent snapshot rule, legal +histories, production notification path, public observation, and settled +checkpoint. ORC-004 and ORC-007 do not apply to these bounded `it.each` +histories. ORC-005 is the reset notification and public Collection snapshot; +ORC-006 is the pre-fix failure at that comparison. ORC-008 does not apply to +the stateless snapshot algebra. ORC-009 needs no model-only lifecycle term. +ORC-010 uses the owner's primary-failure-preserving cleanup helper. ORC-011 +is addressed by the separate empty/changed/deleted formulation beside the +source-precedence matrix. This paragraph records the ORC-012 evidence and +limits: controlled adapter and coordinator scheduling only, no native-host +reset delivery, and no O(visible) read claim. The coverage map retains those +receiving and work-budget gaps. + +The old-behavior mutant failed all three focused reset histories at the +public-row comparison and made an extra unconstrained subset read. The repaired +suite passed 1,003 persistence-core runtime tests with one existing todo. +Vitest's integrated run passed 1,439 checks with two todos and no type errors. +Standalone TypeScript, the persistence-core build, changed-file lint with zero +errors, formatting, and whitespace checks passed. An unrelated checkpoint in +the same oracle uses the same explicit generic type as `origin/main` to accept +its unused transaction result while awaiting the same settlement. + +The reset implementation and oracle were committed at `5dcfbef7a`. +The branch then merged `origin/main` at `a341c317a`; the merge changed the +release workflow and a separate adapter lifecycle test. The post-merge +persistence suite again passed 1,439 checks with two todos and no type errors. +This record reviews the reset code at `5dcfbef7a` as included in that merge. diff --git a/docs/contributing/oracle-reviews/2026-10-12-pr-2096-four-oracle-review.md b/docs/contributing/oracle-reviews/2026-10-12-pr-2096-four-oracle-review.md new file mode 100644 index 0000000000..35ca1971f8 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-12-pr-2096-four-oracle-review.md @@ -0,0 +1,62 @@ +# PR #2096 four-oracle review + +Review intake: `/private/tmp/pr2096-four-oracle-review-ledger.md`. Four independent +reviewers challenged the persisted durable-row, demand/peer, ownership/LocalStorage, +and Electric recovery oracles at `6d5128545e83836410bb8ea307fae987b439db19`. +The append-only intake retains all seventeen reviewer items and the sibling +violation found during evaluation. This record states the law-level findings; +the intake keeps the source-order claims and per-item dispositions. + +## Laws, experiments, and inference + +| Law and authority | Original oracle gap | Decisive experiment and inference | Repair and remaining enforcement | +| --- | --- | --- | --- | +| A peer database change validates a previously public row even when an unrelated filter is active; a DbClient seed stays source-owned. The user explicitly chose retained visible rows and the persisted oracle's demand law distinguishes row admission from validation. | Mixed-demand histories covered targeted and full-reload notices, but never reset. Reset truncated before it captured the public keys or seed authority. | A controlled A-outside/B-inside history had callback snapshots `[]`, then `[B new]`, while durable A remained. A protected C seed also vanished. Three primary reset histories failed at the callback snapshot; the expanded notice × A-state × seed-cache matrix passes eighteen histories after the repair. | On-demand reset no longer eagerly truncates. The independent model authors complete durable rows and protected seeds; every callback and final Collection is compared. Eager reset remains an adjacent control. Paginated mixed demand, atomic multi-subset reads, and a write between sequential reads still need separate witnesses. | +| A targeted peer notice cannot change row metadata owned by an unconfirmed DbClient seed. DbClient hydration authority covers row and metadata together. | Seed histories checked values and change keys, but not metadata set/delete. | Eighteen route × row-change × metadata-operation histories failed at the seed metadata assertion on the original code and pass after the guard. | The persisted owner now checks public seed row and metadata at publication. Same-key accepted source work racing peer delivery remains unmodeled. | +| One mutable options runtime belongs to the successful Collection constructed from those options. Direct/spread admission already held this ownership law. | A selected `sync` copy lacked the descriptor marker. Persisted tests started the original first; LocalStorage tests never selected only `sync`. | Lazy wrapped-source copy-first and both LocalStorage startup orders were RED on original code. They pass when runtime binding checks the Collection's original options claim before changing sync controls. Original-owner write, receipt, public/durable rows, and listener observations remain correct. | The persisted runtime and LocalStorage sync enforce the same construction owner at start. Local-only persisted options force startup and are a neighboring control. Module-held options still strongly retain a cleaned-up restartable Collection; no collectability promise is established by these tests. | +| A durable same-key insert replaces the complete row; update merges omitted fields. A targeted notice contains no operation identity, so the receiver cannot infer field presence from its changed value alone. Real SQLite adapter mutation algebra and complete-row persistence contract are authority. | The Electric recording fixture merged inserts, and its publisher generated updates for existing keys. The receiver's exact-row assertion could not see an omitted field. | The fixture-algebra check failed for insert before repair. The expanded generated history shrank to two same-key inserts, with `obsolete` present then absent; the old receiver kept `obsolete` publicly. The primary persisted owner's insert histories fail at exact public rows when the durable-read guard is removed, while two-update and delete/update controls distinguish merge and deletion. | The fixture replaces on insert, the grammar varies operation and removable field, and a public changed key triggers an authoritative durable read. The checked boundary is a controlled adapter and coordinator; real SQLite sender construction and browser delivery remain receiving limits. | +| Changed public rows publish a subscriber event containing the callback-time snapshot. The glossary defines publication through rows and events. | The Electric trace checker accepted a subsequence with only manual snapshots. | Suppressing the event recorder made four fixed histories fail at the new event assertion; ordinary histories pass. | Electric publication histories require an event when authored rows change. Equal-row notices may emit none. Callback count for arbitrary native schedules is not claimed. | + +The suspected redundant reconciliation event beside an absent or older seed did +not reproduce on a same-position controlled reload. Both variants reached the +reload, preserved row and seed metadata, and emitted zero public row events. +That refutes the proposed public-event defect on its stated path, while leaving +unmeasured internal replacement work outside the claim. + +## Loss audit and reviewer calibration + +The ledger accounts for EB-01–04, DP-01–05, DR-01–05, OL-01–03, and sibling +EB-05: eight fixed now, one refuted, one resource-contract design question, +one bounded positive readiness assessment, and seven open coverage or receiving +limits. Those seven are uncertainties, not demonstrated product defects. None +silently closes the native sender, browser delivery, pagination, concurrent +same-key source/peer, or multi-read atomicity classes. The coverage map names +their executable owners and missing witnesses. + +The demand/peer reviewer found two executed reset violations and a useful +missing product of grammar dimensions; strong hire recommendation. The durable +rows reviewer found the metadata ownership gap and also supplied a plausible +event claim that the same-path experiment refuted; hire recommendation with +calibration to distinguish source-level work from public publication. The +ownership reviewer found both selected-field escapes and correctly separated +resource retention from an accepted contract; strong hire recommendation. The +Electric reviewer identified a false-green fixture and event observer; both +produced sensitive repairs and one sibling product bug, with careful separation +of inspected and executed evidence; strong hire recommendation. Their proposed +work was valuable, but a green controlled oracle is not a native-host proof. + +## Validation scope + +Before repair, reset, seed metadata, selected-sync ownership, fixture insert +algebra, and same-key insert histories failed at their intended assertions. +The event-suppression and durable-read-removal mutants also failed at public +comparison checkpoints. On the repaired tree, the full database suite passed +12,080 tests across 277 files, the SQLite persistence core passed 1,559 tests +across 25 files, and Electric passed 1,088 tests across 28 files. Their +configured type projects reported no errors. The LocalStorage peer oracle +passed 57 tests and Electric recovery passed 74 tests in their package +environments. Both changed packages built. Changed-file lint, formatting, +whitespace checks, generated error docs, and ESM/CommonJS production-error +checks passed. These are finite, controlled histories. Final pushed-head CI +and intended native integration require separate verification before a +whole-PR readiness verdict. diff --git a/docs/contributing/oracle-reviews/2026-10-12-pr-2096-oracle-laws.md b/docs/contributing/oracle-reviews/2026-10-12-pr-2096-oracle-laws.md new file mode 100644 index 0000000000..8c1af6c9ec --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-12-pr-2096-oracle-laws.md @@ -0,0 +1,37 @@ +# PR 2096 persistence oracle law review + +The executable baseline for this review is `8b3debb3a9d5c6ab6d4da15e5d9c597559bf32b6`. The failing histories below were RED on that production revision or the first repair, and pass with the final repair reviewed here. Their primary owner is `packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts`; `packages/db/tests/local-storage-peer-oracle.test.ts` owns the LocalStorage constructor and host boundary. The claims below are bounded to controlled adapter and host histories; the open cells remain open. + +## Law structure and distinguishing histories + +A **complete durable row** replaces the value of a previously public persisted row, including field absence. An ordinary **source partial update** instead retains omitted fields. These are different kinds of evidence. A targeted `tx:committed` notice carries the source patch, not a complete durable row. Its receiver cannot treat that patch as either a complete row or a predicate decision. The independent model authors complete durable rows and selects keys by active demand, previously public keys after demand retirement, and protected DbClient hydration seeds. It does not call the wrapper's row comparator, predicate compiler, or notification classifier. + +This distinction resolves a concrete tension: keeping omitted fields is required for a source patch, while keeping them after a complete durable read leaves stale public data. The same row shape appears under complete demand, retired-demand full-reload notice, retired-demand reset, and targeted notice. Each route has a separate production entry point and public snapshot checkpoint. The model preserves source partial-update semantics rather than changing the Collection's global update mode. + +Eighteen exact-shape histories cross three complete-read routes with removal, removal with metadata, addition, removal/addition of an own `undefined` field, and scalar change. Nine removal cases fail at `expectExactSnapshot` on the baseline; nine neighboring controls pass. Callback-time snapshots, event values, public keys, row metadata, and the later settled public snapshot distinguish a coherent replacement from a delete followed by an observable partial state. The `undefined` cases are generic-adapter checks; SQLite's object encoding omits such fields. + +The targeted histories use legal source patches and an authored durable relation. Six predicate-scope cases cross active `title = Keep` demand and retired demand with stay, enter, and leave transitions: five fail and the leaving-active control passes at exact public-row comparison. Nine hydration-seed cases cross active complete, active filtered, and retired demand with durable replacement, partial update, and deletion of a source-owned seed while an ordinary row changes; all nine fail. The filtered cases keep a hydrated `title = Keep` seed while its older durable row leaves that predicate. Six same-key operation-order cases cross active/retired demand with delete-then-update, update-then-delete, and two partial updates; three fail and three pass. The first two sequences have the same separated `changedRows` and `deletedKeys` arrays but opposite durable outcomes. The fixture verifies the adapter's durable result before delivering the notice. A receiver that merely changes loop order cannot satisfy both; it needs authoritative final-row evidence or an ordered protocol. + +Two active-demand union controls keep a row that leaves `title = Keep` and enters `title = Other`, with both acquisition orders. Both pass on the baseline. Temporary first-only and last-only evaluator mutants each fail one control at the public snapshot comparison; each mutant changed only the subset-evaluator selection and was restored before further runs. A shortcut that handles only a complete second demand would also fail because both demands are constrained. These are assertion failures at `expectExactSnapshot`, not timeouts or setup failures. + +An active filtered demand does not acquire authority over a previously public row outside its predicate. The maintainer explicitly chose to keep that row visible and refresh it from storage. Six mixed-demand histories first load and retire a broad demand, acquire `title = Keep`, then deliver a peer notice. The previously visible `Other` row is unchanged, updated, or deleted in durable storage while the `Keep` row changes. Targeted and full-reload routes must agree on public rows at the change callback and settled checkpoint. On the first repaired revision, three cases failed at `expectExactSnapshot`: targeted update deleted the `Other` row, while full reload deleted it even when durable storage retained it. The current implementation validates those outside-scope keys from a complete durable snapshot. The fixture applies an adapter transaction before emitting a matching term/sequence/version notice; the full-reload envelope omits delta fields, as the protocol requires. + +Hydration-seed protection ends when an applied source transaction confirms its key. Ten histories cross source insert, update, and delete with later peer update, delete, or insert under targeted and full-reload notices. An ordinary sibling changes in every peer transaction, so a wrapper that protects the seed forever reaches the strict public-row comparison. A temporary targeted-path mutant that skipped the confirmed seed failed there with the stale source row; it did not time out. The fixture first checks the source receipt, public row, and durable row, then applies a separate versioned peer transaction and compares every callback snapshot with the authored final relation. + +Four captured-read source histories and eighteen additional complete-demand source-precedence histories pass. They protect the distinct accepted-source obligation at settlement after a held persisted read. They do not assert acceptance before that read releases. A hostile comparison control rejects a missing key, a stale defined field, and a stale own-`undefined` field. + +The one-owner law consumes an options descriptor only after successful Collection construction. Pre-bind invalid configuration, failed coordinator subscription during persisted sync startup, failed LocalStorage listener registration, and a post-acquisition missing `loadSubset` handler are distinct constructor cuts. The corrected descriptor must be reusable after each failure; a third construction must reject after success. A successful LocalStorage owner retains its claim after cleanup and can restart and persist. The baseline fails two persisted post-bind retry cases and four LocalStorage retry/resource cases. Four LocalStorage post-cleanup direct/spread histories pass. The late-failure host retains both a browser listener and an active same-tab refresh; a paired-host write makes three Storage reads after the failure versus two without it. Counts are captured inside the host's original `getItem`, after preload and before assertion reads. + +## Independent challenge and guide audit + +Independent oracle-review agents challenged the SQLite, ownership, and combined laws, then reviewed revisions prompted by their findings. They exposed missing post-cleanup admission, late listener acquisition, filtered-seed authority, two-constrained-demand union, source-confirmation exit, and mixed active/retired demand. A final reviewer found that the first mixed-demand and seed-exit fixtures fabricated a peer version and put delta fields on a full-reload envelope. Both now use real recording-adapter transactions and protocol-shaped notices. The reviewer then found that targeted mixed-demand changedRows omitted the ID carried by the actual mutation; the corrected envelope copies each committed patch verbatim, and all six histories and package TypeScript pass. The ownership reviewer found no further incorrect expectation in the bounded constructor histories. A proposed rule that would suppress metadata for an unseen targeted row was withdrawn: core explicitly permits row metadata for absent rows, so that demand-scoping change needs a separate product decision. + +CI exposed one conflicting Electric recovery oracle assumption. Its generated publication history required zero subset reads after every targeted notice, including a new key whose patch has no completeness certificate. That work count was not a public-row law. The revised oracle retains its exact public/durable row and metadata-marker checks and permits a targeted read. For a full reload with active demand, it counts both adapter row-read routes and requires one authoritative durable read. Its fixture now omits delta fields from a full-reload envelope, matching the coordinator protocol. The 60 Electric recovery tests and all 681 Electric package tests pass after the initial correction. An independent reviewer then challenged the specific-subset-read assertion and the coverage map's accidental implication of real SQLite peer delivery; both are corrected in this follow-up. + +ORC-001: the durable snapshot, source partial-update, hydration-seed, demand-scope, and one-owner contracts are stated beside their tests and in the coverage map. Their host and value limits are explicit. ORC-002: authored rows, Map selection, admission state, and paired-host relation do not import production semantic decisions. ORC-003: comments beside the model and each history name the law, driver, observation, and checkpoint. ORC-004 and ORC-007 do not apply to these bounded enumerations; existing generated campaigns are unchanged. ORC-005: the tests exercise public Collection construction, subset loads, coordinator delivery, Storage writes, public rows, events, and receipts; the REDs reach the named comparisons. ORC-006: the reviewed production revision supplies wrong-result controls, the exact-snapshot helper has explicit hostile outputs, and two temporary subset-selection mutants fail at the intended public comparison. ORC-008 does not apply to the stateless snapshot recomputation; the two admission states are distinguished by the corrected retry and third-construction histories. ORC-009: model durable rows, previously public keys, and protected hydration seeds map directly to the glossary's durable/public/source concepts; active demand and retired demand stay distinct. ORC-010: controlled histories use primary-failure-preserving cleanup; setup failures and timeouts are not counted as product RED. ORC-011: authored durable state versus actual adapter state, paired same-envelope/opposite-outcome order histories, and paired-host Storage work provide independent second formulations for the named shared-fault risks. ORC-012 is this exact-head record and the owner limits in the coverage map. ORC-013: removal versus addition/scalar controls, active versus retired demand, delete/update order reversal, filtered seed versus durable predicate, two constrained demand orders, failed versus successful construction, and pre/post-cleanup admission are neighboring distinguishing witnesses. ORC-014: the controlled adapter and host establish these premises only at their boundaries; real SQLite delta creation and browser/Electron delivery still require receiving witnesses. + +## Open cells + +The repaired implementation passes these controlled histories. Targeted delivery concurrent with an accepted same-key source transaction still needs an acceptance-order history in the persisted owner. Real SQLite mutation-delta construction and browser/Electron notification delivery need receiving witnesses in their existing adapter and host owners before the controlled targeted result can be generalized. Paginated mixed demand and a peer write between full and filtered reads also remain outside the new histories. A private closure or inert same-tab callback could retain a failed LocalStorage Collection without extra work; a lifecycle/GC witness remains outside this controlled host. The full-table read used for zero-demand visible-key validation still has no O(visible) work budget or keyed atomic adapter contract. These are tracked in `docs/contributing/oracle-coverage.md`. + +The focused RED runs used direct repository Vitest binaries with existing dependencies. After the final fixture correction, the persistence oracle passed 842 tests with one existing todo, the LocalStorage peer oracle passed 55 tests, and the 11 persistence-core executable suites passed 1,085 tests with one todo. The full database runtime suite passed 11,749 tests across 242 files with Vitest's integrated typecheck disabled; standalone DB source/test and persistence-core TypeScript passed. The Electric recovery oracle passed 60 tests, and all 13 Electric package suites passed 681 tests. Standalone Electric TypeScript, both package builds, changed-file lint with zero errors, formatting, and whitespace checks passed. On an earlier revision, Vitest's integrated typecheck exited nonzero while trying to write a temporary build-info file through a dependency symlink outside the writable sandbox. diff --git a/docs/contributing/oracle-reviews/2026-10-12-query-authority-oracle-followup.md b/docs/contributing/oracle-reviews/2026-10-12-query-authority-oracle-followup.md new file mode 100644 index 0000000000..3e3b8f07b0 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-12-query-authority-oracle-followup.md @@ -0,0 +1,147 @@ +# Query authority oracle follow-up — 2026-10-12 + +Four independent oracle reviewers examined PR #2065 at +`ebf7d024766dd70fb675ab57e6909f4419660562`. This record evaluates their +read-only findings and the subsequent oracle/documentation repair at +`c277333c4d605b7533096676f201a86c92e11325`. The reviewers covered shared +cache projection, fetch ordering, ownerless and persisted settlement, and +checker calibration. The scratch intake preserved every provisional claim +before verification. No production code changed in this follow-up. + +## Laws, experiments, and inference + +The [Query Collection guide](../../collections/query-collection.md#direct-writes-and-query-sync) +is the public authority. The maintainer also specified that the Query cache is +the source of truth and Collections project it. Compatible eager Collections +therefore share a representable cache edit. An on-demand peer that has already +acquired that exact Query projects the edit; an unacquired peer needs its own +server result. The ownership oracle now crosses both peer timings and a derived +eager peer select. Both public Collections, the cache, and provider call count +are compared at the write and held-fetch cuts. The mixed-mode source-reading +claim was a useful missing law boundary, not a demonstrated product bug. + +For a returned key held by overlapping on-demand subsets, the later-started +successful fetch controls the public value while row ownership is continuous. +The earlier oracle checked both settlement orders and owner releases, but the +guide did not state the lifetime boundary. An executed two-cache probe found +that after *all* owners release, reacquiring only the older Query with infinite +`staleTime` and `gcTime` projects its cached older value without a request. A +new legal oracle lane checks that older and newer cache entries each project +their own value on separate reacquisition. This is consistent with the Query +cache contract; it is not global last-fetch-ever precedence. + +A complete unbounded matching result can retire an omitted **ownerless direct +row**. It does not prove absence across another subset's retained Query cache. +An executed probe inserted x directly, settled a later empty matching result, +then settled an older overlapping response containing x: x disappeared and then +reappeared. A second probe showed the same return without a direct write. These +traces are real, but the accepted law orders two results that *return* the same +key and the omission law concerns an ownerless row at its own result boundary. +Negative authority across different Query entries would be a new product law. +It would need a model for a complete scope's absent-key claim, controls for +disjoint and windowed results, and a bounded retirement rule for old cached +Queries. The guide and coverage map now state this limit; the proposed stronger +law remains a design question rather than a claimed repaired defect. + +The confirmed defect was in the oracle's legal-history grammar. Twenty-eight +ownership histories used `eq('field', value)`, which compares two constants; +several authored provider rows lay outside the declared scope. The seven-case +overlap model could pass an illegal history. All 28 sites now use real +`IR.PropRef` field operands or legal union predicates; four multi-row fixtures +needed union scopes. Two adjacent example tests were corrected too. The overlap +driver now checks that its authored rows satisfy their actual predicates; it +does not use the production expression compiler to calculate expected public +rows. Reintroducing the literal-field predicate at the repaired head made all +seven overlap cases fail at that admission guard; restoring it passed. This is +an oracle integrity RED/GREEN, not a newly discovered production failure. + +The ownerless oracle gained empty disjoint, offset, and cursor neighbors, +matching and disjoint compound predicates, and a real SQLite restart with a +disjoint durable sibling. A proposed public assertion for that sibling was +rejected during testing: after restart, its on-demand scope had not been +acquired, so durable presence was the valid observation. The final test checks +the matching row's public and durable retirement and the sibling's durable +preservation. A broad-predicate mutant failed three scope assertions; a +window-classification mutant failed five, including empty offset and cursor +cases. These checks protect the conditional omission rule without promoting an +unacquired durable row into public state. + +The cursor integration oracle's manual-write property is important generated +coverage. It now has identical 50-run fixed-seed (`20261012`) and seedless +campaigns for three cache shapes, plus direct seed/path replay. Its opening and +local prose state the manual-write law, expected row fold, grammar, production +path, checkpoints, and limits. A temporary page-cache fault failed the page +state assertion and shrank to seed `20261012`, path `0:0:0:0:0`; direct replay +reproduced the same assertion. A simultaneous cleanup fault retained the law +failure as `AggregateError.cause` and recorded cleanup separately. Both faults +were removed. + +## Lossless finding ledger + +The IDs preserve each reviewer's claims, including early claims later corrected +by that reviewer. “Fixed” here means the oracle/documentation gap was repaired; +it does not imply a production patch. + +| ID | Original claim and evidence | Technical verdict and PR action | Durable value | +| --- | --- | --- | --- | +| A0 | “Query cache is source of truth” seemed to conflict with accepted on-demand local rows; source reasoning. | **Fixed now:** guide and coverage distinguish the unacquired local-write exception from an acquired cache projection. | Shared-cache authority boundary. | +| A1 | Mixed eager/on-demand sharing might leak a local edit to B; source-only, originally citing the unacquired-peer rule. | **Duplicate of S1:** reviewer corrected the rule's premise before testing. | Active versus unacquired peer witness. | +| P1 | Later complete omission followed by older matching response may revive x; source-only. | **Design decision:** same cross-scope negative-authority question as M1, with a direct row. No accepted promise yet forbids the later Query projection. | Explicit excluded schedule and proposed absent-key model. | +| M1 | Controlled direct-insert/empty-result/older-result probe made x disappear then return. | **Design decision:** observed behavior confirmed; stronger global-absence law remains unapproved. | Exact settlement-order witness and window/disjoint controls for any future law. | +| F1 | Full release and older-cache reacquisition might replay an old value; source-inferred. | **Duplicate of OF1:** executed and scoped below. | Reacquisition dimension. | +| S1 | Active mixed-mode B may project A's eager cache edit; source-traced, unexecuted at review. | **Fixed now:** paired active/unacquired oracle histories pass at write, held-fetch, cache, and provider-count cuts. | Mixed sync-mode grammar. | +| S2 | Derived eager peer select may miss a representable writer edit; source-only. | **Fixed now:** direct-property writer and filtered peer history passes without a request. | Distinguishes writer patchability from reader projection. | +| OF1 | Executed newer-value then full-release/old-cache replay with no new provider call. | **Fixed now:** guide's lifetime qualifier and oracle reacquisition lane match Query cache authority. The original “bug” label was overbroad. | Continuous-ownership versus new projection distinction. | +| OF2 | Executed newer empty scope followed by older returned x, without a direct write. | **Design decision:** returned-key law does not order negative claims from separate Queries. | Separate cross-scope absence proposal. | +| OWNERLESS-1 | Source-only direct-insert variant of P1. | **Duplicate of P1/M1.** | Per-key absent-position design sketch. | +| OWNERLESS-2 | Empty disjoint/offset/cursor and compound scope could evade current matrix; source-only. | **Fixed now:** legal neighboring histories pass; broad-predicate and window mutants fail at public-row settlement. | Conditional completeness/membership law. | +| OWNERLESS-3 | One-row unfiltered SQLite witness could miss broad durable deletion; source-only. | **Fixed now, bounded:** filtered SQLite witness keeps a disjoint sibling durable; no claim of public visibility before that scope loads. A persisted-only broad-deletion mutant was not run. | Durable receiving boundary and remaining mutant idea. | +| M2 | Seven overlap histories used literal/literal predicates and out-of-scope provider rows; source and QueryFn metadata. | **Fixed now:** legal scopes plus provider admission guard; restored old predicate fails seven cases. | Invalid-history classifier. | +| M3 | Versioned review lacked per-ORC outcomes; source-only. | **Fixed now:** requirement audit below, tied to repaired commit. | Guide conformance and explicit limits. | +| O3 | Cursor property had one seedless normal campaign; source-only. | **Fixed now:** equal fixed/seedless campaigns and checked direct replay. | ORC-007 enforcement. | +| O4 | Cursor manual-write law and grammar lacked opening/local prose; source-only. | **Fixed now:** literate contract, fold, grammar, limits and checkpoints. | ORC-001/003/004 evidence. | +| O5 | Final review record lacked ORC-012 audit; source-only. | **Duplicate of M3.** | Exact-head requirement record. | +| M4 | Twenty-eight ownership and two adjacent test literal-field scopes; source inventory. | **Fixed now:** every inventoried site repaired; 863 ownership/Query tests passed before later witness additions. | Same-class fixture audit. | +| S3 | Cursor cleanup could mask a property failure and skip QueryClient release; source audit. | **Fixed now:** cleanup diagnostics accumulate outside fast-check's verdict; simultaneous fault control retained the primary failure. | ORC-010 failure fidelity. | + +The raw count is **19**: 12 fixed, four duplicates, and three design +questions. No item is silently deferred. The three design IDs concern one +unapproved cross-scope absence rule; their executed traces remain available +for a future explicit law decision. The reviewers' corrections matter: neither +OF1 nor S1 establishes the production bug initially suggested. + +## ORC-001–014 audit at the repaired code commit + +| Requirement | Outcome and evidence or non-applicability | +| --- | --- | +| ORC-001 | **Met for the bounded claims.** The guide supplies shared-cache, returned-key, direct-write, and scoped-omission authority; the ownership opening and cursor opening state their laws and limits. Cross-scope negative authority is expressly outside the current promise. | +| ORC-002 | **Met.** Ownership expectations fold authored fetch/write positions and owner claims; cursor expected rows are cloned and folded independently of Query cache updates. The production expression compiler validates fixture legality only, not expected public values. | +| ORC-003 | **Met.** Ownership prose separates law, reference, seven-history matrix, real QueryClient driver, and public checkpoints. Cursor opening/local prose explains its manual-write law, row fold, 1–8-step grammar, real pager path, and per-write comparisons. | +| ORC-004 | **Met for the new generated cursor property.** Count 2–6, operations 1–8, and position 0–10 include both endpoints. A count-2 history of delete-0, delete-0, then update-0 reconstructs empty-to-insert recovery; one-step insert/update/delete histories are each constructible. Removing a write kind, position variation, or one of raw-sibling/raw-nested/selected-envelope shapes loses a distinct cache action or placement. Duplicate initial IDs are excluded as invalid keyed Collection input; overlapping fetches and derived selects are valid but outside this bounded grammar. Ownership matrices are finite enumeration, so the generated-grammar trigger does not apply to them. | +| ORC-005 | **Met at named cuts.** Real QueryClient and Collections expose public rows, cache data, provider calls, and exact result settlement. Node SQLite supplies matching-row retirement and disjoint durable preservation. The full package run passed 1,257 tests in 36 files, with no type errors. | +| ORC-006 | **Met for repaired claims.** Literal-field mutant failed seven overlap cases at admission; broad predicate mutant failed three scope cases; window mutant failed five; cursor page-cache fault failed and replayed the page-state assertion. Temporary mutants were restored. The original production behavior was not changed in this follow-up. | +| ORC-007 | **Met for the important cursor property.** Each of three shapes runs the same generator/checker/50-run budget once with seed `20261012` and once seedless. Replay selected the same property directly with seed/path `20261012`/`0:0:0:0:0`, ran one case, and reproduced the failed checkpoint. The ownership fixed histories are not important generated properties. | +| ORC-008 | **Met for model-state changes.** An accepted fetch position is distinguishable by a later older settlement; owner identity by a later release. In cursor, expected rows are distinguishable by the next update/delete and nextId by the next insert after deletion; collapsing either changes future public rows. No production state was copied into the reference. | +| ORC-009 | **Met.** “Query,” “Collection,” “owner,” “scope,” “fetch,” and “public row” follow the project glossary. Model-only positions map to authored operation order; `expected` and `nextId` are local row/key holders, not renamed subsystem states. | +| ORC-010 | **Met.** Ownership cleanup aggregates secondary failures. Cursor teardown attempts source and client release, retains the primary property failure as cause, and reports cleanup failures separately; an injected simultaneous fault verified that form. | +| ORC-011 | **Not triggered for these repairs.** No named semantic fault shared by the independent row/owner reference and production required a second formulation. The real SQLite case is a receiving boundary, not asserted to be an independent semantic model. The negative-scope proposal awaits a product law before modeling. | +| ORC-012 | **Met by this record for the repaired commit.** All other numbered requirements have outcomes here. The claimed class is limited to legal predicate histories, returned-key overlap under continuous ownership, representable cache projection, and scoped ownerless omission at named public/cache/durable cuts. The original and adjacent witnesses and wrong designs are above; open negative-scope and larger schedules remain in the coverage map. | +| ORC-013 | **Met within bounded laws.** Active versus unacquired peers, continuous ownership versus full release/reacquisition, complete matching versus disjoint/windowed empty results, and legal versus literal field predicates are neighboring premise controls. Their cache, public-row, provider-count, and durable checkpoints distinguish the wrong rules. | +| ORC-014 | **Met for the claimed receiving boundary.** Real Query Core supplies shared Query cache notifications and refetches; managed node SQLite supplies filtered complete response persistence. The cursor property uses a controlled in-memory page provider and makes no external-provider claim. Non-node persistence and external durable writers remain outside the evidence. | + +The full Query Collection suite passed 1,257 tests after the witness additions; +the cursor integration file passed 47 tests after its final teardown refactor. +Package TypeScript typecheck and build passed. ESLint had no errors (26 +warnings in changed test files), Prettier and `git diff --check` passed. The +literal-predicate, broad-scope, and window mutants were run against the +repaired commit and restored without changing the committed tree. This record +does not claim production fixes or universal coverage of arbitrary overlapping +requests. + +The four reviewers gave strong exploratory signal. One found a real test +integrity defect across 30 sites and calibrated it; another executed both +cache-replay and omission counterexamples; the others identified legal scope, +persisted, and mixed-mode boundaries. Several initial claims were revised +after checking the actual guide premise or the cache authority model. **Hire +for exploratory review, with executable calibration before product-bug +sign-off.** diff --git a/docs/contributing/oracle-reviews/2026-10-12-query-direct-write-authority.md b/docs/contributing/oracle-reviews/2026-10-12-query-direct-write-authority.md new file mode 100644 index 0000000000..6417b83ec1 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-12-query-direct-write-authority.md @@ -0,0 +1,130 @@ +# Query direct-write authority review — 2026-10-12 + +The external medium-effort review read PR #2065 at `9fdcd2e48` without running +tests. This record evaluates all ten claims against that revision and the +repair commit `99c1758e2b451065c88be0c30ebd782678ebf026`. The earlier +[review of `74e399150`](pr-2065-74e399-review.md) describes an intermediate +implementation; its provisional law choices do not govern this commit. + +## Laws, experiments, and inference + +The [Query Collection guide](../../collections/query-collection.md#direct-writes-and-query-sync) +and the maintainer's explicit decisions supply three laws. The Query cache is +the source of truth: compatible eager Collections sharing an exact physical +Query project representable direct cache edits. For a returned key shared by +overlapping on-demand subsets, the later-started successful fetch controls +its value even if an older request settles last. A successful complete +on-demand response may retire an omitted ownerless direct row in its scope; +subset unload or a disjoint, windowed, failed, or pre-write result cannot. +Direct writes themselves start no Query request. A physical fetch started +before an accepted write cannot undo its claimed keys. A local cache edit is +not a physical fetch for an on-demand peer that has not acquired the scope. + +The primary executable owner is +[`ownership-lifecycle.oracle.test.ts`](../../../packages/query-db-collection/tests/ownership-lifecycle.oracle.test.ts). +Its reference folds authored fetches and direct writes by position and tracks +subset owners independently of Query Core's cache-version and event +classifiers. Real QueryClient observers, public Collection rows, provider call +counts, selected cache envelopes, and managed node SQLite provide the driver +and observations. The cursor integration oracle separately receives eager +empty-cache seeding. Histories compare the write cut, held-request cut, +result application, both subset-release orders, cleanup/restart, and durable +storage after a complete fetch. Those intermediate cuts matter: final row +equality alone would miss a peer briefly adopting an unverified cache edit. + +The reviewed implementation was RED for reverse-settled overlapping fetches, +complete ownerless-row omission, and several eager shared-cache histories. +The added histories also exposed a reentrant notification bug: Query Core +notifies observers before its QueryCache success listener, so an observer can +write directly to the cache between the physical success action and that +listener. The old listener recorded the now-mutated cache state as fetched. +A late on-demand peer then settled on the local row without a request. The +new history was RED at the peer's held-request checkpoint and GREEN when the +listener used the success action's payload as fetch identity and retained the +local-edit marker. A real SQLite restart history verifies that the complete +empty provider response runs and removes an ownerless row from both public +state and durable storage. Offset, cursor, limited, disjoint, nonempty, and +failed-result neighbors distinguish complete omission from broad deletion. + +The eager composition history writes different keys from A and B while B's +result application is held. It requires the shared cache to contain both +edits, preserving unrelated rows and `{ items, tag }` envelope metadata; +raw-array and direct-select variants pass. A proposed assertion that B must +still show both edits *after* releasing `deferDataRefresh` was rejected: +release starts a new physical fetch, which may authoritatively replace them. +The held cache and unblocked A are the appropriate composition checkpoint. +The overlap model verifies values after each settlement and ownership after +releasing older and newer subset claims in both orders. The original code +failed three reverse-settlement histories. Hostile controls for alias fetch +positions, raw-only cache patch provenance, and sync-run marker identity +reached their intended public-row or held-request assertions. These are +finite instruments, not proof for every possible schedule. + +## Lossless finding ledger + +| ID | Reviewed claim | Evidence and disposition | Durable value | +| --- | --- | --- | --- | +| R1 | Old ownerless direct-write positions survive restart and suppress cached server rows. | **Fixed now.** A real QueryClient restart/cached-peer history was RED on the reviewed code and GREEN after retiring positions at run boundaries. | The owner checks insert/delete and cache-reacquisition neighbors. | +| R2 | Eager A's direct write reaches eager B through a shared Query. | **Accepted design.** The maintainer chose shared Query-cache projection. Raw and selected-wrapper composition histories enforce that choice. | The guide names representable edits and compatible peers. | +| R3 | An older subset fetch can replace a newer fetch's shared row. | **Fixed now.** Reverse-settlement histories were RED; accepted per-row positions preserve the later-started result's value while allowing the older subset to claim ownership. | Seven histories check prior ownership, both settlement orders, equal values, direct writes, and symmetric release. | +| R4 | A metadata gate always forces a nonpersisted ownerless-row refetch. | **Refuted as a behavioral claim.** A same-path nonpersisted probe kept its position and made no extra request. | Cold persisted collision and stable-empty scan histories cover the meaningful receiving boundary. | +| R5 | Direct `setState` sends no cache event and its counter use is wrong. | **Refuted as stated.** QueryCache emits `updated` with a `setState` action; success-only listeners intentionally do not treat the local edit as a fetch. | The reentrant success and local `setQueryData` histories guard fetch provenance. | +| R6 | The Query Core peer minimum rises from `^5.0.0` to `^5.90.15` in a patch. | **Accepted design.** This is factual; the maintainer explicitly chose to keep the patch. 5.90.15 passed the full package suite; 5.90.14 and 5.85.2 failed authority/error witnesses. | The changeset and coverage map state the minimum and reason. | +| R7 | Empty retained subsets repeat full persisted scans. | **Fixed now.** A controlled scan-count witness failed before the guard and passes afterward; a cold durable-owner control still scans. | Work is bounded for a stable empty retained baseline. | +| R8 | Ownerless direct inserts and positions can outlive subset unload. | **Accepted design** for the row's surgical lifetime after unload. A later successful complete matching response now claims or retires it; the reviewed code was RED for explicit empty refetch. | The guide and scope matrix state the exact settlement boundary. | +| R9 | Hot-path select and accepted-row work repeats per direct write. | **Fixed now.** Source comparison shows one changed-row snapshot and reuse of the inspected selected array. | No new latency law is inferred from a source-only cleanup. | +| R10 | A WeakMap holds an old fetched array; a version number would suffice. | **Refuted as an equivalent remedy.** One array is retained per live Query; `setState` deliberately does not bump `dataUpdateCount`, so a count alone loses fetched-payload identity. | Measure retained live-Query payloads before changing provenance storage. | + +The review comparison footnote that the prior `writingDirectCache` ownership +findings disappeared is accurate for that intermediate implementation. The +claim that R1/R3/R4/R7/R8 were new is provenance, not a separate defect. +Earlier parallel-agent findings are retained in the primary oracle and +coverage map rather than merged into these ten source-order IDs. + +## Sibling findings and oracle audit + +The fresh oracle reviewers found five useful enforcement gaps. The repaired +oracle now checks wrapped eager composition, offset and cursor exclusions, +durable persisted retirement and provider execution, and both ownership +release orders. The reentrant success history is a newly confirmed product +bug with its own RED/GREEN witness. A cursor integration history caught a +separate eager cache-seeding regression introduced during composition work: +an undefined raw cache entry received only the changed delta, losing other +rows. Seeding it from the accepted full snapshot restored that contract. +Source inspection also found that per-row accepted positions survived a +sole-owner unload after the public row was deleted. Cleanup now retires that +position after accepted deletion and the late fetch microtask checks that +the row still exists. This internal memory bound has no public-size assertion; +its proof is the map's insertion and deletion paths plus the passing ownership +suite, not a claimed heap benchmark. + +The oracle's law, reference, history, driver, and checkpoint are separated in +its literate prose and tests. Its expected rows come from authored actions and +owner sets, not production's event classifier. The real Query Core and node +SQLite paths are receiving witnesses; the provider payloads and timing remain +controlled. The final Query DB package run passed **1,247 tests in 36 files**, +including 321 ownership-oracle runtime tests and 174 Vitest type tests, with +no type errors. Package TypeScript and build passed; ESLint had no errors and +ten warnings. Prettier and `git diff --check` passed. + +The finite claim excludes incompatible eager selects, larger overlapping +fetch schedules, a newer complete omission followed by an older same-key +response, same-run external durable writers, non-node persisted adapters, +and local-patch provenance after Query cache dehydration into a different +QueryClient. The [coverage map](../oracle-coverage.md) assigns these +receiving boundaries. No known reachable counterexample remains within the +accepted returned-key and complete-scope laws exercised here. + +The reviewer found important real ordering and work gaps, especially R1, R3, +and R7, and prioritized several meaningful risks. It also inferred a missing +cache event, an unwanted nonpersisted refetch, and an equivalent version-stamp +optimization that did not survive same-path checks. It ran no tests and did +not separate policy questions from confirmed bugs. **Hire for exploratory +review, with reproduction required before sign-off**: its counterexamples +were valuable, but its conclusions were not independently reliable. + +The raw-item audit is complete: four fixed now (R1, R3, R7, R9), three +accepted designs (R2, R6, R8), and three refuted claims (R4, R5, R10). +There are no deferrals or unresolved product decisions in these ten items. +PR readiness still depends on verifying the pushed head and its CI; this +review record alone does not grant merge readiness. diff --git a/docs/contributing/oracle-reviews/2026-10-12-snapshot-selection-review.md b/docs/contributing/oracle-reviews/2026-10-12-snapshot-selection-review.md new file mode 100644 index 0000000000..44c0c758ae --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-12-snapshot-selection-review.md @@ -0,0 +1,152 @@ +# Snapshot selection review for PR #2086 + +The external review inspected `2e0afc8a9`; subsequent callback-key work +reached `6ec97f8a0` before this repair. This record distinguishes that earlier +calibration from the new controlled-provider experiments. The listed RED +results were reproduced against `6ec97f8a0` or the named hostile edit; the +GREEN results use the repaired working tree. The law concerns Collection +subscription selection and provider requests within the stated histories. + +The independent follow-up review covered implementation commit `eeb3a8673`. +Merge commit `91bef133d76990ce93256d4881d27182431c0fbf` adds only the +remote bot's formatting change to that implementation. It contains +`origin/main` at `f6857fee9de59dc82cbe596ca7df4a18152004ed`. The final +evidence commit in that earlier cycle changed this record only. The repaired full package run was +before that formatting merge; the four focused suites passed again afterwards. + +## Law and observation cuts + +`requestLimitedSnapshot` promises not to resend a key already sent to its +subscriber. `Collection.subscribeChanges` promises per-key causal order; the +change-event history owner in the coverage map requires a strict consumer's +valid insert, update, and delete stream. A **published** delete releases a +public key; a source delete held in a private replay has not done so. A later +ordered local snapshot must skip every key selected earlier, including a +private replay selection, and every still-public callback key. The Collection +API, the change-event owner, and the method contract in +`packages/db/src/collection/subscription.ts` authorize this selection rule. + +A selected or public key does not certify that all earlier provider rows were +fetched. The ordered-loading contract in +`packages/db/src/query/live/ARCHITECTURE.md` explicitly rejects a local row +as a remote offset or continuation boundary. Without `minValues`, an ordered +request with no explicit offset must cover a provider prefix wide enough to +admit its requested number of unseen rows. With `minValues` and no explicit +offset, an adapter may ignore cursor expressions; no finite bound derived +from local keys can reach an arbitrary cursor value, so provider acquisition +is unbounded. A locally selected key cannot supply `cursor.lastKey`. An +explicit offset supplied by the ordered source loader remains a separate, +caller-established boundary. + +The lifecycle publication owner models source rows in a plain Map. A separate +Set models subscriber-visible keys. It runs two source-action histories: a new +insert, and a delete followed by reinsert and a new insert. Each history runs +with filtering, full-state pass-through, and `includeInitialState: false` pass-through. Each +mode runs with and without a cursor. The driver uses a real Collection, its +ordered index, sync transactions, and a subscription. It checks callback +key/type batches after each source commit and after the ordered request. It also checks +the retained key count after deletion. That count is a deterministic capacity +observation, separate from the public-row law. + +The replay owner controls an accepted load promise. It compares public batches +while the truncate replacement is private and after the promise fulfills. It +also checks that private selections widen a later prefix request and that a +sparse retained row does not turn into a provider key cursor. A second replay +test checks a filtered snapshot selected during callback reentry. The +publication owner uses a controlled adapter over `[one, two, three]` to return +actual offset pages after sparse callbacks and ordered callback reentry. An +offset-only provider over three and seven ranks checks a high cursor value: +the request has offset zero and no finite limit, and its target row becomes +public. +The pagination recomputation oracle exercises cursor-expression and offset +transports. CollectionSubscription never supplies `cursor.lastKey`, so its +former key-transport cases exercised the offset path and were removed. The +TrailBase adapter test exercises an explicit last-key caller and its offset +fallback. All 16 pending-mutation transport cases pass in the two reachable +live-query modes. + +## RED and GREEN calibration + +| Subject | Reached comparison and result | +| --- | --- | +| Prior follow-up: restore the old `trackSentKeys` early return | Eight pass-through callback cases failed: both source histories duplicated `insert:two`, and delete/reinsert retained one deleted key. Four filtering cases passed. This earlier run established callback tracking, not provider coverage. | +| Prior follow-up: publish ordered snapshots during a private replay | The held-publication comparison saw early inserts for `one`, `two`, and `three` instead of only the initial `one` batch. | +| Prior follow-up: select held replay by `loadOptions.length === 2` | An extra empty subset load made the fixture hold the wrong request and caused early replacement publication. Matching the ordered request by its options passed that perturbation. | +| `6ec97f8a0` as the original implementation against the new provider witnesses | Sparse callbacks lost `two` in both initial-state modes, with and without a key cursor; ordered callback reentry lost `three` in skip-filtering mode without a cursor. After a successful sparse replay, a cursor honoring `lastKey` could not acquire `one` at the first request. Six of nine new cases failed at public-row comparisons. | +| `6ec97f8a0` against the high-cursor offset-only witness | All four three- and seven-row cases reached the adapter request assertion and failed with `limit: 1` instead of an unbounded request. The fixture cleanup completed; these failures are request-bound assertions, not setup failures or public-row comparisons. | +| Remove the regular snapshot reservation from the repaired tree | The private mixed-replay witness reached its provider-prefix comparison with limit `2`, below the independent minimum `3`. Its selected filtered row had not been reserved before publication. | +| Remove the ordered snapshot reservation from the repaired tree | The repeated ordered-replay witness reached the provider-prefix comparison with limit `1`, below minimum `4`; the mixed-replay witness saw `2` below minimum `3`. | +| Repaired tree | The provider, replay, selection, and subscription suites pass: 629 tests in four files. The package Vitest configuration passed 242 files and 11,789 tests with coverage and Vitest typecheck disabled; a separate source/test TypeScript check passed. The new provider and replay cases pass at their public-row checkpoints. | + +The high-cursor baseline and the two reservation mutants were run against the +latest witnesses in an isolated archive with the repaired test file. They +failed by value assertion, with cleanup complete; their failures did not rely +on a timeout or fixture error. The older last-enumerated-row cursor mutant is no longer relevant to the +accepted law: no locally derived `lastKey` is now sent. The old reset witness +in filtering mode remains narrower because callback tracking alone lets it +pass when the regular reservation is removed. The mixed replay witness owns +that private-selection requirement. + +## Oracle guide audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | The Collection callback and ordered-snapshot contracts authorize the public-key law; the live-query architecture denies a continuation boundary from local rows. The retained-key check is an internal work bound, not an API memory limit. | +| ORC-002 | A source Map, visible Set, and plain ordered remote array choose expected rows independently of subscription tracking. Replay expectations derive from initial and replacement row sets; a separate callback map records actual public rows. | +| ORC-003 | Each owner states its law and limits. The new comments place the model rule, legal histories, real driver, public cuts, and comparisons beside their code. | +| ORC-004 | The callback matrix crosses three subscription modes, two source histories, and both local cursor paths. Sparse callbacks, ordered callback reentry, and sparse replay add three-row provider histories; a high-cursor offset fixture uses three and seven ranks. They exclude arbitrary ties, predicates, and remote schedules. | +| ORC-005 | The driver calls real snapshot methods and source sync transactions. Raw callback batches retain duplicate inserts and their order. Controlled adapters apply actual offset and key-cursor pages; replay checks before and after settlement. | +| ORC-006 | The original implementation reached the public-row comparison in six of nine provider/replay cases and failed the request-bound comparison in all four high-cursor cases. Both current reservation mutants failed their request-bound comparisons. Earlier callback and publication mutants are listed separately. | +| ORC-007 | No important generated property changed. Both existing owners still run their fixed and random campaigns in the package test suite. | +| ORC-008 | The model keeps source, selected, and visible keys distinct. A source row absent from the subscriber can be selected next; a public key may still leave earlier provider rows unseen. Published deletion and reinsertion distinguish a prior selection from current visibility. | +| ORC-009 | Model source rows map to installed Collection rows; model visible keys map to the subscriber's public snapshot. The remote array models provider order, not a production index. Retained-key count is an internal observation, not model ownership state. | +| ORC-010 | Replay fixtures resolve held promises in `finally`. Current mutant failures reached value assertions and cleanup succeeded. These fixed witnesses do not separately report a future simultaneous cleanup failure. | +| ORC-011 | The sparse-provider counterexample challenges the old offset and key-cursor rule. The callback fold separately detects duplicate inserts that a final-key check would miss. | +| ORC-012 | This record names the original review head, the later baseline, finite histories, and the exact fresh mutant outcomes. | +| ORC-013 | Full-state and skip-filtering cases reach the pass-through premise; filtering is the neighboring control. Delete/reinsert and new-insert histories distinguish release from permanent reservation. The high-cursor request assertion rejects a plausible finite overfetch even when the three- and seven-row public results would pass. | +| ORC-014 | Controlled adapters supply replay timing and offset or key-cursor pages. Their three- and seven-row results demonstrate the Collection boundary but do not prove every real provider's pagination behavior. | + +Open scope: rank ties, other predicates, arbitrary and changing provider result +sets, provider-specific cursors, and compiled live-query windows outside the +bounded optimistic and settled-page histories remain with +the owners named in the coverage map. These controlled providers and +bounded histories do not prove every possible source schedule. + +## Second review: optimistic rows and source coverage + +The next external review inspected `ba6dcb3dd`. It found that a pending +optimistic delete can consume a bounded provider page without supplying a +visible row. The same source-coverage law also applies to optimistic inserts +and rank moves: a local row can fill the visible window, but it cannot prove +that the provider supplied an earlier rank. The ordered-loading architecture +authorizes this distinction. The pagination recomputation oracle owns the +compiled live-query window, while the lifecycle publication oracle owns the +direct Collection subscription request. + +The lifecycle owner checks one and two pending deletes or predicate-removing +updates at request submission. The remote array's next visible rank must lie +within the submitted provider page. The original bounded request and a +one-row-only headroom edit fail those comparisons. The pagination owner also +checks final public rows after authoritative source acceptance. Its continuation +matrix holds one or two inserts or rank moves, widens a window, and crosses +cursor-expression and offset-only adapters; insert histories start with zero +or one source row in the initial window. The array model sorts the remote rows +and applies the authored mutation without reading loader state. It requires a +request for the needed unchanged source rank when that request can start before +settlement, and exact public rows after the source commit. + +Before the source-coverage repair, six of the first eight continuation cells +failed at the request comparison: both offset-only inserts and all four rank +moves. The two-move history also failed at the final public window after the +earlier request assertion was moved past a legally held repair. Filtering +currently optimistic keys alone fixed the single-row cases but left that +two-move failure. The source commit could drop the overlay before a padded +request settled, allowing moved rows beyond its original requested count to +be mistaken for its provider boundary. Capping that boundary read at the +original count and checking the source-coverage deficit on delayed +continuations repaired the same history. Runtime hostile edits that disabled +overlay exclusion, optimistic headroom, the original-count cap, or delayed +coverage each failed a corresponding public-row witness. The complete +pagination file passed 289 tests; the four relevant DB files passed 641 tests; +TrailBase's adapter file passed 27 tests. These checks use controlled providers, +not a real Electric, query-db, or TrailBase backend. diff --git a/docs/contributing/oracle-reviews/2026-10-13-pr-2135-query-source-reuse.md b/docs/contributing/oracle-reviews/2026-10-13-pr-2135-query-source-reuse.md new file mode 100644 index 0000000000..99641f5ad2 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-13-pr-2135-query-source-reuse.md @@ -0,0 +1,173 @@ +# Query source reuse oracle review — 2026-10-13 + +The reviewed code head is `b9f7afaba36a3e30dfc29cdc7cf966fcb6247aff`, +following the first repair at `52c8c04b4102fd9aae091e896acc3ba3de92b3d3`. +This record covers the bounded full-source reuse, retained-subset, and shared +signal additions in the Query DB lifecycle and ownership oracles. The record's +follow-up commit updates review evidence and coverage limits without changing +the reviewed code. + +## Law, path, and checkpoints + +The [Query Collection guide](../../collections/query-collection.md) and the +Collection [sync-adapter contract](../../guides/collection-options-creator.md) +authorize the laws. An applied complete full-source Query may serve a narrower +on-demand demand while its Query remains owned. Query Core success before +Collection application, an incomplete source, a failed source, or a retained +subset awaiting revalidation cannot establish that narrower demand. A row +remains public and durable while any committed Query owner needs it. Releasing +one acquisition aborts its signal, while another observer may keep the same +physical Query and provider transport alive. A provider that ignores the Query +signal can finish into Query cache after final observer release. + +The [load lifecycle oracle](https://github.com/TanStack/db/blob/b9f7afaba36a3e30dfc29cdc7cf966fcb6247aff/packages/query-db-collection/tests/load-subset-lifecycle-oracle.test.ts) +drives real QueryClient observers and Collection acquisitions. It checks +provider scope and call counts, public rows, acquisition settlement, error +state, and cached rows at named application, invalidation, release, and +held-fetch checkpoints. The +[ownership oracle](https://github.com/TanStack/db/blob/b9f7afaba36a3e30dfc29cdc7cf966fcb6247aff/packages/query-db-collection/tests/ownership-lifecycle.oracle.test.ts) +uses controlled persisted storage and compares public rows, durable rows, +retention markers, and row owners when a retained subset reacquires. These +expected results come from complete source rows and ownership leases. They do +not copy the adapter's reuse gate, Query Core option selection, or retention +classifier. + +## Distinguishing evidence + +- The original #2135 code fetched a new filtered Query despite an applied, + active full result. The settled-full witness failed at the provider-call + checkpoint before the first repair and passed afterward. Initial and + replacement full-result histories reject settling a filtered demand before + Collection application; the replacement cut rejects relying on old applied + rows while new result application is pending. +- A controlled persisted `until-revalidated` subset remains independently + marked until its scope is reconciled. Omitted, unchanged, and moved row + histories check public and durable rows and marker removal at acquisition + settlement. The omitted case was RED before the first repair. An executed + hostile deletion of the moved row after its old subset owner retired failed + both public and durable row comparisons; a retained old owner fails the + moved-row owner comparison. +- Equal full and filtered peers, full-source dependents, and two Collections + sharing one Query distinguish logical acquisition signals from physical + Query ownership. Held initial and refetch histories put an equal filtered + peer before or after fetch start, then release the creating acquisition. + Before the second repair, three late-peer cases were RED at the public-row + checkpoint. Exact, custom, and fresh `AbortError` cancellation now reach the + peer; a no-peer release starts no replacement call. An initial ordinary + provider error rejects the pending peer and sets `lastError`; a refetch + error retains the prior applied snapshot and records `lastError`. +- The recovery-specific ignored-signal history was RED at the Query cache row + checkpoint when the recovery guard read Query Core's lazy signal. Removing + that read made it GREEN. Separate full-source and equal-filtered ignored + signal controls show that final unload can leave a provider result cached. + The SSR oracle keeps the sole direct request's exact metadata signal. +- Independent in-memory mutants reached their intended comparisons. Retrying + every provider error failed the unrelated-error public-row check. Retrying + only the exact signal reason failed both fresh-`AbortError` public-row + checks. An eager lazy-signal read failed the recovered-cache check. These + were assertion failures, not setup failures or timeouts. No mutant was + checked into the repository. + +The affected runtime and type suites passed **618 tests in six Vitest test files** +with no type errors. The Query DB package build passed. Changed-file ESLint +had no errors and ten warnings outside the changed lines. The finite matrix +and mutants establish only the named contract, histories, production paths, +and checkpoints; they are not a proof for arbitrary predicates or schedules. +The focused rerun command is `pnpm --filter @tanstack/query-db-collection exec +vitest run --coverage.enabled=false tests/load-subset-lifecycle-oracle.test.ts +tests/ownership-lifecycle.oracle.test.ts tests/ssr-dehydration-oracle.test.ts`. +The documentation link check still reports 32 existing links outside `docs`; +this record adds none of them. + +## ORC-001–014 audit + +| Requirement | Outcome for these additions | +| --- | --- | +| ORC-001 authority and limits | Pass within the documented on-demand Query and controlled persistence boundary. Complete-source provider behavior remains a premise, not a fact proved for every backend. | +| ORC-002 independent judgment | Pass. Expected rows and owners derive from the authored source and lease graph, not production's reuse or retention branches. | +| ORC-003 literate responsibilities | Pass. Opening and local prose name the law, model rule, legal timing, real production path, observation, and settlement checkpoint. | +| ORC-004 grammar controls | Not triggered. These are fixed, bounded timing and value matrices rather than a generated-history claim. | +| ORC-005 path and observation | Pass. Real QueryClient and Collection paths reach provider, public-row, public-error, cache, and durable-row comparisons at the stated cuts. | +| ORC-006 calibration | Pass for the bounded claims. Original code and the hostile mutants above fail at their intended comparisons. | +| ORC-007 campaign and replay | Not triggered. No important generated property was added or changed. | +| ORC-008 model-state minimality | Not triggered. The reference ownership state was not split, combined, or expanded. | +| ORC-009 vocabulary mapping | Pass. Prose separates logical demand, acquisition signal, observer lease, physical Query, transport, applied settlement, and durable row owner. | +| ORC-010 failure fidelity | Partial. `try/finally` cleanup and caught pending loads release resources, but no injected cleanup fault proves that a cleanup exception cannot replace a primary mismatch. The fixed witnesses do not shrink or capture generated histories. | +| ORC-011 second formulation | No second semantic formulation was required within the controlled boundary. Real provider completeness and abort shape need receiving witnesses under ORC-014. | +| ORC-012 review evidence | This versioned record ties the audit and mutant results to the reviewed code head; open cells are assigned below and in the coverage map. | +| ORC-013 boundary witness | Pass for the bounded laws: pending/applied full result, sole/shared and before/after fetch-start ownership, exact/fresh abort versus ordinary error, and omitted/unchanged/moved retained rows have distinguishing neighbors. | +| ORC-014 controlled handoff | Open. The controlled provider and storage do not prove real network abort shape, complete unfiltered responses, or native SQLite cold retained replay. The coverage map assigns receiving witnesses. | + +## Scope that remains open + +The adapter cannot distinguish a fresh `AbortError` caused by the creator's +signal from an unrelated fresh `AbortError` delivered after that same abort. +Its one-retry policy favors the surviving peer and can retry the latter once. +A stronger causal guarantee needs provider provenance; the oracle does not +claim one. A synchronous provider throw after reentrant peer acquisition has +no separate witness. The external `queryClient.cancelQueries` pending-load +settlement policy needs a public acquisition/status witness and is not a +confirmed bug from the current signal-state probe. The late-peer matrix +checks the surviving peer's promise; its retired creator's raw internal +`_sync.loadSubset` promise can fulfill after release. The guide's explicit +abort-then-reject rule addresses a request-scoped `commit(signal)` receipt, +and the existing public LiveQuery retirement oracle tests a different timing +cut. A public live-query preload witness for this late-peer cut and an explicit +raw adapter-settlement policy remain open. + +The [coverage map](../oracle-coverage.md) owns further witnesses for arbitrary +predicate containment, function-built Query keys, TTL and native SQLite +retention, real network cancellation, cross-Collection Query option selection, +and the policy for an equal demand arriving after failed full revalidation. +The requirement that an unfiltered on-demand provider return the complete +source remains a maintainer contract and versioning decision. Source reuse +may retain and refetch a complete source rather than doing narrow work; this +finite correctness oracle does not establish a memory or request-efficiency +bound. + +## Follow-up review of direct-write request scope + +CodeRabbit review `5510417620` on head `486eac00a` found that the direct-write +witness at `load-subset-lifecycle-oracle.test.ts:449` filtered its provider-call +record to `full` requests. Its name claimed full-source reuse, while the test +could pass after an exact filtered request. The independent oracle reviewer +ran the real QueryClient path and observed `[full, filtered]`, with the promised +`a1` and `b1` rows public after preload. This is permitted by the Query +Collection guide: a direct cache patch is not a new fetched result for an +unacquired scope. The original settled-full witness, with no intervening direct +write, separately requires `[full]` and establishes reuse at that cut. + +The oracle correction at code head +`a16fc95e42811ef25b457c86fab89059813851f5` names the direct-write law and +compares the entire request sequence. Its bounded history permits `[full]` or +`[full, filtered]`: one new filtered demand, a successful provider, and no +retry, invalidation, or explicit refetch need no second request. A hostile +production-path mutant called the filtered provider twice; the corrected test +failed at the request-sequence assertion on `[full, filtered, filtered]`. The +unmodified path passed **98 runtime and type tests** in the load-lifecycle file +with no type errors, and changed-file ESLint passed. No production code changed. +This correction strengthens ORC-003, ORC-005, ORC-006, and ORC-013 for the +direct-write witness. It makes no claim about every later demand or about +reuse after a direct write. + +## Retained ownership exception and companion handoff + +The guide's initial full-source reuse sentence omitted an accepted exception: +a narrower scope with retained persisted ownership needs revalidation before +its old row and owner can retire. The current adapter makes an exact-scope +request for that case. This follow-up clarifies the guide and changeset; it +does not change #2135 production code or the existing same-version retained +subset oracle. That oracle already crosses omitted, unchanged, and moved rows +after an applied full result and checks public rows, durable owners, and the +retention marker at subset settlement. + +Companion #2134 code head `5239c25c2` adds a fixed historical-key handoff +with an active applied full Query. Marker-present and markerless variants both +pass on #2134 and in a scratch merge with #2135 head `d429e0dd8`. In that +merge, removing historical pending revalidation from the reuse gate fails +both variants at the immediate public-row assertion. Discovering old aliases +from markers alone fails only the markerless variant; the marked control +passes. The #2134 versioned review records the independent oracle challenge +and full evidence. The test permits either an exact request or safe +reconciliation from the applied full result, so the guide's current exact +request behavior is not an oracle requirement. diff --git a/docs/contributing/oracle-reviews/2026-10-14-ivm-structural-identity-normal-form.md b/docs/contributing/oracle-reviews/2026-10-14-ivm-structural-identity-normal-form.md new file mode 100644 index 0000000000..6fae86979a --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-14-ivm-structural-identity-normal-form.md @@ -0,0 +1,175 @@ +# Structural identity oracle review — 2026-10-14 + +Reviewed executable head: `e1cd28afb6b9bd76641b42a06110eb184b5fac1a`. +Base: `87bc07354fa47df48e9b05c599f24dad74dea0f3` (`origin/main` at review). +The binary diff for the oracle, replay registry and manifest, and coverage map +has SHA-256 `9e94b37b8a03caa07f457d80d82a7aff399f70e3b47a6c59fe10190b43071c84`. +This record is added after the reviewed executable head; it changes no test or +production behavior. + +## Law, failure, and observation + +`structuralIdentity` must give equal D2 values the same result, including two +undefined results, and must not give unequal values the same defined string. +The contract is stated beside `structuralIdentity` in `hash.ts`; `groupBy` +depends on it when it merges contributions. The oracle checks the direct +`structuralIdentity` and `equalHashValues` return values after each call. It +does not claim to prove `groupBy` integration, which the incrementalization +law owns. + +On untouched main, the independent-pair property failed at seed `1158727847`, +path `255`: an explicit array around `deep(5, value)` and `deep(6, value)` +built equal arrays, but the model gave their specs different normal forms. This +was an oracle model error. The repair expands `deep` shorthand into repeated +one-element array normal forms. A named equal pair preserves the original +witness, and an adjacent-depth pair checks that the model still distinguishes +one missing array level. The equal-pair grammar now generates both same-spec +and explicit-array/`deep` spellings. A structural grammar filter excludes +RegExps with a NaN `lastIndex` from that equal-pair lane; the independent-pair +lane still generates them. + +The collision sweep added two distinct symbols with the same description and +Map, Set, and plain-object pairs with equal entry bodies. Their different D2 +values must retain different defined identities. The alphabet includes `0` so +an object can reconstruct the index key of a Map or Set body. + +## Grammar controls and executed calibration + +The generated specs cover primitive leaves, short strings, arrays with holes, +objects with unique keys and optional enumerable symbol keys, Dates, RegExps, +Maps, Sets, small byte arrays, and `deep` nesting. Arrays have 0–3 items; +objects have 0–3 unique string keys; strings and keys have 0–4 characters; +binary values have 0–3 bytes from 0–2; generated `deep` nodes have depth 3–6. +The equal-spelling bridge uses depth 3–5 plus one explicit array. The +equal-pair lane varies prototypes, key order, and hidden symbols through a +64-Boolean choice stream. Duplicate object keys and cyclic specs are excluded +from the grammar. A separate fixed case checks that a cyclic object gets no +identity. The NaN-RegExp exclusion applies only to equal pairs. + +Two fixed equal-pair witnesses assert that the builder produces an array +subclass with a hidden symbol and class/null-prototype objects with the +requested key order and hidden symbol. Each compares the variant against a +plain value through the model and production entry points. + +The named equal/adjacent `deep` pairs reconstruct the reported and neighboring +cases. Same-description symbols and equal-body Map/Set, Map/object, and +Set/object pairs reconstruct the four collision boundaries. With the old +`deep` model and the equal-spelling bridge removed, both 2,000-case campaigns +passed; restoring the bridge made the fixed campaign fail after three cases at +`model vs equalHashValues`. Replacing symbol identity with its description or +collapsing any of the three object-kind tags fails a named pair at the shared +identity assertion. Date-as-number, binary-as-string, and bigint-as-number tag +mutants fail the generated comparison. The earlier length/header controls +exercise string length, key length, array length, and Date/RegExp headers. +Four isolated mutants made identity sensitive to key order, object prototype, +hidden symbols, or array subclass. Each failed its named representation witness +at `equal values`. Holes versus undefined, enumerable symbols, inner Map/Set +content, and byte content are generated and modeled; this review did not +execute a fresh mutant for each of those axes. + +The old `deep:${depth}` model mutant fails the ordinary fixed equal-pair +campaign at seed `20261014`. The shrunk counterexample is an explicit array +around `deep(3, 0)` versus `deep(4, 0)`. Both the unshrunk and shrunk failures +reach `model vs equalHashValues`. Guarded replay of the shrunk path records one +run with `failed: true` for that mutant; the reviewed head records one run with +`failed: false`. The direct replay path is +`2:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0`. +The independent-pair replay at seed `1158727847`, path `8092`, also records +one successful run. Each generated property uses the same 2,000-run generator, +driver, and comparison for its fixed and seedless campaigns. The manifest +selects just the requested property for seed/path replay. + +The reviewed oracle file passes 17/17 tests, including guarded replay of the +shrunk path. Typecheck, lint, formatting, and diff checks pass. Hostile-mutant +results above are assertion failures at the named comparison, not setup errors +or timeouts. + +## ORC disposition at the reviewed head + +| Requirement | Disposition and evidence | +| --- | --- | +| ORC-001 | Met. The opening law and `hash.ts` contract identify authority; the coverage map bounds the owner. | +| ORC-002 | Met. `normal(Spec)` computes expected identity independently of hashing. The equal-pair exclusion uses a separate structural NaN-RegExp predicate, not `normal`. | +| ORC-003 | Met. Opening prose states law and limits; the spec grammar, `build` path, `normal` model, and `expectAgreement` return-time comparison remain distinct beside their code. | +| ORC-004 | Met for the bounded grammar above. Named and generated witnesses reconstruct the repair and collision axes; fixed representation witnesses confirm subclass, prototype, key-order, and hidden-symbol choices reach production. Mutants reject those four sensitivities, and removal of the equal-spelling bridge lets the old model pass both campaigns. Bounds and excluded states are explicit. Other axes were not freshly mutated. | +| ORC-005 | Met. The real hash entry points run, and equality/identity are compared at synchronous return. No concrete missing-input hypothesis applies to these fully constructed values. | +| ORC-006 | Met. The old-model and collision mutants fail at `model vs equalHashValues` or `a shared identity`. The reduced old-model failure preserves the same law and checkpoint. | +| ORC-007 | Met. Both properties run fixed and seedless 2,000-case campaigns; manifest-backed guarded seed/path replay selects one property and emits one-run witnesses. | +| ORC-008 | Not applicable. `normal` is a stateless recomputation; this change adds no model state or transition. | +| ORC-009 | Met. The model maps `deep` to repeated arrays and symbol leaf indices to stable symbol instances; the shared D2 terms follow the glossary. | +| ORC-010 | Met for this harness. The reporter preserves the original comparison failure, seed, path, and reduced failure; tests acquire no resource needing cleanup. | +| ORC-011 | Not applicable. The review identified no remaining concrete shared semantic fault for which a second formulation would distinguish this bounded law. `equalHashValues` is a sanity observation, not the model source. | +| ORC-012 | Met by this exact-head record, including the applicable and non-applicable requirements and the bounded repair claim. | +| ORC-013 | Met. The equal array/`deep` pair and adjacent-depth pair distinguish the nesting rule from a lost-array-level rule at the model/equality comparison. | +| ORC-014 | Not applicable. Values are constructed in-process; no controlled provider or host premise is handed to a real integration claim. | + +The repair closes the known false-red trace and tested nearby structural +identity collisions for finite acyclic specs at the direct helper boundary. +Temporal values, opaque reference values and functions, large binary values, +getters or proxies, arbitrary cyclic pairs, and mutation between observations +remain outside this owner. The coverage map assigns the separate `groupBy` path +to the incrementalization law. + + +## Post-merge independent review — 2026-10-16 + +Executable head: `4d9f3fffce65e9ed34567bc2ebf4a3051e20c159`. +Base: `2c83fdc53bd762fa8b8d5e5a8b82e092710618be` (`origin/main` at merge). +Merge commit `e61027695` preserved the earlier test behavior and retained the +coverage entries added on main. Three independent reviewers examined the merged +oracle's model, input grammar, and replay integration. + +The model review found a pre-existing product defect outside the numeric +RegExp grammar: two frozen RegExps with separate frozen object-valued +`lastIndex` headers compare unequal under D2 but share a defined structural +identity. A real `groupBy` witness merges their contributions. This remains +open in [#2192](https://github.com/TanStack/db/issues/2192); the numeric bound +is now explicit in the oracle and coverage map. Returning no identity alone +would leave equal structural hashes on this pair. + +The grammar review found two false greens in the previous 17-test oracle: +a Map/Set identity that serializes only its first entry, and an array identity +that omits enumerable non-index keys, each passed its fixed and fresh campaigns. +The executable head above generates zero to two Map/Set entries and an optional +enumerable array field. Pinned second-entry and field-difference pairs reach +the direct `structuralIdentity` and `equalHashValues` comparison. In scratch +mutant runs, the first-entry-only mutant failed both pinned Map/Set cases at +`a shared identity`; the array-field mutant failed its pinned case and a +generated independent pair at the same checkpoint. These were coverage gaps, +not observed production defects. + +The revised Set builder wraps its second member in a fresh tagged array, so +JavaScript cannot deduplicate two equal primitive inputs before the comparison. +This lane does not test native Set deduplication or reorder two unwrapped +members; the separate hash-identity owner covers insertion-order behavior. + +The old `deep:${depth}` model mutant still fails the fixed equal-pair campaign +after three cases at `model vs equalHashValues`. The reduced replay seed +`20261014` and path +`2:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0` +executed one failed mutant case and one passing repaired-model case. The +updated focused suite passed 20/20 tests, including fixed and fresh generated +campaigns; five repeated suite runs passed. Typecheck, lint, formatting, and +diff checks passed. The integration reviewer found no merge defect. + +| Requirement | Post-merge disposition | +| --- | --- | +| ORC-001 | Met within the declared finite grammar; reference-valued RegExp headers remain an open product defect in #2192. | +| ORC-002 | Met. Spec normal forms judge the generated values independently of hash code. | +| ORC-003 | Met. The opening law and limits, model, grammar, builder, production calls, and synchronous comparison remain visible together. | +| ORC-004 | Met for the revised bounds: zero to two Map/Set entries, optional array field, and numeric RegExp headers. Pinned pairs reconstruct the two survivor boundaries; their mutants now fail. | +| ORC-005 | Met for the direct helper boundary. `groupBy` integration is a separate receiving path. | +| ORC-006 | Met. The old deep model and both former survivor mutants fail at the stated comparisons. | +| ORC-007 | Met. Fixed and seedless campaigns retain the same properties and 2,000-case budget; direct seed/path replay retains one executed case. | +| ORC-008 | Not applicable: the normal-form model is stateless. | +| ORC-009 | Met. `deep` is declared model shorthand for nested arrays; the second Set member's wrapper preserves its JS insertion count. | +| ORC-010 | Met for this harness. Shrinking and direct replay preserve the comparison failure; no resource cleanup is involved. | +| ORC-011 | Open for the larger RegExp header domain: #2192 owns a reference-identity model and a `groupBy` receiving witness. The numeric grammar does not claim that domain. | +| ORC-012 | This addendum binds the revised evidence and open cells to the executable head above. | +| ORC-013 | Met. Equal and adjacent-depth pairs distinguish the nesting law; second-entry and array-field pairs distinguish the new grammar boundaries. | +| ORC-014 | Not applicable: all values are built in-process without a controlled external provider. | + +The PR repairs the false oracle failure for equivalent nested arrays at the +direct helper boundary. It changes no production code. Structural identity +correctness for reference-valued RegExp headers and the receiving `groupBy` +path remains open in #2192. diff --git a/docs/contributing/oracle-reviews/2026-10-20-on-demand-network-first-law.md b/docs/contributing/oracle-reviews/2026-10-20-on-demand-network-first-law.md new file mode 100644 index 0000000000..0218c7d260 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-20-on-demand-network-first-law.md @@ -0,0 +1,370 @@ +# Draft law: persisted demand coverage + +Status: implemented in PR #2263, which also contains the oracle, the matching +`ARCHITECTURE.md` and glossary edits, and the review record +(`2026-10-21-persisted-demand-coverage-review.md`). The law revised an +accepted contract; it was not a defect report under the old one. It belongs to +issue #2102, item 1. Later sections record maintainer decisions made during +review, with their dates. + +## Question + +A user turned on SQLite persistence for an app. `initialRender: { strategy: +'network-first' }` worked for eager Collections but not for progressive +Electric ones. They expected progressive Collections to render from SQLite +without waiting for the network. + +Electric reports `progressive` to core as `on-demand` +(`packages/electric-db-collection/src/electric.ts:1216`). The persisted wrapper +rejects network-first for `on-demand` +(`packages/db-sqlite-persistence-core/src/persisted.ts:6833`). Every on-demand +source has this limit, not only Electric. + +The product need is offline use. Offline, an on-demand source can serve a query +only from an exact query that was persisted before, unless everything was +synced. Serving everything is one reason to use progressive mode. + +## Evidence gathered + +Two witness files record current behavior on main at 31dad2dd3. They state no +contract. + +`packages/db-sqlite-persistence-core/tests/on-demand-offline-readiness-witness.test.ts` +uses a generic on-demand source: + +| Upstream | Local subset | Query rows | Query ready | +| --- | --- | --- | --- | +| never settles | complete | `a1, a2` | no | +| never settles | partial | `a1` | no | +| never settles | empty | none | no | +| settles at once, no write | complete | `a1, a2` | yes | +| settles at once, no write | empty | none | yes | + +`packages/electric-db-collection/tests/electric-progressive-offline-readiness-witness.test.ts` +runs two Electric `progressive` sessions over one real `node:sqlite` database. +Session 2 is offline: + +| Session 1 | Session 2 resumes stream | Query rows | Query ready | +| --- | --- | --- | --- | +| completed full sync | yes (`10_0`, `shape-1`) | `1, 2` | yes | +| loaded the query subset only | no | `1, 2` | no | +| nothing | no | none | no | + +What the evidence establishes, for these histories: + +1. Restored rows reach the query before readiness in every on-demand case. Only + the readiness signal waits. +2. After a completed progressive full sync, Electric resumes and settles loads + at once. The query is ready on SQLite rows with no network. +3. If the full sync never completed, the persisted subset rows are present, but + the query waits on the network with no deadline. +4. The wrapper cannot tell a complete subset from a partial or never-loaded + one. All three read back as rows, or as no rows. + +The witnesses do not cover React or Solid Suspense timing, native SQLite hosts, +cache-claim rotation, or a must-refetch during the offline session. + +## The law + +**Persisted demand coverage.** An opted-in persisted wrapper over an on-demand +source may settle a demand's load from SQLite. It may do this when all of these +hold: + +1. The persisted subset restore for the demand has published its rows in the + current sync run. The rows may be empty. +2. The exact demand has persisted demand coverage in the current persisted + cache generation. +3. The configured wait has passed since the load started, or the demand's + upstream load has failed. + +All three must hold, in any order of arrival. An upstream failure that +arrives before the persisted subset restore publishes settles the demand +once the restore publishes. Until then, the load waits for the upstream load +as it does today. A demand without coverage never settles from SQLite. + +After the load settles from SQLite, its upstream load continues as work that +the wrapper owns. Rows from that load publish as ordinary source writes. A +later upstream failure does not undo the settlement. Releasing the demand, or +cleanup, aborts the upstream load. Coverage survives release: a later load of +the same demand starts its own wait and may settle from SQLite again. +Settling from SQLite does not change Collection readiness. + +An upstream load that settles successfully records coverage for its exact +demand. Coverage is written durably with the load's rows or after them, and +records how many rows the demand reads from SQLite at that moment. Three more +rules came from review: + +- A load that a reset made obsolete records no coverage (law 6 in + `ARCHITECTURE.md`). +- A load with `refetch: true` never settles from SQLite. It starts a new + acquisition that waits for its own applied rows. +- Coverage counts only while the persisted subset restore returns at least as + many rows as coverage recorded. A row deleted afterwards, by the source or by + Query DB retention, ends coverage until the demand loads online again + (maintainer decision, 2026-10-26). An unordered demand counts only rows: one + that loses a row and gains another keeps its count and stays covered, even + when retention removed the lost row (maintainer decision, 2026-10-26, + chosen over recording every row key, whose storage grows with the demand). +- Coverage is bookkeeping beside a load. A coverage read that fails proves + nothing: the load records no coverage, or waits for its upstream load, and + otherwise keeps its own outcome. A load released during its coverage check + owns no wait. + +The first draft also let a certified complete resume baseline cover every +demand. No path implemented that, and a source that resumes from a complete +baseline already settles its own loads at once, so the clause was dropped +(maintainer decision, 2026-10-26). + +### Terms + +These require glossary entries; the draft entry is in +`docs/contributing/glossary.md`. + +- **persisted subset restore**: the wrapper's read of one demand's request data + from the current persisted cache generation, and publication of those rows to + the Collection, in the current sync run. The result may be empty. +- **persisted demand coverage**: a record, in one persisted cache generation, + that a load of one exact demand reached applied settlement with its rows + durably committed. + +### Why coverage matches by exact demand + +Coverage matches by the canonical demand key (`getLoadSubsetDemandKey`). The +law never infers that one demand covers a different +one. #1797 made the same choice for request deduplication. It removed subset +algebra after it treated "requests as established coverage." Law 7 in +`ARCHITECTURE.md` already says settlement proves no broader extent than the +exact request. + +Without coverage, an empty or partial local result is not evidence. The +witnesses show the wrapper cannot tell "no matching rows" from "never loaded." +A fallback on such a demand would render a false empty or partial result. + +Coverage records completeness at the time of the covered load. It says +nothing about freshness, just as an eager restore can be stale. + +### What follows without new rules + +The live-query architecture routes every readiness consumer through demand +settlement. Settling a covered load therefore reaches all of them: + +- initial-query readiness and `preload()`; +- React and Solid Suspense, which follow initial-query readiness; +- `queryOnce`; +- `skipInitial` Effects; +- atomic ordered-window publication, so a covered next page publishes offline; +- demand added after the first render, such as includes children and new + filters. + +`ARCHITECTURE.md` already states that a source resuming from complete local +rows settles its loads at once. This law applies the same idea to one demand +instead of the whole Collection. The live query adds one query-level signal, +described next. + +### Queries over several sources + +The sources combine. A query may render from local data when every source +permits it: + +- an eager source, after its persisted restore completes in the current sync + run; +- an on-demand source, after every demand the query reaches on it has settled, + from SQLite or from the network. + +A source failure does not change these conditions. It never settles a demand, +and it does not keep a settled demand from permitting a render: a covered +demand that settles from SQLite after a source reports an error still permits +the fallback render, as an eager source's restore does after a source failure. + +This is initial-query readiness with one substitution: an eager source's +persisted restore stands in for its Collection readiness. A query over only +on-demand sources needs no substitution, because its readiness already follows +its loads. The eager network-first law in `persisted-readiness-oracle.test.ts` +is unchanged for a query over only eager sources. + +Today the observer's network-first gate runs only when every source registers a +`PersistedReadinessSource` (`collectPersistedReadinessSources` in +`packages/db/src/live-query-observer.ts`). That interface reports one state per +Collection, but demand settlement differs by query: two queries over the same +on-demand source can be in different states. An opted-in on-demand source +therefore registers only that it opted in, with no settlement state. The gate +reads settlement from the live query itself: the query's own initial +readiness, with each eager source's persisted restore standing in for that +source's Collection readiness. An opt-in alone never permits fallback; that +would release a join before the on-demand side's covered rows are visible. + +The live query does not expose that signal today. `preloadForInitialRender` +combines the network `preload()` with per-Collection persisted snapshots; it +cannot ask whether the query's own demands have settled independently of +eager Collection readiness. The implementation must add that query-level +signal: initial readiness with each eager source's persisted restore in place +of its Collection readiness. + +Update, 2026-10-27: the source-health change (#2168) removed the observer's +network-first gate and `PersistedReadinessSource`. A network-first eager +Collection now becomes `ready` from its restored rows after the deadline or a +remote failure, so the substitution above happens in Collection status +itself. Query readiness is the signal, and this law adds nothing to core: an +on-demand source permits initial readiness, and the query is ready when its +demands on it have settled. How a source error reaches the query's status is +the source-health law (`packages/db/tests/source-health-oracle.test.ts`); the +two failure paragraphs above are superseded by it. + +### The wait + +The wait reuses the network-first option and its default. #1955 set that +default to 3 seconds (`DEFAULT_NETWORK_TIMEOUT_MS`), and 0 is valid. The wait +is a preference for fresher data while online, not a correctness condition: +removing it breaks no property above. At 0, a covered demand behaves like a +demand on a completed progressive resume today. + +Each load measures its wait from its own start. Only that load's own upstream +failure ends the wait early. + +### Invalidation + +Coverage belongs to one persisted cache generation. It ends with that +generation and with any reset that replaces the durable rows: truncate, +must-refetch, schema reset, or cache-claim rotation. A record that memory still +holds after a rotation in the same sync run does not count. A crash between +writing the rows and writing the coverage must not leave coverage without rows. +Coverage also ends when the restore returns fewer rows than it recorded. + +Update, 2026-10-28 (high review of the head after #2168): three consequences +of existing terms are now enforced. + +- A snapshot transaction replaces every synced row, as a truncate at its + start does (glossary, "snapshot transaction"), so it ends coverage too. +- A load that core aborted decides nothing, even if its source resolves it + (glossary, "abort"), so it records no coverage. +- Scoped recovery ends coverage and does not certify the new generation for + offline reuse (`docs/collections/electric-collection.md`). Its persisted + subset restore publishes no rows, so coverage cannot count during the + rest of that sync run: no load settles from SQLite and none records. + +### Limits + +- **Coverage lifetime after release.** After a demand is released, its source + stops sending changes for it. Coverage stays valid until its generation ends + or a later load of the same demand settles. An expiry option, like Query DB's + `persistedGcTime`, is tracked as a separate issue and is not implemented. +- **Keys that depend on other rows.** Cursor pages and includes children build + demand keys from other rows' values. If restored rows differ from the + previous session, the key differs too. That demand is then uncovered and + waits for the network. +- **Uncovered demand while offline.** It waits indefinitely, also after a + source reports an error: the network-first wait neither renders nor + rejects. The in-progress error-reporting work owns how that condition is + reported. +- **A failure after a settlement from SQLite.** The query stays ready and the + failure does not reach it, so the application cannot tell that a refresh + failed. The in-progress error-reporting work owns whether such a failure is + reported elsewhere; the guides must state the behavior. +- **Optimistic writes while offline.** An optimistic mutation that touches a + covered demand's rows is outside this law. Optimistic overlay rules apply as + usual; the oracle checks one write and its rollback on a demand settled + from SQLite. +- **Ordered windows.** A window settles from SQLite only while its restore + holds the rows it held when it settled (maintainer decision, 2026-10-26). + Coverage of a limited load records its row keys, and the restore must + return the same keys in any order. The tie-boundary load's key comes from + the restored boundary row, so most changed windows also ask for a key that + no load covered; the recorded keys catch a row replaced or gained without a + boundary change. +- **Failed coverage reads.** A coverage read that fails, including one under + an expired cache claim, proves nothing. The wrapper records no coverage, and + a load that would settle from SQLite waits for the network. Neither path + fails a load whose rows applied. An expired claim also starts claim + recovery. A failed coverage write is a durable failure, which fails the + runtime as any failed source transaction does. +- **Source settlement.** The coverage read waits behind every source + transaction already committed, so it counts their rows durably, also for a + source that answers synchronously. It relies on the source committing a + load's rows before it settles that load (law 7 in `ARCHITECTURE.md`). +- **Multi-tab coordinators.** By maintainer decision (2026-10-26), the law + covers only the default `SingleProcessCoordinator`. Under a Browser or + Electron multi-tab coordinator, demand routes through the coordinator, so a + covered demand neither settles from SQLite nor records coverage; it waits + for the network as before. +- **Electric's own load.** Electric's upstream load may stay pending after the + wrapper settles from SQLite. Its release and abort ownership need a receiving + test. + +## Oracle design + +**Owner.** `packages/db-sqlite-persistence-core/tests/persisted-demand-coverage-oracle.test.ts` +owns this law. It sits beside the eager network-first owner, +`persisted-readiness-oracle.test.ts`, under the same coverage-map section. +The live-query consequences listed above stay with their existing owners. A +receiving witness is needed for the window-publication case. + +**Independent model.** For each demand, the model records the local restore +(`pending`, `done`, or `error`) and the coverage (`covered`, `uncovered`, or +`baseline`). It also records the upstream state (`pending`, `settled`, or +`failed`) and whether the wait has passed. From these it computes whether the +load may settle and which rows are visible at settlement. It shares no code +with the wrapper. + +**History grammar.** Session 1 produces coverage through real loads, not seeded +metadata. Seeded metadata would make the model's coverage and the production +coverage the same assumption. Session 2 steps then arrive in any legal order: + +- the local restore settles or fails; +- the upstream load settles or fails; +- the wait passes; +- a reset or rotation happens; +- the demand is released; +- a second demand starts. + +**Production driver.** The real persisted wrapper over +`SQLiteCorePersistenceAdapter` and `node:sqlite`, with a generic on-demand +source. Receiving witnesses cover Electric `progressive` (full-sync resume and +subset-only) and Query DB. + +**Observations.** After each step: load settlement, query rows, `isReady()`, +whether `preload()` has settled, whether the network-first wait has released, +and, for an ordered query, the published window. A joined history pairs an +eager source with an on-demand source. + +The glossary's persisted readiness and network-first initial render entries +follow this combining rule (edited in this worktree). + +**Wrong designs the oracle must reject.** Each needs a demonstrated failure: + +1. Removing the on-demand guard and using startup readiness. This settles + before the subset restore, with no rows. +2. Treating any completed local restore as evidence. This fails "offline, + empty, uncovered" and "offline, partial, uncovered." +3. Treating nonempty rows as coverage. This fails "covered and genuinely + empty." +4. Writing coverage before the rows commit durably. This fails a crash between + the two writes. +5. Keeping coverage across reset or rotation. +6. Matching a different demand key, such as `limit 10` coverage for `limit 20`. +7. Aborting the upstream load when the wrapper settles from SQLite. Fresh rows + then never arrive. +8. Leaving a covered next page unpublished after the wait. +9. An on-demand opt-in that permits fallback on its own. The gate then releases + a join before the on-demand side's covered rows are visible. +10. Coverage keyed by filter only, or ignoring order while honoring limit. +11. A wait measured from the sync run instead of the load, or coverage cleared + on release. +12. A late upstream failure that undoes a settlement from SQLite. + +## Documents that change with the law + +- Persisted-readiness oracle: prose, model, grammar, driver, and checks. +- `packages/db/src/query/live/ARCHITECTURE.md`: source settlement and the + initial-readiness limits (edited in this worktree). +- `docs/contributing/glossary.md`: new persisted subset restore and persisted + demand coverage entries; revised persisted readiness and network-first + initial render entries (edited in this worktree). +- `docs/guides/sqlite-persistence.md` ("Render after SQLite restore") and + `docs/guides/live-queries.md`. They must say that a covered result, including + an empty one, is complete as of the load that covered it, not fresh, and that + the wait changes when a query is ready, not how fresh its rows are. Rows from + the network can still arrive after a query is ready. +- `docs/contributing/oracle-coverage.md`, "Persisted readiness and network-first + initial rendering." +- `docs/contributing/oracle-reviews/issue-2056-cache-eviction-design.md`, per + issue #2102's completion criteria. diff --git a/docs/contributing/oracle-reviews/2026-10-20-stale-reacquired-ownership.md b/docs/contributing/oracle-reviews/2026-10-20-stale-reacquired-ownership.md new file mode 100644 index 0000000000..3a5f328b6d --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-20-stale-reacquired-ownership.md @@ -0,0 +1,175 @@ +# Stale reacquired Query ownership oracle review + +Reviewed source: branch `investigate-finite-stale-reacquisition`, based on +`origin/main` at `31dad2dd3`. The first runs used `acaaf4c0a`. The counts, +RED/GREEN table, and mutant table were last rerun on the commit that follows +`67987ce10`, which extends the reacquired lease table and records the open +cells; that tree was measured before it was committed. The independent +reviews read the `acaaf4c0a` tree. + +## Contract and defect + +PR #2198 (`db0ac815e`) made a released Query keep an empty `queryToRows` entry +until its release publishes, so metadata that has not published the release +cannot restore it as an owner. The result handler's already-applied guard and +the on-demand direct-write skip also read entry presence as "the active +observer lease has applied a result". When a stale Query was reacquired while +its release was held, the refetch-start notification repeated the cached +result, the guard read the release's entry as applied, and the reacquired +lease owned nothing. The release then removed the row, and a failed refetch +never restored it. + +Laws checked, each stated beside its model in +`packages/query-db-collection/tests/ownership-lifecycle.oracle.test.ts`: + +1. A reacquired observer lease owns the Query's latest successful data from + reacquisition until its own release. A successful refetch replaces that + result; a failed one keeps it. `staleTime` decides only whether a refetch + starts. +2. Published owners never name a released lease, including when another lease + claims the row while the release is held. +3. An accepted on-demand direct write patches the cache of an active Query + whose lease has not applied a result. +4. An accepted direct write survives a refetch start of an empty current + result. +5. A release whose durable commit fails is terminal for its sync run, and a + restart begins from clean ownership. + +Authority: the file's opening ownership and direct-write laws, the existing +reacquisition history, TanStack Query's stale-mount and failed-refetch rules, +the DbClient hydration history, the glossary's cleanup definition, and the +AGENTS.md fail-fast rules. + +## Repair + +`hasAppliedOwnership` reports an entry only when it is not a release's entry. +`removeQueryOwnership` marks its empty entry in a `WeakSet`. The guard and the +on-demand direct-write skip use the helper. The result-application rollback +snapshot keeps the entry by reference (CR-1 below). The production diff is 15 +lines added and 6 removed in `src/query.ts`. + +## RED and GREEN + +| Witness | `main` `31dad2dd3` | Repair | +| --- | --- | --- | +| Reacquired lease table (42 cells: 7 refetch histories × 0–2 peers × 1–2 rows) | 20 fail. Example: `published: []` and `settled: []` where the model gives `["shared"]`. With peers, `shared` is lost only at `peerReleased`, while a two-row result loses `solo` at publication | 42 pass | +| Published owners table (8 cells) | 8 pass | 8 pass | +| Reacquired cache patch behind `deferDataRefresh` | Fails: cache `["Shared"]`, model `["Manual"]` | Passes | +| Empty-result direct insert (eager, on-demand) | 2 pass | 2 pass | +| Failed release commit (3 cells) | 3 pass | 3 pass | +| Reacquired commit fails after a held release | Passes | Passes | + +The oracle file passes 585 of 585 tests with the repair and 564 on `main`. The +Query Collection package passes 1460 tests with 2 skipped, and the type check reports no errors. +Three repeated runs of the new tables gave identical results. + +## Mutant results + +Each mutant was applied to the repaired source, run against the whole oracle +file, and reverted. Counts are for the final oracle file. + +| Mutant | Outcome | Killing witness | +| --- | --- | --- | +| Guard reads entry presence (the defect) | Assertion failure, 20 tests | Reacquired lease table | +| Release entry not marked | Assertion failure, 21 tests | Reacquired lease table, cache patch | +| Direct-write skip reads entry presence | Assertion failure, 1 test | Cache patch (Query cache only; public rows agree) | +| Applied means non-empty entry | Assertion failure, 2 tests | Empty-result direct insert: on-demand loses the insert at its receipt; eager keeps Manual after Newer | +| Delete entry at release | Assertion failure, 4 tests | Published owners `while-held`: owner count 2 instead of 1; without a peer the hydrated row leaks | +| Delete the released entry when the Query is reacquired | Assertion failure, 2 tests | Published owners `while-held, reacquired`: the reacquired lease has no cached result and its fetch fails, so `b` copies `a` back from unpublished metadata | +| Replace the released entry with an unmarked empty Set at reacquisition | Assertion failure, 21 tests | Reacquired lease table, cache patch | +| Mark the reacquired lease's own application entry as released | Assertion failure, 5 tests | Persisted direct-write refetch-start histories and the direct cache patch bound | +| Reuse the released Set object for the next application | Assertion failure, 25 tests | Reacquisition history and reacquired lease table: the reused entry keeps its mark | +| Guard removed (`false &&`) | Assertion failure, 4 tests | Persisted direct-write refetch-start histories | +| Hostile storage that swallows a failed commit | Assertion failure, 2 cells and 1 test | Both failed-commit witnesses | +| Cleanup skips entries without an observer | Survives the oracle file; assertion failure, 1 test, in the package suite | `tests/query.test.ts` "should clear retained ownership during explicit collection cleanup": a retained Query's retention marker survives cleanup | + +The last four rows came from a later independent review. It reported two +survivors without a distinguishing history; the concrete versions above were +written from its descriptions. Deleting the released entry at reacquisition +survived every test until the `reacquired` published-owner histories were +added; it fails them and passes nothing else differently. + +## CodeRabbit review of `8ff1db49f` + +Review 5600814169 had one inline finding and a summary. Ledger: + +| ID | Claim | Verdict | Action | +| --- | --- | --- | --- | +| CR-1 | The rollback snapshot in result application clones the entry, so a failed application that restores it drops a release's mark | True at the code level. No public consequence reached: in persisted histories the release retires its entry before the reacquired application snapshots (instrumented with the release commit gated and ungated), and in non-persisted histories the held commit rejects only by cancellation, which the restore guards skip | Fixed: the snapshot keeps the entry by reference, because application replaces the entry rather than mutating it. No witness distinguishes this; reverting it passes all 557 tests | +| CR-2 | Add a history with reacquisition during a held release, a commit failure before publication, and a failed refetch; the row must remain until the reacquired lease is released | The history runs; its expected result conflicts with the fail-loud law. The release is durable and the reacquired result is not, so the row leaves and the Collection enters `error` | Added "fails loudly when a reacquired lease's commit fails after a held release"; a storage adapter that swallows the failure fails it | +| CR-3 | Docstring coverage 0% for touched functions | True; the touched closure helpers use line comments like their neighbours | No change, per AGENTS.md's rule to match surrounding comment density | +| CR-4 | CodeRabbit's ESLint tool timed out | A tool note, not a code claim; the repository Lint check passed on `8ff1db49f` | No change | +| CR-5 | Title, description, and issue checks passed | Informational | No change | + +## ORC outcomes + +- ORC-001: Each table states its law, authority, and limits before mechanics. +- ORC-002: The models keep lease results and owner sets. They do not import + `queryToRows`, the guard, or sync transactions. +- ORC-003: Prose beside each model, grammar, driver, and comparison. The + prose was updated with each grammar change. +- ORC-004: Fixed enumerations, not generated properties. Each axis has a + stated contribution: `staleTime` starts the refetch; outcome and placement + separate keeping, replacing, and losing the row; `peer` hides a lost result + until `peerReleased`; `claims` reaches the unpublished-metadata premise. + Excluded: eager reacquisition, whose premise is unreachable (probed: + cleanup removes the Query from the cache, so a restart fetches); persisted + retention, which keeps released rows by design. +- ORC-005: Public `loadSubset`, `unloadSubset`, direct writes, `DbClient` + dehydrate and hydrate, `cleanup`, and `preload`. Observations are public rows, + stored rows, Collection status, rejection messages, dehydrated owner counts, + and `QueryClient.getQueriesData`. No missing-information hypothesis was + identified. +- ORC-006: The mutant table above. Every kill is an assertion failure at the + named comparison. +- ORC-007: Not applicable; no generated property. +- ORC-008: The reacquisition model keeps one result per active lease; the peer + axis needs the union, shown by the peer cells that differ only at + `peerReleased`. The owner model keeps a set; the owner count distinguishes + `peer` histories that public rows do not. +- ORC-009: Glossary terms observer lease, acquisition release, publication, + accepted sync transaction, and cleanup. `a`, `b`, `c`, and blocker are + declared local names. +- ORC-010: Each history registers cleanup that releases its holds and Query + clients; comparisons are single structured assertions. +- ORC-011: Not applied. The named shared-fault risk (an entry read as applied) + is separated by the empty-result and cache-patch witnesses, which observe + different boundaries; no further formulation was identified. +- ORC-012: This record. +- ORC-013: The reusable law "an empty entry is applied unless it belongs to a + release" has a premise witness (reacquired lease table) and a nearby + boundary witness (empty-result direct insert) that rejects the non-empty + rule. +- ORC-014: The controlled storage adapter supplies the commit failure. A + receiving probe on Node SQLite aborted the release's delete with a SQLite + trigger. The Collection entered `error` and storage kept the row, but the + public Collection dropped it, while a peer's row stayed. The controlled + adapter keeps the row public, whether it rejects at once or after a delay. + The real SQLite adapter drops it on every sync-present configuration tried: + managed, resolved for `sync-present`, and adapter-only without a + coordinator. The controlled premise does not hold on the real host, so the + cross-boundary claim is contradicted, not established. The divergence + belongs to the SQLite persistence owner; see Open cells. + +## Bug-class boundary + +Contract: Query ownership after release and reacquisition. Histories: an +on-demand lease with a one- or two-row result and a held release, a stale or +fresh reacquisition, zero to two peers, an optional refresh barrier, a direct write, and a failed release commit. +Path: Query Collection over a real QueryClient and a controlled persistence +adapter. Observations: public rows, stored rows, owners, status, and Query +cache at the named checkpoints. + +Open cells: + +- CR-1, unreached. Core rejects a committed, held sync transaction only in + `state.cleanup()`, which runs after sync cleanup has removed the observers, + so the restore guard returns. The remaining route is a duplicate-key replay + inside the reacquired application's own open transaction, which no + history here constructs. +- SQLite durability failure. On the Node SQLite adapter, a sync + transaction (here, a release) whose durable step fails still removes the row from the public + Collection, while storage keeps it. This contradicts the glossary's + accepted-sync-transaction rule, which accepts a wrapping sync's + transaction only after its durable step. It belongs to the SQLite + persistence owner and is outside this Query DB change; tracked in [#2210](https://github.com/TanStack/db/issues/2210). diff --git a/docs/contributing/oracle-reviews/2026-10-21-offline-on-demand-explainer.html b/docs/contributing/oracle-reviews/2026-10-21-offline-on-demand-explainer.html new file mode 100644 index 0000000000..fafe962260 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-21-offline-on-demand-explainer.html @@ -0,0 +1,658 @@ + + + + + +Offline On-Demand Readiness + + + + + + + +
+ +
+
TanStack DB · persistence design notes
+

Offline On-Demand Readiness

+

Why a persisted on-demand query can't render from SQLite while offline today, what we measured, and the one-rule model we're exploring to fix it.

+
+ Worktree: .worktrees/progressive-network-first + Base: main @ 31dad2dd3 + Status: proposal, nothing committed +
+
+ +
+

Where you left off

+
+
The report

A Discord user turned on persistence. initialRender: network-first worked for eager collections but not for progressive Electric ones. They expected progressive to render from SQLite without waiting for the network.

+
Why it fails

Electric reports progressive to core as on-demand, and the persisted wrapper rejects network-first for any on-demand source. Not Electric-specific.

+
What we measured

Cached rows already show while offline. Only the ready signal waits. A completed progressive sync already works offline; anything short of that waits forever.

+
Where we landed

One rule: the wrapper may settle a load from SQLite when that exact query finished loading before. Everything downstream follows from existing laws.

+
+
+ +
+
+

How readiness works today

+

A live query is ready when every source gives it evidence that its rows are present. Since #2115, that evidence depends on the source's sync mode.

+
+
+
+ EAGER SOURCE +

Collection readiness is the evidence

+

It loads everything without per-query requests, so the query waits for the Collection to be ready. With persistence, a finished SQLite restore of the whole Collection is a separate signal. Network-first (#1955) lets Suspense render that restore after a deadline.

+
+
+ ON-DEMAND SOURCE +

A settled load is the evidence

+

Each query asks for its subset. The persisted wrapper reads that subset from SQLite, then waits for the upstream load. The query is ready when both settle. There is no per-query "restored" signal, so network-first is rejected.

+
+
+

Progressive Electric is on-demand to core. Its Collection stays loading until the full sync, but its queries go ready as their own loads settle.

+
+ +
+
+

Try it

+

Pick a source, what happened last session, and what the app asks for now. Then compare today's behavior, a naive fix, and the proposed rule. Times are illustrative: SQLite answers at 120 ms, a live network at 900 ms.

+
+ +
+
+
+ Source +
+ + + +
+
+
+ Last session +
+ + + +
+
+
+ The app now asks for +
+ + + +
+
+
+ Network +
+ + +
+
+
+ Model +
+ + + +
+
+
+ +
+ + 0 ms +
+ How long to wait for the network before trusting SQLite. +
+
+ +
+
+
SQLite
+
Network
+
Rows on screen
+
Query ready
+
+
+
+ +
+
Rows visible–
+
isReady / Suspense–
+
Is the answer correct?–
+
+

+
+
+ +
+
+

What we measured

+

Two witness tests record current behavior on main. They pin observations, not a contract. Each checkpoint is 50 ms after the query starts.

+
+

Generic on-demand source

+
+ + + + + + + + +
UpstreamLocal subsetRows visibleQuery ready
Never answersCompletea1, a2no
Never answersPartiala1no
Never answersEmptynoneno
Settles at once, writes nothingCompletea1, a2yes
Settles at once, writes nothingEmptynoneyes
+

Progressive Electric, two sessions on real SQLite, second session offline

+
+ + + + + + +
Session 1Session 2 resumes streamRows visibleQuery ready
Completed full syncyes1, 2yes
Loaded the query subset onlyno1, 2no, forever
Nothingnononeno
+

The wrapper can't tell a complete subset from a partial or never-loaded one; they all just read back as rows. That's why "show whatever SQLite has" is wrong.

+
+ +
+

The model we're exploring

+
+ The one rule +
The persisted wrapper may settle a query's load from SQLite once the local rows are visible, if that exact query is covered in the current cache: a previous load of it finished, or the source holds a complete baseline. The network load keeps running and replaces the rows when it answers.
+
+
Exact query keyMatches by getLoadSubsetDemandKey. No "this query is broader than that one."exists today
+
Covered setPer cache generation: keys whose load finished, including empty results, plus "complete baseline" which matches everything.new, in the wrapper
+
Settle ruleSettle when local rows are visible and the query is covered and the wait has passed or its load failed.new
+
Network continuesUpstream keeps loading; its rows publish normally when they arrive.exists today
+
+

Why it's small: initial readiness, Suspense, queryOnce, skipInitial effects, and ordered-window publication (pagination) all already wait on "this query's load settled." Settle a covered load and every one of them works offline with no live-query changes. ARCHITECTURE.md already says a source that resumes from complete local rows "settles its loads at once." The rule moves that idea from the whole Collection down to one query.

+

The network-first wait turns into an optional knob. No correctness property needs it. At 0 it matches how a completed progressive resume behaves today.

+
+
+ +
+
+

What the rule settles

+

The first draft had nine open decisions. Most went away once the design reduced to the single rule.

+
+
+ + + + + + + + + + + + +
Earlier open decisionUnder the one rule
Where coverage livesThe wrapper, because it has to work the same for every source
loading vs unavailable statusGoes away; on-demand needs no new persisted status
Progressive subsets before the full syncCovered if their load settled (inferred, needs a witness)
Deadline meaning per queryOne wait, measured from each load's start; that load's failure ends it
"Not available offline" statusExposed to the in-progress precise-errors PR
Page keys built from other rowsA known limit: if restored page 1 differs, page 2 is a miss and waits for the network
Publishing then replacing rowsAlready normal for ready queries
API shapeOne wrapper option: on/off plus wait
Oracle ownerThe wrapper's settle rule; existing live-query oracles own the consequences
+

Decisions since the first pass

+
+
1

Coverage lifetime. Valid until revalidated or until the cache generation ends. A persistedGcTime-style expiry gets its own issue and waits until someone asks for it.

+
2

The wait. Reuses the network-first option and its 3 s default from #1955; 0 is valid.

+
3

Mixed joins. On-demand sources register no persisted readiness, so a query that joins an eager source keeps the ordinary readiness path and waits for the eager network offline. That's safe, so it's recorded as a limit.

+
4

Law and ARCHITECTURE.md. Both are updated in the worktree, along with two glossary terms.

+
+

Not done yet: no formal model check, no independent range case, and Electric's own pending load after the wrapper settles is unexamined.

+
+ +
+
+

How we got here

+

Earlier work that shaped this problem. Filled dots are the ones that matter most.

+
+
+
2025-11-05 · #618Electric maps progressive to on-demandCore only ever sees eager or on-demand.
+
2026-03-20 · #1380 (samwillis)Persisted sync metadataQuery DB stores per-row query owners and persistedGcTime retention, "for offline-first flows."
+
2026-09-10 · #1797Subset algebra removedDeleted isPredicateSubset and friends. Dedupe matches exact keys "without inferring broader coverage."
+
2026-09-11 · #1785 / 2026-09-24 · #1846Complete resume baselinesA progressive resume restores and certifies the full baseline.
+
2026-09-30 · #1955Network-first initial renderEager only by design: "An on-demand Collection has no complete startup snapshot."
+
2026-10-11 · #2069Managed cache generations"A partial Electric cache does not certify an offline baseline."
+
2026-10-10 · issue #2102 (open)Design safe offline subset reuseItem 1 asks for durable evidence of what a subset covers. This proposal belongs there.
+
2026-10-13 · #2115On-demand queries ready on their own loadThe per-query readiness rule this design builds on.
+
+
+ +
+

Files in the worktree

+
+ + + + + + + +
FileWhat it is
docs/contributing/oracle-reviews/2026-10-20-on-demand-network-first-law.mdThe law draft, rewritten around the one rule
packages/db-sqlite-persistence-core/tests/on-demand-offline-readiness-witness.test.tsGeneric on-demand witness, 5 cases
packages/electric-db-collection/tests/electric-progressive-offline-readiness-witness.test.tsElectric two-session witness on node:sqlite, 4 cases
docs/contributing/oracle-reviews/2026-10-21-offline-on-demand-explainer.htmlThis page
+
+ +
+ + + + diff --git a/docs/contributing/oracle-reviews/2026-10-21-persisted-demand-coverage-review.md b/docs/contributing/oracle-reviews/2026-10-21-persisted-demand-coverage-review.md new file mode 100644 index 0000000000..897dcbd415 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-21-persisted-demand-coverage-review.md @@ -0,0 +1,676 @@ +# Persisted demand coverage: implementation review + +Reviewed head: `cd2aceaec858e0d090fa330d67a9d511c51ba5da`. +Implementation commits: `4fecdc75835ea7b706772b88e9c02caaa60f5caf` (production +change), `42e15a33fc0a7cff5e569bb00f60d3758035b5a2` (truncate history). +RED head: `85372868c05ed933a2234fcf7c5365aa281e48d9` (oracle before the +production change). Merge base with `main`: +`9aecbab4cacb3f1123b1716e6f063abb5affc74f`. + +The law, the oracle design, and the wrong designs are in +[the law record](2026-10-20-on-demand-network-first-law.md). This record +contains the evidence obtained against the implementation. + +## Law and result + +An opted-in persisted wrapper over an on-demand source may settle a demand's +load from SQLite when the persisted subset restore of that demand is visible, +the exact demand key has persisted demand coverage in the current cache +generation, and the wait since the load started has passed or the upstream +load has failed. The upstream load keeps running. Coverage survives release +and ends at a source truncate. For network-first initial rendering, an eager +source permits local rendering after its restore and deadline; an on-demand +source permits it when the query's demands on it have settled. + +Authority: the maintainer decisions recorded in the law record (exact keys, the +same wait option and default as eager, joined sources combine, a late failure +leaves the query ready, coverage written through the source-transaction +pipeline). `packages/db/src/query/live/ARCHITECTURE.md` (the persisted-wrapper +paragraph, the initial-readiness limit, and law 7) states the same contract. + +Production path: `persistedCollectionOptions` over a generic on-demand source +and `SQLiteCorePersistenceAdapter` on `node:sqlite`. The wrapper's +`PersistedCollectionRuntime.loadSubset` and `loadHydratedSubset` settle a +covered demand. The wrapped `loadSubset` records coverage after the source +load in a metadata-only source transaction. The wrapped `truncate` deletes +coverage keys in its own transaction. In core, `CollectionConfigBuilder` +exposes `initialDemandsSettled()` and notifies subscribers from +`updateLiveQueryStatus`, and `LiveQueryObserver.preloadForInitialRender` +combines the sources. + +## RED and GREEN + +RED, at `85372868c` with the 96-cell grammar: every production case failed at +setup with `InvalidPersistedCollectionConfigError`. A probe without the opt-in +(recipe in the law record) passed all 72 uncovered cells. It failed 22 of 24 +covered cells at the first checkpoint where the law applies; the two +early-network-answer cells pass under both designs. The lifecycle case and the +covered joined case failed, and the uncovered joined case passed. + +GREEN, at the reviewed head: the coverage oracle passes all 139 tests (108 +cells, the lifecycle history, two joined histories, and calibration). Also at +this head, `packages/db` passes 13,422 tests, `db-sqlite-persistence-core` +passes 1,327, and `electric-db-collection` passes 719. The type errors at +`persisted-oracle.test.ts:4730` and `:4871` also occur with this change's +sources reverted, so this change does not cause them. + +Two driver repairs reached production paths for the first time. Neither +changed an expected result: + +- Session 1 attached its rejection handler to `preload()` only after the + answer loop, so the `failed` history produced unhandled rejections. +- The joined comparison included virtual row properties (`$key`, `$synced`, + ...), which the law does not govern. + +## Production mutants (ORC-006) + +Each mutant edited one production line. The script lives outside the +repository. Every kill is an assertion failure at a refinement comparison. No +mutant failed at setup, at a `checkpoint not reached` witness, or by timeout. + +| Mutant | Outcome | +| --- | --- | +| Coverage ignored (always wait for upstream) | Killed: 24 cells, first `settled-empty, wait 0, pending` at `restored` | +| Wait ignored (settle at restore) | Killed: 13 cells, `wait 1000` covered cells at `restored` | +| Half deadline | Killed: 9 cells at `beforeDeadline` | +| Failure errors a covered demand | Killed: `settled` and `settled-empty`, `wait 1000, fails-early` | +| Coverage recorded before the upstream load settles | Killed: 33 cells, first the `failed` history | +| Deadline from the sync run, not the load | Killed: lifecycle history at checkpoint 1000 | +| Truncate keeps coverage | Killed after `42e15a33f`: 11 `truncated` cells; it survived at `4fecdc758`, before that history existed | +| On-demand opt-in permits fallback alone | Killed: joined `never requested` | +| Gate ignores the eager deadline | Survives this oracle (Part 3 uses a 0 ms eager wait); killed by `persisted-readiness-oracle.test.ts` ("does not hide a query failure behind an available persisted source") | + +The "coverage recorded before the upstream load settles" mutant is coarser +than the wrong design it stands for: it records coverage for a load that later +fails, not only for one whose rows fail to persist. The model-level +`durable-write-failed` calibration and the rejecting adapter keep the narrower +design distinguished. No production mutant that writes coverage through a +separate durable path, outside the source-transaction pipeline, was run. + +## Requirement outcomes + +- **ORC-001:** The opening prose names the law, its sources, and its limits. + Truncate moved from the limits into the grammar. +- **ORC-002:** The model reads only the history label. It never reads SQLite, + coverage keys, or the wrapper. The positive witness compares session-1 and + session-2 keys through `getLoadSubsetDemandKey`. That is a trusted base for + the witness only; it does not compute any expected result. +- **ORC-003:** The contract, grammar, model, driver, and refinement check are + separate, and each has prose beside it. The `truncated` history has its own + prose and ablation note. +- **ORC-004:** Not triggered; the grammar is a bounded enumeration, not + generated. The file still records ablation for each history, wait, and + schedule value, and the excluded pre-restore event. +- **ORC-005:** A positive witness checks session-1 demand keys, and a + checkpoint witness checks the restored rows. For `truncated`, another witness + checks that the truncate replayed D and that the load stays pending. No + missing-information hypothesis was identified; this is not a claim of an + exhaustive search. +- **ORC-006:** Sixteen model-level wrong designs (thirteen in Part 1, two + lifecycle, one joined), each distinguished at a named case, and the + production mutants above. +- **ORC-007:** Not triggered; there is no generated property. +- **ORC-008:** The model keeps four facts: covered, visible rows, settled, and + failed. Each one changes a later observation, as the model prose explains. The + `truncated` history adds no state; it maps to "not covered" with no rows. +- **ORC-009:** The glossary terms are persisted subset restore, persisted demand + coverage, and load settlement, and the model prose maps them. +- **ORC-010:** `withCleanup` keeps a primary failure and cleanup failures apart + in one `AggregateError`, and a calibration test checks this. +- **ORC-011:** No shared-fault hypothesis was named. Demand-key identity is the + candidate: the oracle trusts `getLoadSubsetDemandKey` to tell the session-1 + demands apart. A second formulation that derives keys independently was not + added. +- **ORC-013:** The wait threshold has checkpoints at `wait - 1` and `wait`, and + the lifecycle history has checkpoints ±1 ms around each load's own deadline. + The half-deadline and sync-run-deadline designs are rejected there. +- **ORC-014:** The controlled premise is a source whose load settles after its + applied receipt. Two receiving witnesses supply it with a real source. In + the Electric progressive witness, subset-only coverage settles offline and a + never-loaded subset waits. In the Query DB witness + (`persisted-on-demand-offline-witness.test.ts`, added after the reviewed + head), a covered query settles at the wait or at once on a rejected + `queryFn`, and a never-loaded query waits. The coverage-ignored mutant fails + both covered Query DB histories. + +## Open in-scope cells + +The [coverage map](../oracle-coverage.md#persisted-readiness-and-network-first-initial-rendering) +owns these. Each needs a witness: + +- ordered windows and cursor pages in session 2, including a key that ignores + offset or cursor; +- must-refetch, schema reset, and cache-claim rotation in the coverage oracle; + Electric must-refetch goes through `truncate`, but no Electric history + exercises it; +- a failure that arrives before the persisted subset restore publishes; +- optimistic writes on a covered demand; +- nested live queries beyond one outer query over the joined query. After + the reviewed head, Part 3 adds that nested shape and reads rows at release; + a gate that reads the root query's demands fails only the nested cells. +- Suspense over a real persisted on-demand Collection. After the reviewed + head, React and Solid tests receive the joined gate with capability fixtures, + and the gate mutant that lets the on-demand opt-in permit fallback alone + fails both. + +This record claims a new law, not the closure of a bug class. + +## 2026-10-26: code-review findings after merging `main` + +Merge head: `5c0e04dce` (merges `main` at `5649a07d4`; the network-first wait +now lives in `waitForPersistedInitialRender`, and the on-demand gate moved +into it). Oracle extension: `e4add5b18`. Repair: the next commit. + +A code review reported ten findings. Four were defects under the accepted +contract, one needed a product decision, and the rest were refuted or a cost +only. The oracle was extended first and run against unchanged production: + +- three session-1 histories put a reset after a load's rows and before it + settles: a source truncate, a completed scoped recovery that rotates the + cache generation, and a scoped recovery still pending; +- Part 3 adds a shape that reaches the on-demand source through two live + queries, with a history that covers only the inner query's demand. + +RED on `5c0e04dce`: 35 failed and 137 passed of 172, all by assertion at a +named checkpoint. In 22 cells an obsolete load recorded coverage. In 12 cells +a coverage record during pending recovery failed the session-1 load. The +two-reader inner-only history released the wait. + +Repair: a load records coverage only if no lifecycle change, reset (rotation +or scoped recovery), or source truncate happened since it was forwarded +upstream. The observer gives an on-demand source one gate entry for each live +query that reads it. Because scoped recovery marks a reset before it can abort +the coverage commit, the epoch check also closes the failed-record case; a +`.catch` on the commit had no reachable history and was removed. + +GREEN: 172 of 172. Also `db` 13,462, `db-sqlite-persistence-core` 1,535, +`react-db` 601, and the Electric and Query DB witnesses pass. Solid did not +run: its test environment needs a package that the registry proxy blocks. + +Mutants, all killed by assertion with no timeout or unreached checkpoint: + +| Mutant | Outcome | +| --- | --- | +| Restore the original: any settled load records coverage | 34 cells fail across all three in-flight histories | +| Narrow fix: check only for a source truncate | 23 cells fail in the two scoped-recovery histories | +| Restore the original gate: the first live query reached on a source stands for all | The two-reader inner-only history fails | + +By maintainer decision (2026-10-26), multi-tab coordinators are outside the +law: under a coordinator other than `SingleProcessCoordinator`, demand routes +through the coordinator, so a covered demand neither settles from SQLite nor +records coverage. A probe showed a covered demand staying loading offline even +in the leader tab. The coverage map, law record, architecture paragraph, and +guide state the limit. + +Refuted: scoped recovery and reload truncates keep coverage (they refresh +memory only; rotation starts a fresh storage target and schema reset deletes +collection metadata); a raw on-demand root releases early (granular mode waits +for its load; wholesale mode requests no demand online or offline). Cost only, +no law: a source's literal `true` becomes a promise, and each load writes one +metadata record. Accepted: coverage keys grow until a truncate or expiry +(#2211). + +## 2026-10-26: CodeRabbit and xhigh reviews of PR #2263 + +Reviewed head: `3de5a922f` (CI green). CodeRabbit review `5655576117` gave five +findings (CR1-CR5) and an xhigh code review gave fifteen (X1-X15). The oracle +was extended first and run against the parent's production code. Maintainer +decisions: Query DB retention expiry ends coverage, through a row-count check +that also covers deletes in a limited window; the certified-baseline clause +is dropped; multi-tab coordinators stay out of scope. + +Oracle and witness changes: + +- Part 1 adds `settled-then-row-deleted`, and its wrong design "coverage + ignores rows deleted after it settled". +- Part 2b adds two same-session histories: a load of D after a cache rotation, + and a `refetch: true` load of D. Both must wait for the network. +- Part 3 adds a source failure on a never-covered demand, which must not + release the wait. +- The Query DB witness adds a retention-expiry history. +- The Electric witness driver now serializes work, as the driver contract + requires. + +RED on the parent production code (`3de5a922f`): the oracle failed 14 of 188 +(11 row-deletion cells, and the refetch, rotation, and source-failure +histories), and the Query DB witness failed its retention history. All +failures were assertions at a named checkpoint. + +Repair: a coverage record stores the cache generation's storage ID and the +number of rows the demand reads from SQLite. It counts only for the current +generation and while the restore returns at least that many rows. A load +rewrites the record only when that count changes. A `refetch` load never +settles from coverage. An aborted load clears its wait timer. The gate checks +on-demand settlement on the network-failure path too, and the observer keeps +one entry for each source and owning live query. + +GREEN: the oracle passes 188 of 188 and the Query DB witness 4 of 4. Also: +`db` 13,462, persistence 1,415, `react-db` 601, `solid-db` 99, +`electric-db-collection` 719, `query-db-collection` 929. Size against `main`: +`app` +78 B gzip, `db-sqlite-persistence-core` +475 B gzip. + +Mutants, all killed by assertion: + +| Mutant | Outcome | +| --- | --- | +| Drop the row count | 11 row-deletion cells and the Query DB retention history fail | +| Require more rows than recorded | 28 covered cases fail | +| Ignore the cache generation | The rotation history fails | +| Let a refetch settle on coverage | The refetch history fails | +| Let a network failure bypass on-demand settlement | The source-failure history fails | + +Counters, not oracle cases: five loads of one covered demand now cause one +coverage commit, not five. Releasing a covered load leaves no settlement timer +behind, while the unfixed code keeps one. + +| ID | Verdict | Action | +| --- | --- | --- | +| CR1, X11 | Confirmed: the glossary entry claimed demand settlement | Fixed: the entry now names the source status only, and the network-first entry carries the demand rule | +| CR2 | Confirmed | Fixed: the law record states its implementation and the query-level signal | +| CR3 | Confirmed | Fixed in the guide | +| CR4, X4 | Confirmed (RED) | Fixed | +| CR5 | Confirmed by reading the driver | Fixed | +| X1 | Confirmed (RED) | Fixed by the row count | +| X2 | Confirmed (RED) | Fixed by the generation check | +| X3 | Confirmed (RED) | Fixed | +| X5 | Refuted: a truncate clears hydrated demand, so the later load must hydrate, and the wrapper rejects a source transaction that crosses a hydration cycle | Recorded as an excluded, illegal history in the oracle | +| X6 | Confirmed by counter | Fixed for writes; each load still reads its row count, and a synchronous `true` result settles asynchronously | +| X7 | Not reproduced; read from the code | Open in the coverage map | +| X8 | Confirmed by reading; deletes are now reached through `settled-then-row-deleted` | Fixed by the row count for deletes; an order change without a delete stays open | +| X9 | Confirmed by counter | Fixed | +| X10 | Confirmed by reading | Fixed: one entry for each source and owner | +| X12 | Accepted as the design direction | The generation and the row count replace per-path invalidation, except the truncate deletion that empty demands still need | +| X13 | Confirmed | Fixed: the observer reuses the exported type | +| X14 | Confirmed | Partly fixed: the eager deadline is read once per source; the demand key is still computed more than once per load (cost only) | +| X15 | Confirmed | The clause is dropped from the law record and the glossary | + +## 2026-10-26: second CodeRabbit review + +Reviewed head: `f62f9dc19`. CodeRabbit review `5656501808` gave two findings. + +| ID | Verdict | Evidence | Action | +| --- | --- | --- | --- | +| CR6 | Confirmed | A real-timer probe with a driver that answers reads on a later macrotask: a load that rejects as soon as it is forwarded raised one `unhandledRejection` during the coverage read, and none after the fix | Fixed: `settleCoveredDemand` observes the upstream rejection before the read | +| CR7 | Confirmed (RED) | The new Part 2b history "keeps a synchronous answer for an already hydrated demand synchronous" failed by assertion: the second request reported a promise | Fixed: a literal `true` returns unchanged, and its coverage record follows without holding the load | + +The oracle adds the session-2 schedule `fails-at-once`. Its driver answers +SQLite reads within the same microtask turn, so it reaches the failure path but +cannot hold the rejection across the coverage read. Under the fake clock, a +driver that delays reads with a timer stopped session 2 before its first +checkpoint, so CR6 keeps the probe as its witness. GREEN: the oracle passes +215 of 215, persistence 1,442, Electric 719, and Query DB 929. + +The CI failure on `f62f9dc19` in `ordered-work-oracle.property.test.ts` is +issue #2221. Its seed `1535602160` fails the same way on `main` at +`5649a07d4`, in the core ordered loader. The fixed-seed case of that run timed +out under CI load and passes locally in about one second. + +## 2026-10-26: closing open limits and the expired-claim finding + +Starting head: `5ba90763d` (CI green). The maintainer asked to close the open +limits and then evaluate CodeRabbit review `5657028264` (CR8: a coverage read +under an expired cache claim fails a load whose rows applied). + +Test-only commit `0949bc478` turned open limits into enforced cases, with no +production change. Each passed on the existing code except where noted: + +- the restore-before-forward invariant; +- macrotask reads in session 2, which put CR6 inside the oracle (removing the + early rejection handler raises six unhandled rejections); +- a schema reset over an empty covered demand (killed by a mutant that keeps + collection metadata); +- an optimistic write that rolls back on a demand settled from SQLite; +- ordered windows (Part 4): row deleted, moved out, moved in, swapped, and + page 2 with an index (killed by a coverage key that ignores cursor and + offset); +- Electric on-demand must-refetch, and Electric's own load after release; +- React and Solid Suspense over a real persisted on-demand Collection (each + killed by a mutant that ignores coverage). + +An on-demand Electric mock that ended a subset with `snapshot-end` instead of +`subset-end` let Electric settle its load before its rows committed. Coverage +then recorded zero rows, and the offline session went ready on `[]`. The SDK +ends injected subsets with `subset-end`, so the mock now does. The law record +states the dependency on source settlement. + +CR8, confirmed on two paths. RED on the parent production code: the session-1 +query went to `error` when the claim expired before the coverage record, and +D went to `error` when it expired before the coverage check. With coverage +recording disabled, the first history stays `ready`, so the coverage read +caused it. Repair: a coverage read that fails under an expired claim returns +no count and schedules claim recovery; other failures propagate. GREEN: the +oracle passes 227 of 227. + +X7 stays open: a probe that queued an empty load's coverage record during +another demand's hydration, truncated, and started scoped recovery left the +Collection `ready`, but it did not prove that the record was queued when the +truncate listed metadata. + + +## 2026-10-26: high code review after the shared test driver + +Starting head: `5d85ce247` (CI green). A high code review reported nine +findings, H1 to H9. The oracle was extended for every behavioral finding before +any production change, and each extension was run on the starting head. + +| Finding | Law and authority | Oracle change | RED on `5d85ce247` | Repair | +| --- | --- | --- | --- | --- | +| H1: a source error leaves the network-first wait pending forever when the query reaches an on-demand source | The glossary's network-first gate permits a render once every eager source passed its deadline or saw the network fail and every on-demand demand settled. A source failure does not settle a demand and does not block a settled one. | Part 3: P or C reports an error before C's 1000 ms wait ends, observed before the wait and after a late checkpoint. The observation adds rejection and the observer's synchronous gate. | Both covered histories: not released, not rejected; the never-requested histories pass | `initialDemandsSettled` no longer reads the error state, which still blocks `markReady`. `settleDemand` notifies settlement listeners, because a query in error runs no graph and so no status check. | +| H2: a failed coverage read fails the load | Prior behavior: a load settles as its upstream load did. The law record makes coverage bookkeeping. | Part 2c: the read after the load fails; the read during the coverage check fails. | Session-1 query `error`; D `error` before the network answers | Any failed coverage read returns no rows. An expired claim also starts claim recovery. | +| H3: a synchronous answer's coverage counts rows before they are durable | Law: coverage records the rows SQLite holds for the demand when the load settles, so a later deletion ends it. | Part 2c: a synchronous answer, with and without a later deletion of one row. | With the deletion, D settled offline | The coverage read waits on the apply mutex behind every committed source transaction. | +| H4 (= X7): the truncate's metadata list makes a pending coverage transaction fail-stop on abort | — | None: not reachable | — | A coverage transaction has no abort signal. Its only abort is `SyncTransactionAbortedError` after its lifecycle was replaced, and the fail-stop then marks a terminal failure on the replaced lifecycle generation, which `getCurrentTerminalFailure` ignores. X7 is closed. | +| H5: the row count accepts a window that lost one row and gained another | The approved window law (maintainer decision, 2026-10-26): a window settles only while its restore holds the same rows. | Part 4: `row-replaced` (`a1` replaced below the boundary) and `row-gained-under-limit` (an under-full window gains a row). Neither changes the tie-boundary key. | Both settled from SQLite | Coverage of a limited load records its sorted row keys, and the restore must return the same keys. | +| H6: a release during the coverage check leaves the wait's timer running | Law record: a released load owns no wait. | Part 2c: release while the coverage read is held. | The source Collection still loaded after release | `settleCoveredDemand` returns at once when the signal is already aborted. | +| H7: a second SQLite read per load | Cost, not behavior | None | — | Kept. The read now also waits on the apply mutex. Reusing the restore read for the check is a possible optimization. | +| H8: duplicated builder-or-Collection lookup | Maintainability | None | — | Declined: a shared helper does not reduce shipped bytes. | +| H9: settlement listeners fire only from the status check | — | None: not reachable | — | `retireDemand` runs only for an empty demand update inside a graph run, which ends with a status check. Setup sends only nonempty initial keys. `settleDemand` now notifies directly (H1). | + +The unordered part of H5 was a law question, not an enforcement gap: the +approved row-count rule accepts an unordered demand that lost one row and +gained another. The maintainer kept the row count for unordered demands +(2026-10-26) over recording every row key. A fixed history now pins that +choice: an unordered demand that loses `a1` and gains `a3` settles offline +on `a2` and `a3`. A mutant that records keys for every demand fails it. + +Hostile mutants, beyond the starting head: + +- a window check that accepts any superset of the recorded keys survives + `row-replaced` and is killed by `row-gained-under-limit`; +- dropping the error-state check without notifying from `settleDemand` is + killed by both covered source-failure histories. + +GREEN: the oracle passes 262 of 262 with the unordered history (vitest counts its type checks too). + +## 2026-10-26: strengthening the oracle beyond the review findings + +Starting head: `4293b0021`. The maintainer asked for seven strengthening steps. +None changed production; each was checked with mutants against the current +code. + +- **Window limits** (`34642864a`): Part 4 adds four histories with a tie at + the window boundary and runs both page-move histories without an index. + The model treats the window as a limited demand (same rows) plus an + unordered tie-boundary demand (row count). A mutant that ignores the + tie-boundary row count fails `tie-row-outside-deleted`; one that records + keys for every demand fails `tie-row-added`. +- **Generated campaign** (`b9a5e481d`): a fixed and a random campaign of 40 + histories each. The first model predicted when the wrapper reacquires a + demand after a truncate, and three counterexamples showed that this + scheduling differs from the guess (the wrapper reacquires a demand only + after its obsolete load settles, and then reacquires every active + demand). The law leaves that timing open, so the model now replays the + source's own event log and judges each settled load as current or + obsolete by whether a truncate came between its arrival and its + settlement. No counterexample indicted production. Mutants that ignore + the row count, let an obsolete load record coverage, or keep coverage + across a truncate each fail both campaigns. +- **Storage failures** (Part 2 of the campaign): every adapter call of a + covered history fails once, before or after its effect, in either session. + Safety held at every call. A mutant that lets a failed coverage read fail + the load fails the sweep. Losing the acknowledgement of the coverage write + fails session 1 terminally, while session 2 still settles on the durable + rows, which is consistent with the law. +- **Releases** (Part 3): the demand is released after each step of a load, + including while a coverage read is in flight. A mutant that keeps a + released load's wait fails the sweep. +- **Framework witnesses** (`eff968951`): Vue, Svelte, and Angular. These + adapters have no network-first render gate, so the witnesses check + readiness and rows. +- **Native drivers**: a better-sqlite3 witness in `node-db-sqlite-persistence` + and a Chromium OPFS witness in `browser-db-sqlite-persistence`, in the + OPFS Playwright matrix that CI runs. The OPFS witness also pins the + guide's statement that a multi-tab coordinator waits for the network. +- **Mutation sweep**: reported separately below. + +### Mutation sweep + +A scripted sweep applied 63 semantic mutants to the coverage code in +`persisted.ts`, the gate in `persisted-readiness.ts`, the builder's +settlement signal, and the observer's source collection. Each ran against the +fixed demand coverage oracle and the persisted-readiness oracle, and each +survivor also ran against the receiving witnesses. Rerun against the +generated campaign, 24 survivors left 23. + +The oracle then gained five histories, and each kills a survivor: + +- an empty covered demand after a rotation (the record ignores its cache + generation); +- a truncate during the coverage read of an empty load (no check after the + read); +- a synchronous answer whose durable write lands late (the coverage read + does not wait on the apply mutex); +- claim recovery after an expired claim during the check (recovery not + scheduled); +- a coverage read that outlasts the wait (the deadline runs from the check). + +The release sweep also checks that no upstream load starts after a release. + +Classified survivors: + +- Equivalent: an early `isCurrent` check, rewriting an unchanged record, the + on-demand check in `settleCoveredDemand`, the timer clamp, a no-op + unsubscribe, re-settling a settled demand, the hydrated path's refetch + guard (repeated in `settleCoveredDemand`), the hydrated path's + expired-claim guard (a read under an expired claim already proves + nothing), and ignoring the lifecycle generation in the reset epoch (a + replaced lifecycle's sync callbacks are inert). +- Not distinguishable here: the forward guard after a release, because + hydration checks the abort signal at each step before it forwards a load. +- Open, outside this owner or needing a new history: the clock read after + the claim wait; the hydrated mark after a truncate, which the hydration + path owns; and six observer and gate mutants for the persisted-readiness + owner. Those six skip the restore check, the preload registration and its + check, or the client-stream failure in `isInitialRenderReady`; take the + source as the demand owner; or stop rendering at once on network + readiness. + +## 2026-10-27: closing the remaining gaps + +Starting head: `0db1fa68b` (CI green). No production change. + +- **Readiness owner.** Six histories in `persisted-readiness-oracle.test.ts` + kill the six observer and gate survivors: + - a restore held past the deadline, and a gate read before the preload + starts; + - a retried observer after a failed client stream, over an empty and a + nonempty restore; + - a retried observer after the Collection's preload has started; + - two readers of one on-demand source, only one of which has settled; + - a network answer before a 60 s deadline. + + The owner passes 77 of 77. One finding is left for design review: the + on-demand initial-render wait rarely differs from network readiness, + because an on-demand query becomes ready when its demands settle. Only the + synchronous gate distinguishes the two-reader history. +- **Claim renewal.** A history holds the load's own cache-claim renewal past + the wait; a wait measured from after the renewal fails it. The first + attempt held the startup renewal instead, before the load started, and its + failure on unchanged code came from the history, not production. Every + survivor of the sweep in this owner is now killed or classified. +- **Windows in the campaign.** The campaign adds the window `w` over + generated ranks with ties. A query is ready offline exactly when every + load it sent in session 2 is covered, so the model judges the + tie-boundary load without predicting when core sends it. A driver check + that every active demand loads again right after a truncate did not hold + for the window, and the model no longer depends on it, so it was removed. + Mutants that ignore window keys, compare them in order, or record keys for + every demand each fail the campaign. So do the five earlier campaign + mutants. +- **Remaining hosts.** Receiving witnesses run the five native histories on + Electron (two app launches over one file, in the desktop E2E job), + Cloudflare Durable Objects (`wrangler dev` restarted over one persist + directory, in the cloud-mobile job), React Native (an op-sqlite-shaped + driver, cloud-mobile job), Expo (an expo-sqlite shim, not in CI), and Tauri + and Capacitor (their unit-suite SQLite stand-ins). In each, a 60 s + session-2 wait fails the two covered histories. The native Tauri and + Capacitor paths need `cargo` or a device here. The Expo witness failed once + on its first local run, under concurrent load, and passed the next ten + runs; the cause is unknown. + +## 2026-10-27: one judgment for every fixed history and receiving witness + +Starting head: `4855c67e9`. No production change. The maintainer asked for a +shared receiving contract, a committed mutation harness with a nightly run, +and more campaign grammar; reading the current `oracle-authoring` skill and +oracle guide then showed that the fixed oracle and the witnesses broke +ORC-016 and ORC-014 by stating their own expected answers. + +- **Shared judgment** (`696d319a8`, `86efc7f7f`): + `tests/persisted-demand-coverage-model-oracle.ts` holds the model and + comparison. It replays the source's own log and judges each load of a + query against its exact demand's record. It covers: + - per-load waits, generation ends, and refetch loads; + - failed coverage reads and checks; + - settlement by the upstream load, cursors, and offsets; + - an eager-and-on-demand join. + + Parts 2 to 4 of the fixed oracle now record and pass their histories to + it. Their hand-written expectation tables are gone, and two pinned checks + owned elsewhere moved to `persisted-demand-coverage-supplemental.test.ts`. + Part 1 keeps its own bounded trace model. +- **A model error found on the way:** a window's tie-boundary load starts only + after its limited load settles and measures its own wait. A single wait + premise per query predicted the window ready at 1500 ms, while production + correctly still waited. The premise is now per load. Production was right. +- **Campaign grammar** (`696d319a8`): session 2 generates a 0 or 1000 ms + wait and a source that never answers or fails at once, with checkpoints + before and after each load's deadline. A mutant that settles a covered + load without waiting fails both campaigns. +- **Receiving contract** (`544c64340`): `tests/contracts/` holds six + histories as data. Every host and framework records and passes its + observations to the shared judgment. All pass. With the contract's session-2 + source forced to a 60 s wait and a silent upstream, every host and + framework reports violations. +- **Mutation harness and nightly job** (`cd9544ce5`): + `scripts/demand-coverage-mutants.mjs` applies 66 mutants in memory through + `vitest.mutants.config.ts`. The full run on `cd9544ce5` matched every + expected outcome. The 22 mutants tied to the converted histories are each + killed, nearly all by the converted fixed histories themselves. + `.github/workflows/oracle-nightly.yml` runs the campaign with ten times its + PR budget and the full mutant list. + +Limits: the campaign generates no joins and no page moves, which the fixed +oracle and the shared model cover; native-runtime witnesses remain blocked +here as the coverage map records. + +## 2026-10-27: integrating the source-health change (#2168) + +Merge commit: `9262ec82d`. #2168 removed the observer's network-first gate, +`PersistedReadinessSource`, and `packages/db/src/persisted-readiness.ts`. A +network-first eager Collection now becomes `ready` from its restored rows, +and Suspense releases at query readiness. The core half of this PR (the +builder's `initialDemandsSettled()`, its listeners, and the observer's +on-demand sources) therefore has no place left and is gone; this PR changes +no `packages/db/src` code now. The law is unchanged. An on-demand source +already permits initial readiness, so a joined query is ready when its eager +source has opened and its demands on the on-demand source have settled. + +- **Production:** `openFromDisk` skips an on-demand Collection. Without the + guard, an on-demand Collection whose covered rows were restored before the + deadline would become `ready` at the deadline, though its source never + called `markReady()`. For Electric `progressive`, that status claims a + completed full sync. +- **Law added to the shared model:** settling loads from SQLite never changes + the on-demand Collection's own status. The guide already stated it ("only + the load settles from SQLite"), but no oracle observed it: mutant P39, which + drops the guard, survived the first sweep after the merge. The model now + judges an optional Collection observation. The campaign, the fixed + lifecycle history, and the joined histories supply it. With it, P39 is + killed by the fixed campaign. These histories never call the source's own + `fail()`; that path is the source-health law's. +- **Part 3:** the joined histories observe the query's status directly + instead of the removed observer gate. The source-failure histories that + closed H1 moved to the source-health law, which #2168 owns in + `packages/db/tests/source-health-oracle.test.ts`. +- **Mutation harness:** the 29 mutants whose targets #2168 deleted (D01–D11, + B01–B10, O01–O08) are gone; P39 is new. 38 mutants remain. +- **Receiving witnesses:** all 13 hosts and frameworks pass on the merge. The + Vue witness cleans up with `{ dependents: true }`, as #2168's cleanup + admission requires for a query with no component scope. +- **Readiness oracle:** main's case that expected on-demand network-first to + throw contradicts this law and is removed. This owner covers that + configuration in every history. + +Limits: the Collection observation is not yet part of the receiving +contract, so hosts and frameworks do not check it; the wrapper is the only +production path that decides it. + +## 2026-10-28: high code review of the head after #2168 + +Reviewed head: `83b10fd97` (clean worktree, files matched `gh pr diff`). +Repairs: `de9fec4c3`; merge of #2278: `32404fa2a`. Each oracle change ran on +the parent production code before the repair. + +| ID | Claim | Verdict and evidence | Action | +| --- | --- | --- | --- | +| R1 | An unordered demand that loses one row and gains another stays covered | True. This is the approved row-count law (maintainer decision, 2026-10-26); a fixed history pins it. The guide overclaimed that a removed row "therefore ends coverage". | Accepted design. The guide now says a row that arrived meanwhile keeps the count. | +| R2 | A failed coverage commit fails a load whose rows applied; the synchronous path ignores it | The asymmetry is real, the consequence is not. A probe failed the coverage write before and after its effect: the Collection crashed both times, as for any durable write (glossary, "crash"), so the load's outcome decides nothing. The one non-durable rejection, an abort while scoped recovery is pending, also aborts the load's own row transactions. | Refuted as stated. The storage sweep now asserts the crash (`coverage write failed`). No mutant separates this path from the general crash law. | +| R3 | A released load whose source resolves anyway records coverage | Confirmed. The glossary's "abort" says an aborted load's later outcome decides nothing, and the signal contract lets a source stop and resolve. RED: the campaign's new `stops and resolves` source; counterexample `start w, release w` settled `w` offline. A first run reached the state only at teardown, where production cannot record either; teardown now drops waiting loads first, and the declared reach `demand settled only by an aborted load` forced the distinguishing case. | Fixed: `recordCoverage` treats an aborted request as obsolete. Mutant P41 killed. | +| R4 | `begin({ snapshot: true })` does not delete coverage records | Confirmed. A snapshot transaction replaces every row as a truncate does (glossary). RED: new `snapshot` command; counterexample `cover a, snapshot` settled `a` offline. | Fixed: truncate and snapshot share `endDemandCoverage`. Mutant P40 killed. | +| R5 | `readDemandRows` lacks the scoped-recovery guard | Confirmed. Coverage counts only while the persisted subset restore returns the rows (glossary), the restore publishes none during scoped recovery, and scoped recovery does not certify the generation for offline reuse (Electric guide). RED: Part 2b `a load after scoped recovery reloaded the demand` showed `ready` on `a1`, `a2`. The first RED was false: the history started recovery before any sync run, and the driver's hook silently did nothing; the driver now throws on that premise. | Fixed: the coverage read proves nothing during scoped recovery, so no load settles from SQLite or records. The reviewer's proposed fix (reuse the restore read) would have recorded a zero-row coverage that every later restore satisfies. Mutant P42 killed. | +| R6 | Up to three SQLite reads per load | True, and the hydrated fast path now reads when a record exists. | No change: H7 kept the read; no work promise exists. Reusing the restore's rows for the check remains an optimization. | +| R7 | Coverage metadata grows without bound | True: one record per demand key until a truncate or rotation. | Accepted design: expiry is deferred to #2211 by maintainer decision. | +| R8 | The model imports core's demand key | True, against AGENTS.md ("Keep Oracles Independent"). | Fixed: the model compares request data itself (offset 0 as none). Every existing history agreed. Mutant K01 (coverage keyed without cursor and offset) is killed. Mutating core's own key is not distinguishable: core's deduplication uses the same key, so the merged key changes which loads core sends, and the model reads those from the record. | +| R9 | On-demand Collections install a no-op deadline timer; `onDemand` duplicates `syncMode` | True. `failUpstream` also calls `openFromDisk`, so its guard stays either way; skipping the timer adds a branch. | No change. | +| R10 | The abort listener stays after the timer or upstream settles | True but bounded: core creates one signal per subset request (`subscription.ts`, `subset-demand-controller.ts`), so the listener lives only as long as its request. The "repeated loads on one signal" premise does not occur. | Refuted as a leak. | + +Totals: 10 items; 4 fixed (R3, R4, R5, R8), 2 accepted design (R1, R7), +2 refuted (R2, R10), 2 no-change confirmations (R6, R9). R1 also fixed a doc +overclaim, and R2 added a sweep assertion. + +Why the suite missed R3 to R5: the grammar had no snapshot transaction, no +source that resolves an aborted load, and no online load after scoped +recovery in the same sync run. The model shared core's demand key (R8). + +### Never-run queries after a completed progressive full sync + +The guide claims that a query that never ran renders offline once a +progressive full sync completed. No witness checked it: the Electric witness's +session 2 read only the subset session 1 had loaded. The shared model gains a +`full sync completes` source event (authority: `ARCHITECTURE.md`, "A source +that resumes from complete local rows settles its loads at once"), and the +Electric witness's network-first block becomes a production driver for it, +over session 1 ∈ {full sync, subset only, nothing} × session 2 reads +{the loaded subset, a never-loaded subset}. All six histories pass; the +full-sync, never-loaded history shows `ready` on its restored row and reaches +`settled from a completed full sync`. Mutant E01 (drop the complete resume's +`resolveFullSnapshot(true)` in `electric.ts`) fails exactly that history. This +covers #2202 without a new adapter API, unlike #2203's +`isPersistedSnapshotComplete`. + + +## 2026-10-28: final high review of `d9bc29d74` + +Reviewed in a clean worktree whose files matched `gh pr diff`; the reviewer +could not run tests there. No production change followed. + +| ID | Claim | Verdict and evidence | Action | +| --- | --- | --- | --- | +| S1 | A covered demand settles while a source truncate is staged but uncommitted | Refuted on the Electric path: a same-path probe (cover group a, then an open `must-refetch` in session 2, then a group-a query offline) kept the query `loading` with no rows until `up-to-date`. On the generic path the precondition needs a source transaction held open across a new demand's hydration, which the wrapper rejects at commit (`cannot cross a hydration cycle`); the oracle already excludes that history (X5). A probe history reproduced exactly that rejection and was removed. | None | +| S2 | A rejected coverage commit fails a load whose rows applied | Duplicate of R2. The new path, a coverage transaction queued behind hydration and rejected by a later reset, belongs to a load that core already aborted at that reset (see S5), so its outcome decides nothing. | None | +| S3 | A load without a signal, released by `unloadSubset`, records coverage | Refuted: every on-demand subset load carries its own `AbortController` signal (`subscription.ts`), and an acquisition release aborts it (glossary, "acquisition release"). | None | +| S4 | A `BrowserCollectionCoordinator` leader without a subset owner records and settles coverage | Refuted for conforming coordinators: `registerRemoteSubsetOwner` is required, and the wrapper registers the owner synchronously when the source session starts and again inside scoped recovery's mutex turn. The OPFS witness with a real coordinator waits for the network. | None | +| S5 | The reset epoch is captured after the source's `loadSubset` returns | Same-path history added (the source truncates synchronously inside its own load call): it passes on production, because core's truncate replay aborts the in-flight load and an aborted load records nothing. | Regression history kept | +| S6, S7 | Extra SQLite reads and a metadata commit on the online path | Duplicates of H7 and R6. The synchronous path also reads on every deduplicated re-subscription. | No change; no work promise | +| S8 | Unbounded coverage metadata | Duplicate of R7 (#2211). | Accepted design | +| S9 | Abort listener and timer outlive their load | Duplicate of R10; the timer resolves an already-settled load. | None | +| S10 | The wait starts after startup, so offline it is startup plus `networkTimeoutMs` | True, by design: the wait is a preference for the network and runs from when the wrapper asks it, as the eager deadline runs from the sync start when its source starts. | Accepted design | + +The full mutation sweep after the earlier repairs reported three survivors, +because the abort check (R3) and the scoped-recovery guard (R5) now subsume +older guards on the tested paths: + +- P33 (no reset-epoch check) is killed by a new history: an empty synchronous + answer, then a truncate during its coverage read, with the replay left + waiting. A synchronous answer's load has already settled, so core cannot + abort it, and only the epoch check stops the stale record. +- P05 (the epoch ignores the reset sequence) and P10 (the record ignores its + generation) are equivalent in the single-process scope: every in-run + rotation or reset-sequence change that can meet a record or a load goes + through scoped recovery, where the coverage read proves nothing, and + claim recovery after startup also calls `startScopedRecovery`. A new + history with an empty covered demand and an expired claim confirms P10 + survives with its premise reached. + +Totals: 10 items; 0 fixed, 6 refuted or duplicate-refuted (S1 to S5, S9), +2 duplicates of no-change cost items (S6, S7), 2 accepted design (S8, S10). diff --git a/docs/contributing/oracle-reviews/2026-10-22-dbclient-migration-feedback.md b/docs/contributing/oracle-reviews/2026-10-22-dbclient-migration-feedback.md new file mode 100644 index 0000000000..87ff5a3e3f --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-22-dbclient-migration-feedback.md @@ -0,0 +1,1994 @@ +# DbClient migration feedback evaluation + +Evaluation date: 2026-10-22. Source artifact: `Feedback.txt`, identified by its +SHA-256 under [Content identifiers](#content-identifiers). +The report describes db 0.15.1, react-db 0.5.11, electric-db-collection 0.6.2, +react-router-with-db 0.1.0, and Start 1.169, without a reviewed commit or an +application fixture. This evaluation uses fresh `origin/main` +`205ab7158d81680c3cc4f06cd9ebe62b30911a5a`, in the isolated +`codex/evaluate-dbclient-feedback` worktree. Current package versions are db +0.15.2, react-db 0.5.12, and electric-db-collection 0.6.3. + +The review found two reproducible correctness failures. A newly admitted ordinary +subscriber must be able to start a source left deferred by an abandoned render; +that defect is repaired here. A plain SSR hook can render a warm source's rows +that are absent from result-only transport; actual React hydration then recovers. +That defect remains unfixed, with a product decision needed about the initial +snapshot policy. A green scoped test run does not close it. + +Production changes add one executable call and an ordering comment. The React +oracle, shared lifecycle judgment, three core test drivers, +SSR guidance, and coverage map also change. At the initial evaluation closeout, +the changes were uncommitted and unpushed; only the helper's initial branch was +published. The user subsequently approved PR preparation after both review +findings were repaired. No external review response was sent. + +Source weight: production +2/-0, including the comment; oracle and ordinary +test files +332/-46. Documentation is separate. The production call repairs +the existing admission path without adding state or replacing its control flow. + +PR preparation also measured shipped bytes with +`pnpm size:compare --packages db,react-db --files` against the stated merge-base. +The final db production bundle grows by 8 gzip bytes and 116 brotli bytes. Its changed +module grows by 24 minified bytes before compression. The react-db bundle is +unchanged. A simplifier review found no safe consolidation: moving release into +the shared start helper or resumed-acquisition path broadens its authority. +The earlier experiment releasing from resumeSubscriber broke synchronous-error +and peer-release laws. The measured growth pays for the separate independent +demand boundary without a new state or branch. + +## Laws, experiments, and remaining enforcement + +### Hydration must begin from the server's observable snapshot — F02 + +Authority: the [SSR guide](../../guides/ssr.md) promises query-result transport +and hydration before hooks render. React requires the initial hydration output +to agree with server markup. The review exposes a missing transport boundary, +not an obligation to choose one particular capture implementation. + +The existing [React descriptor owner](../../../packages/react-db/tests/descriptor-query-binding-oracle.test.tsx) +checks request isolation, descriptor binding, transported results, source claims, +and handoff. Its green histories either register the observed query or supply +the corresponding normalized source data. They do not establish the behavior +of a second, unregistered plain query over a source loaded by another query. +This is an absent history dimension, not a wrong per-client reference map. + +The diagnostic history uses one descriptor and two projections. Preload the +first projection into the server client; it synchronously publishes +`{ id: 'one', value: 'server' }`. Render a plain hook for the second projection, +dehydrate the server client, hydrate a fresh browser client, and compare the +initial server snapshots. Then run real `hydrateRoot` in jsdom with an +`onRecoverableError` recorder. Explicitly preloading the second projection is +the distinguishing control. The expected relation is markup equality and zero +hydration recoveries, independent of the query evaluator. + +| History | Server markup | Initial browser snapshot | React recoveries | +| --- | --- | --- | --- | +| Second query unregistered | `server` | `` | 1; assertion RED | +| Second query explicitly preloaded | `server` | `server` | 0; GREEN | + +The source facts exist on the server at render time. The transport lacks the +second query's result and the source rows; waiting until browser commit cannot +repair that earlier observation. The real React receiving witness confirms the +reported host failure with a controlled source. It does not establish the +reporter's Start/router/Electric deployment. + +The unresolved choice is whether plain SSR hooks automatically capture their +observable synchronous result for transport, or initially render empty/loading +unless a result or normalized source snapshot was explicitly supplied. The +former needs capture and transport semantics; the latter changes observable +server behavior. Merely refusing new source starts does not solve reads from an +already warm source. Capturing only rows from eager source Collections with +status `ready` fixes one trace. It misses partial nonready rows and status flags. +Suppressing all unregistered rows would conflict with documented reuse of +explicitly normalized sources. + +The failing diagnostic and passing control are preserved in task scratch as +`dbclient-hydration-feedback.probe.test.tsx` and `react-hydration-red.log`. +They are not installed as a passing oracle or hidden behind an expected-failure +gate. The permanent owner remains the React descriptor owner, as recorded in +the [coverage map](../oracle-coverage.md). After the product decision, extend its +model and histories for ready/partial/nonready sources, full rendered status, +normalized-source transport, and mixed plain/Suspense/preload schedules; reject +the original behavior and a ready-only capture mutant at the hydration cut. +Actual Start/Electric delivery is a separate receiving witness. F02 is +`design-decision`, with the correctness failure still confirmed and open. + +### Independent subscription demand outlives an abandoned render — F03 + +Authority: public ordinary `Collection.subscribeChanges()` starts sync when +needed; `deferAcquisition` explicitly withholds that demand. React render-time +source deferral prevents work before commit, but does not grant an abandoned +render authority to veto a later independent reader. This composes the +Collection subscription contract with the descriptor owner's zero-work-before- +commit law. Source `preload()` is independent demand too, although on-demand +source preload alone does not acquire rows. + +The old React owner tested only the preload reader after suspension. Its +expected start rule was correct within that scope; its history grammar omitted +ordinary and deferring subscriptions. The first extension crossed all three +readers with startSync true/false and eager/on-demand sources (12 cells). PR +review then exposed cleanup reentry during ordinary admission, R01 below. + +The final React grammar has 24 cells: ordinary subscriptions cross those source +axes with no cleanup, cleanup from loading, synchronous cleanup from ready, and +held cleanup from ready (16 cells); preload and deferring readers retain their +eight no-cleanup controls. The core lifecycle owner adds 24 cases crossing the +four cleanup histories with no start gate, a held unrequested gate, or a held +requested gate, in both source modes. The shared finite judgment is defined in +`collection-subscription-lifecycle-grammar-oracle.ts`. It derives expectations +from demand and cleanup authority, without reusing production classifiers. + +Without cleanup, ordinary/preload demand starts once and reaches ready; +deferring demand remains idle with zero starts. An eager source publishes at +start; on-demand rows are owed to an ordinary subscriber, while source preload +alone does not acquire them. Cleanup during loading prevents adapter entry. +Cleanup during ready permits one entry and retires it. Held resource cleanup +retains the ready public status until settlement, as defined by the glossary. +Admission cannot retry a retired start. A fresh later subscriber may restart +it; otherwise the peer reuses the existing run. + +The real React driver renders an ordinary hook and throws a never-settling +promise inside Suspense, preventing commit. Direct public Collection operations +then supply demand. The driver checks adapter entries, cleanup calls, status +and the complete status trace at admission return and cleanup settlement. It +checks rows only in no-cleanup histories, and checks fresh demand afterward. +The core driver reaches the same judgment independently of React, including +an explicit no-gate control. These are finite histories, not a claim about all +concurrent schedules. + +| Subject | Result at intended checkpoint | Classification | +| --- | --- | --- | +| Original implementation, first 12-cell extension | All 4 ordinary cells see 0 starts instead of 1 | Historical assertion RED | +| First release-before-request repair, first 12 cells | All pass | Historical GREEN; omitted cleanup histories | +| Previously-requested-start mutant on that first repair | Both startSync=false ordinary cells fail | Historical assertion RED; not current calibration after reorder | +| Release-before-request repair, expanded cleanup grammar | 4/223 core owner cases fail and 4/24 selected React cases fail at admission return | Assertion RED | +| Original/no-release mutant, final 24-cell React grammar | All 16 ordinary cells fail on adapter entries or status; 8 controls pass | Assertion RED | +| Final request-before-release repair | All 741 surrounding core and 441 React tests pass | GREEN | + +The repair records ordinary demand, requests/starts sync, then calls +`sync.resumeStart()` to release any render gate. There is no trailing start +request that could retry after callback cleanup. It adds no state, retry, +recovery path, or lifecycle. Releasing the gate in `resumeSubscriber()` was +tried and rejected: that query-owned acquisition transition may occur while +the renderer still owns a separate start gate, and it broke existing +synchronous-error/peer-release tests. That distinction remains intact. + +Three core test files previously used an ordinary new subscriber to simulate +the renderer-held gate. Their drivers now create a deferring subscriber and +resume its acquisition while retaining the separate renderer gate. Their +queued request, cancellation, lease, and release assertions remain. Core tests +that directly exercise `_sync.loadSubset` still cover the actual deferred sync +queue. No expected result was changed to match a wrong production output. + +F03 is `fixed-now` for abandoned-render deferral followed by independent public +subscription demand in the finite eager/on-demand, startSync true/false grammar. +The model, histories and drivers check admission, cleanup settlement, and later +fresh demand; both the original design and release-before-request repair are +rejected. No remaining +counterexample is known within those cells. Arbitrary React interruption +schedules and Electric `awaitTxId` delivery are not established. In particular, +the reporter's downstream txid timeout was not reproduced against Electric. + +### Indexes must exist before compilation if diagnostics depend on them — F01/F08 + +This is an initialization requirement, not evidence of incorrect rows. Four +executed cells compile the same ordered-limit query before source sync: + +| Index setup | Sync starts | Missing-index warnings | Installed indexes | +| --- | --- | --- | --- | +| None | 0 | 1 | 0 | +| Inside sync callback | 0 | 1 | 0 | +| `client.collection(descriptor).createIndex(...)` before query | 0 | 0 | 1 | +| Eager auto-indexing | 0 | 0 | 1 | + +The report is right about timing and the missing descriptor config API. The +literal claim that there is no place to initialize indexes is too broad: an +application client initializer is available. The guide now shows it. Whether to +add declarative `indexes` or `onCreate` is a product/API decision. F08 retains its +unique hydration-fallback context but is a duplicate of F01's timing issue; +that exact React fallback/index-warning combination was not independently run. +The existing index-suggestion owner remains authoritative for diagnostic laws; +this evaluation does not invent a new runtime failure law. + +### Readiness, cancellation, identity, and ownership — F04/F05/F06 + +Preload waits for readiness. A controlled never-ready source starts once and +keeps the promise pending. Whole-client cleanup rejects it and stops the source +once. The API has no timeout or `AbortSignal` option. Thus F04 identifies an +operational need and API choice; it does not falsify an existing deadline +promise. Real unreachable-Electric retry behavior remains reporter evidence. +A timer race alone does not cancel work; the guide now calls out cleanup. + +The same prepared query produces different function-form `query` and +object-form `config` hashes. F05's diagnosis is correct. Config identity also +includes result settings such as schema, getKey, singleResult, and collation; +blindly merging hashes could alias different result contracts. The requested +guide note and shared-config example are implemented. This documentation action +is closed without changing identity semantics or adding a warning. + +Three distinct browser preloads produce three public result records and three +retained preloaded Collections. Once one mounted observer takes authoritative +live ownership, public records fall from three to two, while retained preloaded +Collections remain three. Whole-client cleanup exists, and private error/retry +paths can discard entries; therefore “nothing discards them” is too broad. +There is no public per-query release. F06 needs a policy for cache lifetime, +active observers, repeated preload, eviction, and release. Cardinality proves +retained ownership here, not endless active network work or a violated memory +budget. The guide documents current ownership and independently owned query +Collections as an alternative. This does not close the API request. + +## Lossless finding ledger + +Severity below is this evaluation's estimate; the report supplied no formal +severity ranking. `design-decision` does not mean fixed or permission to defer a +known defect. No item is marked deferred. + +| ID | Original claim and remedy | Evidence and technical verdict | Action | Durable destination | +| --- | --- | --- | --- | --- | +| F01 | 55 manual indexes have no descriptor creation hook; sync is too late; propose indexes/onCreate, use eager indexing | Timing confirmed by four cells; existing client initializer disproves literal lack of an initialization point. Low correctness severity, meaningful API friction | `design-decision`: declarative indexes versus initializer/eager policy | SSR index guidance; law analysis above | +| F02 | Plain warm-source SSR query renders rows but hydrates empty; use separate preload client or refuse unregistered source starts | Confirmed markup mismatch and actual React recovery, high SSR correctness impact; proposed refusal alone misses already warm sources | `design-decision`: capture versus initial empty/loading semantics; unfixed | React descriptor owner and coverage-map gap; preserved failing probe | +| F03 | Discarded render strands deferred start; ordinary subscriber should resume; preload works; awaitTxId may time out | Start defect confirmed; four original cells fail. Downstream Electric consequence unverified. High lifecycle impact | `fixed-now`: one executable call, 24-cell receiving grammar, 24 core cleanup histories and preserved acquisition boundaries | React descriptor owner, core acquisition owner, review calibration | +| F04 | Unreachable Electric can hang preload; request signal/deadline; race timer and cleanup | Missing option and controlled pending lifetime confirmed; real SDK retries unrun. Operational API need, no pre-existing deadline law | `design-decision`: signal/cancellation ownership and deadline policy | SSR lifetime guidance and API decision here | +| F05 | Function and config hashes differ; function hook misses preload silently; request guide note or warning | Distinct hashes executed; config result semantics prevent naive canonicalization. Medium migration friction | `fixed-now`: requested documentation alternative implemented; hashes unchanged | Shared-config SSR example and identity warning in guide | +| F06 | Browser preload map retains results on navigation; request public release; server-only workaround | Retained map confirmed; public records are consumed separately. Cleanup/private discard qualify literal claim. Potential unbounded workload-dependent retention | `design-decision`: release/eviction/cache semantics | SSR lifetime guidance and measured ownership history | +| F07 | Default ElectricCollectionUtils rejects Date descriptors; use generic any; old concrete Collection worked | Not reproduced by reliable direct strict compiler check on current source, including Date schema/non-schema, wrapper, and handler. Earlier isolated Vitest failure is contradictory and withdrawn as confirmation | `confirmed-open`, evidence gap; smallest failing assignment and resolved dependencies requested | Type probe in scratch and fixture request; no unsafe variance change | +| F08 | Hydration fallback warns about missing index before sync; eager indexing fixes | Compile-before-sync timing confirmed; exact fallback path not run. Low severity | `duplicate` of F01; unique fallback premise retained | F01 initialization guidance and this ledger | + +F07 needs particular care: `tsc --noEmit` with strictFunctionTypes enabled and +explicit absolute paths to current db/electric sources accepts assignments to +`CollectionOptions & NonSingleResult`. +It also accepts the report's generic `TConfig extends CollectionConfig` wrapper with a Date schema and typed mutation handler. Positive +non-never assertions avoid a vacuous `never` witness. Reversing the assertions +causes errors and leaves an unused `@ts-expect-error`. The tested fixtures do +not refute an unprovided application assignment. The report's Date/type-variance +explanation remains unconfirmed. There is no basis to loosen typed public +utilities or introduce production `any` for it. + +The surrounding setup and closing prose also contain useful claims. They are +kept separately rather than silently folded into the numbered issues: + +| ID | Contextual claim | Evidence, qualification, and action | Destination | +| --- | --- | --- | --- | +| N01 | 26 collections and about 50 hooks migrated; every page SSR/hydration and handoff succeeds with workarounds | `confirmed-open` for independent verification: reporter's application observation; no application fixture. Not an additional alleged defect | Retain integration testimony without claiming an end-to-end run | +| N02 | Descriptor factory receives request DbClient; handlers resolve another descriptor through it | `accepted-design`: client-aware factory signature in client.ts and SSR factory guidance. Source-verified; application handler not run | Existing scoped-client API and this review | +| N03 | Keep all browser sources syncing for txid waits; await only written tables, chosen from mutation result; unwritten collection “always times out” | `accepted-design` for evidence-driven waiting and scoped browser ownership, qualified: awaitTxId also accepts snapshot visibility evidence, so “always” is not a universal rule. Existing snapshot-end test exercises that exception | Electric awaitTxId/snapshot tests; do not turn the ERP's observation into a Postgres law | +| N04 | Separate finite-lived server preload client; query-results-only transport; timer, finally cleanup, graceful browser recovery | `design-decision` linked to F02/F04/F06. Cleanup is material; timer does not cancel; graceful application recovery not independently run | SSR lifetime guidance and unresolved transport policy | +| N05 | Shared builder/config lets loader and hook hashes match across clients | `fixed-now` documentation action linked to F05. A callback alone is insufficient across function/config forms; use the same config or config objects with the same explicit queryKey at both consumers, preserving compatible sources and result configuration | SSR shared-config example | +| N06 | Nested materialize descriptors resolve; descriptor ids stabilize identity across clients | `accepted-design`: existing descriptor owner checks includes/nested FROM, two-client identity and rows, with explicit finite limits | Existing owner, exercised in full React run | +| N07 | routerWithDbClient, Electric sync metadata hooks, and hydrated-to-live handoff work unchanged | `confirmed-open` for independent application verification; preserved positive report. Relevant component tests are not that ERP/Start integration | Reporter testimony; real-provider receiving limit above | +| N08 | Pass DbClient into module-level optimistic actions; wait for transaction settled before success toast | `accepted-design`: scoped-client mutation ownership and Transaction.when('settled') contract support this usage; reporter's toast integration not run | Existing transaction/client APIs; no unrelated optimistic machinery changed | + +## Reviewer assessment + +Recommendation: **hire**, with moderate confidence from this one integration +report. The author found two concrete lifecycle/SSR counterexamples, connected +them to practical application behavior, supplied useful workarounds, and +identified hash and retained-ownership details accurately. Signal is high and +the report preserves successful integration evidence as well as friction. + +Prioritization and fix design need strengthening. Hydration and independent +demand are correctness defects; index hooks, cancellation, cache release, and +type erasure need separate contract/API treatment. “No place,” “nothing +discards,” and unwritten collections “always” timing out overstate the evidence. +The typing claim lacks a minimal fixture and did not reproduce in the reliable +compiler check. Refusing source starts is an incomplete hydration remedy. +Missing tests, law boundaries, partial-ready states, and adjacent schedules were +not analyzed by the reporter. These reduce confidence in a ready-to-implement +fix specification, without diminishing the value of the original diagnosis. + +## Verification and replay + +Scratch root: `/tmp/evaluate-dbclient-feedback/`. It holds the unchanged raw +report, append-only initial ledger and corrections, diagnostic sources, and +logs. Scratch files are not intended repository changes. Logs can include +cleanup diagnostics; the decisive REDs below are assertion failures reached +before cleanup, not setup failures or timeouts. + +| Check | Executed result | Evidence file | +| --- | --- | --- | +| Expanded React subscriber grammar on original implementation | 4 subscriber assertion failures | original-subscriber-red.log | +| Historical previously-requested-start mutant before reorder | 2 false-config subscriber assertion failures | narrow-start-requested-mutant.log | +| Historical first-repair 12-cell subscriber grammar | 12 pass | react-subscriber-expanded.log | +| Full React suite before R01 | 22 files, 429 tests pass; type errors none | react-full-final.log | +| Full React suite after R01 | 22 files, 441 tests pass; type errors none | r01-react-green.log | +| Core surrounding suites before R01 | 7 files, 717 tests pass; type errors none | subscription-surrounding-final.log | +| Core surrounding suites after R01 | 7 files, 741 tests pass; type errors none | r01-core-green.log | +| R01 expanded core owner before reorder | 4 assertion failures, 219 pass; no suite/audit errors | r01-core-full-red.log | +| R01 expanded React matrix before reorder | 4 assertion failures, 20 pass | r01-react-red.log | +| Original/no-release mutant on final React grammar | 16 ordinary assertion failures, 8 controls pass; restored | r01-original-mutant.log | +| Final shipped-size comparison | db +8 gzip / +116 brotli bytes, changed module +24 minified bytes; react-db unchanged | r01-size.log | +| Diagnostic runtime probes | 7 pass: index timing, pending cleanup, identity, ownership counts | runtime-probes.log | +| Actual hydration diagnostic | 1 RED, 1 control GREEN | react-hydration-red.log | +| Reliable direct strict TypeScript check | Exit 0 on positive Date descriptor assignments | electric-tsc.log | +| Electric snapshot visibility exception | 1 existing test passes; 124 unrelated tests filtered; typecheck disabled for this targeted runtime check | txid-snapshot.log | +| Scoped ESLint | Exit 0, no errors; 3 pre-existing no-shadow warnings in lifecycle owner | Direct command output | +| Whitespace validation | git diff --check passes | Direct command output | + +Run the permanent React owner from `packages/react-db`: + +```sh +pnpm exec vitest run --coverage.enabled=false \ + --pool-options.threads.maxThreads=2 --pool-options.threads.minThreads=1 \ + tests/descriptor-query-binding-oracle.test.tsx +``` + +Run its demand matrix alone with `-t 'after an abandoned render'`. The original +calibration removes only the added `this.sync.resumeStart()` in addSubscriber. +The narrow calibration keeps that repair but changes sync.resumeStart's gate +to require both deferred start and a previously requested start; filter to +`"'subscriber' after an abandoned render"`. Those historical mutations were temporary and restored. The previously-requested +start mutant is no longer discriminating on the reordered path, which requests +before release; its old kill does not calibrate the current implementation. +The expanded final matrix is also run with the added resume call removed. +sync.ts has no final diff. + +Core surrounding command, from `packages/db`: + +```sh +pnpm exec vitest run --coverage.enabled=false \ + --pool-options.threads.maxThreads=2 --pool-options.threads.minThreads=1 \ + tests/collection-subscription-lifecycle-oracle.test.ts \ + tests/collection-subscription.test.ts tests/collection.test.ts \ + tests/db-client.test.ts tests/query/immutable-demand-boundary.test.ts \ + tests/live-query-deferred-acquisition-oracle.test.ts +``` + +Vitest's path matching also selects `tests/query/live-query-collection.test.ts`. +For scratch probes, copy each archived file back to its original package's +tests directory, run the same package-local Vitest filename, then move it back. +The electric probe uses direct `pnpm exec tsc -p +/tmp/evaluate-dbclient-feedback/tsconfig.electric.json --noEmit`, with that config +including the package-local file and absolute source paths. The hydration probe +is intentionally RED on this branch. It is not part of the green suite count. + +## Oracle review evidence + +This is a self-review of the modified oracle portions, not an independent +review of the whole owner or repository. Base HEAD is the exact commit above; +the content identifiers below bind the uncommitted executable changes to this +record. Recheck this evidence if those files change. + +| Requirement | Outcome for the changed F03 boundary | +| --- | --- | +| ORC-001 | Law and authority distinguish independent demand, explicit deferral, cleanup retirement, pending-cleanup status and on-demand preload limits | +| ORC-002 | Shared finite judgment derives entries and status traces from demand and cleanup authority; row predictions follow fixture facts. Neither reuses production classifiers | +| ORC-003 | Opening and adjacent prose distinguish the judgment, finite cleanup grammar, core and React drivers, and observation checkpoints | +| ORC-004 | Bounded enumeration: 24 React cells (16 ordinary cleanup cells plus 8 controls) and 24 core cells (4 cleanup histories × 3 gate states × 2 modes). Gate, loading/ready, held cleanup and mode axes distinguish concrete next observations. Arbitrary schedules and failed providers remain outside scope | +| ORC-005 | Real Suspense render establishes the abandoned-render cut. Drivers compare callback entries, cleanup calls and full status traces at admission return and cleanup settlement; later demand checks restart/reuse. Rows are checked without cleanup | +| ORC-006 | Original/no-release mutant fails 16 final React cases. Release-before-request repair fails four core and four receiving cases at admission return. These are assertion failures, without timeout/setup failure | +| ORC-007 | Not triggered: bounded enumeration, no new random property or replay grammar | +| ORC-008 | No stateful reference machine added; finite demand/cleanup judgment distinguishes callback entry, public status, trace and fresh demand. Pending cleanup is explicit, without copying production state | +| ORC-009 | Uses source, sync run, acquisition, subscription, and render/commit cuts as in glossary; fixture starts are sync-entry instrumentation, not a new production state | +| ORC-010 | finally resolves held cleanup, removes listener, releases peer/subscriber/tree/client and handles pending preload rejection. Original-mutant source restoration uses finally; no shrinking layer changed | +| ORC-011 | Expected entry counts and cleanup retirement derive from public contracts; preload/deferring controls and fresh-demand restart challenge overbroad suppression. No named shared semantic computation fault requiring another formulation was found | +| ORC-012 | This exact-base/content record, loss audit, and coverage-map update state the finite closure and open receiving cells | +| ORC-013 | Ordinary versus deferring demand distinguishes veto from ownership. Callback cleanup rejects trailing restart; held cleanup distinguishes delayed retirement. A new peer rejects suppressing all restarts. Historical previously-requested-start mutant evidence does not calibrate the reordered path | +| ORC-014 | Real React supplies the abandoned-render premise. Sync readiness/publication remains controlled; Electric txid delivery and arbitrary host schedules are expressly outside the closure claim, with owner/gap in coverage map | + +## Content identifiers + +SHA-256 of the executable files reviewed above, on the stated base plus working +changes: + +| File | SHA-256 | +| --- | --- | +| `packages/db/src/collection/changes.ts` | `9b7d6bf3f24bd23cfc383fd416630527134e8082dc04b563b1269247351651e7` | +| `packages/react-db/tests/descriptor-query-binding-oracle.test.tsx` | `ac6686ebc5caa4aa4aceca43fb1b89e495ab1e01e923c866e35ed4835f07940d` | +| `packages/db/tests/collection-subscription-lifecycle-oracle.test.ts` | `ed48ff71b10b5878a182527ce3ce29bf1f3866b848b91b4cb164a881d88f75f4` | +| `packages/db/tests/collection-subscription.test.ts` | `eaea5e97d9af83383af3c9595f05db4f1dbdbafbd13ea8c30e3a209347af3239` | +| `packages/db/tests/db-client.test.ts` | `8ff61c5f994db41062834c4857502f96a9118678b43a6da41571bb1749d50bb7` | +| `packages/db/tests/collection-subscription-lifecycle-grammar-oracle.ts` | `4b915ee56770f18334ab477cf7710ae2108e0dbaada2cd5654a1f6be3c25499b` | + +The source report SHA-256 is `846313a43c3deae3b72abcd7ae2102c4b589460465ee838c9fb42fa6dd379922`. + +## Loss audit and implementation state + +Returned to the raw report after verification: all eight numbered issues, +setup/helper suggestions, and closing positive claims are represented above. +F08 overlaps F01 without losing fallback timing; N04/N05 preserve workaround +details without erasing their corresponding defects. No sibling production +defect was confirmed. The erroneous intermediate typing conclusion is corrected +in the append-only scratch ledger and here. + +Numbered finding totals: 2 `fixed-now`, 4 `design-decision`, 1 `confirmed-open`, +1 `duplicate` = 8. Contextual claim totals: 1 `fixed-now`, 1 `design-decision`, +2 `confirmed-open` reporter observations, 4 `accepted-design` qualified API +usages = 8. Combined: 3 fixed + 5 decisions + 3 open + 4 accepted + 1 duplicate += 16 raw items. These are disposition counts, not 16 production defects. +There are zero deferred, stale, already-fixed, or fully refuted items. + +Item accounting is complete; implementation is not fully complete. F02 remains +a demonstrated defect pending its exact initial-snapshot contract. F07 needs +the failing application fixture. Index initialization, cancellation and preload +release need API decisions. Reporter deployment success, actual unreachable +Electric retries, the txid consequence, and Start/Electric hydration are not +independently established. No postponement of confirmed repair work has been +approved or inferred. The permanent suite's green excludes the explicitly +preserved failing hydration diagnostic. + +Initial evaluation readiness: **not assessed**. The original request was to +evaluate feedback, before the later PR preparation request. The known +hydration counterexample prevents claiming this work closes all migration bugs. + +## Subsequent PR preparation review + +The user subsequently requested PR preparation. Parallel simplification and +independent production/lifecycle and oracle/documentation review found two +introduced issues. No simplification was warranted. The user accepted R01; the +admission path is now repaired and the expanded oracle covers cleanup reentry. +The user also accepted OD01; the guide and N05 now require config objects at +both consumers when using an explicit queryKey. + +| ID | Finding | Evidence and gap | Current action | +| --- | --- | --- | --- | +| R01 / PL01, P2 | Ordinary subscription can restart a source after its startup callback cleans it up | A deferred, previously requested start reaches ready, whose listener calls cleanup once. At subscribeChanges return, starts=2 and status=ready; the trace is loading, ready, cleaned-up, loading, ready. Nondeferred and deferred-without-queued-request controls each start once and remain cleaned-up. The copied original stays stranded at zero starts/idle in both deferred cases. Existing cleanup law requires retirement; new reader histories omitted callback cleanup, while core cancellation histories use explicit gate release without this admission path | Fixed after user acceptance. Mark demand, request/start sync, then release the gate. The shared cleanup judgment reaches 24 core and 24 React cells; original wrong order fails four cases in each path, and the repair passes the surrounding suites | +| OD01, P3 | Added queryKey guidance promises unsupported cross-form reuse | getExplicitQueryKey reads object inputs only; the bare callback hook accepts a dependency array as its second argument. Therefore an explicit loader key cannot connect useLiveQuery(openTodos.query) | Fixed after user acceptance. The guide and N05 require config objects with the same explicit queryKey at both consumers, preserving compatible sources and result configuration. Verified against the hook overloads, getExplicitQueryKey and the preload key lookup | + +The runtime probe ran successfully against the current source and an isolated +copy with only the added resume call removed. An earlier TypeScript-script +setup failure is excluded from the evidence. The parent repeated the successful +three-case current-source probe. Raw independent reviews, exact scripts, and +stdout are preserved under the same scratch root as `prep-code-review.md`, +`prep-production-review.md`, `prep-production-current.log`, +`prep-production-original.log`, and `prep-oracle-docs-review.md`. + +The hydrated-row core test now resumes detached demand rather than itself +proving the exact sync-layer queue path. Adjacent explicit _sync.loadSubset +and lifecycle request histories preserve that path; this is a qualification, +not another confirmed defect. Internal resumed deferring acquisition remains +distinct from new ordinary demand. Executable content identifiers above bind +the accepted repair and expanded judgment. + +F03 is fixed-now within the expanded finite scope. Numbered totals return to +2 fixed, 4 decisions, 1 open, and 1 duplicate. R01 and OD01 are fixed. +The eight contextual items retain their earlier dispositions, with N05 qualified +by OD01. R01 and OD01 are additional PR-review items, not additional claims +attributed to the migration reporter. The user approved committing the reviewed +changes and generating PR documentation. The open hydration +policy and missing typing fixture remain outside this repair's closure claim. + +A focused independent follow-up reviewed the accepted R01 repair and changed +judgment/drivers after calibration. It found no residual actionable defect: +request-before-release removes the trailing restart; the shared judgment is +independent; both drivers check entries, cleanup calls, full status traces at +admission return/settlement, and fresh-demand restart. It verified the counts +for 24 core cases and 24 React cases, including loading cleanup and held cleanup. +This was a read-only source review, not a new broad test run or whole-PR +readiness verdict. + +## Subsequent CodeRabbit evaluation + +[Review 5618234197](https://github.com/TanStack/db/pull/2229#pullrequestreview-5618234197) +covers `2975790568cab6c4834db575fe22904693f669e3`. The next head +`75a7d043fee10f50f34ecd4b1f13df918b2bf373` changes only skill instructions; +all six reviewed executable hashes still match. The inline comment's newer +commit metadata does not change the review body's stated coverage. + +| ID | Claim or contextual assessment | Disposition | +| --- | --- | --- | +| C01 | LGTM shared cleanup judgment | accepted-design | +| C02 | LGTM ordinary subscriber production order | accepted-design | +| C03 | LGTM db-only patch changeset | accepted-design | +| C04 | LGTM core lifecycle changes and cleanup histories | accepted-design | +| C05 | LGTM subscription driver deferral changes | accepted-design | +| C06 | LGTM client test driver changes | accepted-design | +| C07 | LGTM React receiving grammar | accepted-design | +| C08 | LGTM coverage-map limits | accepted-design | +| C09 | LGTM SSR guide identity, indexes, lifetime | accepted-design | +| C10 | Rewrite 24 core and 24 React case counts | fixed-now | +| C11 | Clarify ready eager rows | fixed-now | +| C12 | Replace machine-local Feedback.txt source path with stable identifier | fixed-now | +| C13 | Mutable summary warns 0% docstring coverage against 80% threshold | accepted-design | +| C14 | Mutable summary says mergeable with documentation follow-up | accepted-design | +| C15 | Mutable summary title check passes | accepted-design | +| C16 | Mutable summary description check passes | accepted-design | +| C17 | Mutable summary linked-issue check skipped | accepted-design | +| C18 | Mutable summary out-of-scope check skipped | accepted-design | + +The three corrections change prose only. The source reference now uses the +existing immutable artifact identifier, whose SHA-256 matches the archived raw +feedback. Counts and Collection status wording retain the original meaning. +Direct source and link checks, plus whitespace validation, are sufficient for +these findings. No production or oracle behavior changed, so no new runtime +RED/GREEN claim is made. + +The docstring warning names no functions and supplies no repository authority +for its 80% threshold. The admission method has JSDoc, and the shared judgment +has adjacent literate contract prose. Preserve that approved documentation +form rather than add redundant comments for an unverified external metric. + +Loss audit: 18 items = 3 fixed-now + 15 accepted-design contextual assessments. +No CodeRabbit finding is deferred or confirmed open. Raw review, inline data, +mutable summary and the detailed ledger are retained in task scratch. Related +PR #2060 is a useful navigation pointer, not new proof of current behavior. +Reviewer recommendation: hire, with low confidence from this review alone. +It supplies three useful documentation corrections and no new runtime +counterexample. The original open hydration decision and typing fixture gap +remain separate; pending CI and a mergeable flag do not establish PR readiness. + + +## Cleanup admission follow-up — CodeRabbit review 5620262151 + +[Review 5620262151](https://github.com/TanStack/db/pull/2229#pullrequestreview-5620262151) +covers `75a7d043fee10f50f34ecd4b1f13df918b2bf373`. Its runtime code is +identical to the starting head `7f2ad8e12f44c1811d1b557b19b9116435259763`. +This appendix audits the follow-up on that head. The executable content hashes +below identify the tested tree independently of this report's commit. + +### Law, experiment, and repair + +A same-path diagnostic reproduced the admitted-but-idle subscription in eight +cells: loading/ready status, eager/on-demand mode, and both initial-snapshot +options. The adapter cleanup promise remained held when `subscribeChanges` +returned an active subscription. After cleanup settled, the Collection stayed +`cleaned-up` with one subscriber until an explicit restart. The same observations +held with `changes.ts` from merge-base `205ab7158`; this predates the PR. + +The review proposed automatic retry. The public cleanup contract instead closes +restart admission while cleanup is active. On 2026-10-22, the user selected +rejection: “yes reject during cleanup.” This decision applies to ordinary +subscription admission and deferred acquisition resumption. A subscription that +still defers acquisition can retain logical demand without starting source work. +Existing subscriptions can survive cleanup for an explicit restart. An Effect +constructed during active cleanup must still return a disposed handle. + +| Law and authority | Oracle extension and observations | Decisive evidence | +| --- | --- | --- | +| Ordinary admission and deferred resumption reject during active cleanup. Authority: the user decision, `subscribeChanges` contract, and glossary cleanup boundary. | The primary cleanup/restart owner's three-cut model now drives 18 cells: loading/ready/error, eager/on-demand, and plain/snapshot/resumed admission. At the held cut it checks the error, unchanged subscriber ownership and callbacks, zero new sync runs or acquisitions, and retained deferral. After settlement it checks no automatic restart, successful explicit admission, and the replacement row. | Original implementation: 18 admission assertion failures. Omitting only the resume guard: six resumption assertion failures. Final owner: all 70 tests pass. | +| Cleanup start makes an Effect terminal without retaining partial source ownership. Authority: the existing Effect cleanup contract and glossary. | Twelve receiving histories use real `createEffect`: all three prior statuses, both source modes, and cleanup on either joined source. They check a disposed handle, one cleanup report, no batches, no source subscriptions or retained cleanup observers, and successful new construction after settlement. | Guards with the old Effect observer order fail all 12 receiving histories during construction with `CollectionStateError`. The final observer order passes them. | + +The production repair adds the existing lifecycle admission assertion before +ordinary ownership or deferred resumption changes. The Effect cleanup observer +moves before subscription admission. It can retire a late Effect before that +Effect requests forbidden source work. There is no retry queue or new state. +These are two new executable guard statements; the Effect machinery is moved. +Tests and contract prose account for the remaining source growth. + +The old cleanup owner checked start/preload admission and the late Effect +handle, but did not exercise fresh ordinary admission or deferred resumption +while adapter cleanup remained pending. The general subscription grammar models +completed cleanup and explicit restart. R01 checks cleanup during an admission +already in progress. Neither history is this new admission boundary. + +The new oracle retains the independent three-cut judgment. It adds production +paths and finite histories, without changing that model to match production. +A deferred subscription may report its local snapshot before a rejected resume. +The callback recorder preserves that prefix and requires rejection to add no +callbacks. The later row projection deliberately compares only `id` and `rank`; +virtual-field semantics are outside this law. All recorder entries remain +countable, so an extra callback cannot disappear in a final row map. + +### Executed evidence and limits + +Scratch root: `/tmp/tanstack-skill-reading-update/`. It retains the complete raw +review, append-only item ledger, diagnostic source, and logs. Calibration restores +the final source bytes in `finally` and verifies their identity. The original +and missing-resume outcomes are assertion failures at the held admission cut. +The guard-only outcome is a runtime failure after setup at Effect construction; +it is not a timeout or a refinement-assertion kill. + +| Check | Result | Evidence file | +| --- | --- | --- | +| Same-path current and merge-base diagnostics | Eight observed histories pass on each version | cleanup-probe-current.log, cleanup-probe-base.log | +| Original implementation, final 30-cell selection | 18 admission failures, 12 Effect controls pass | cleanup-calibration-original.log | +| Fresh-subscriber-only guard | Six resumption failures, 24 controls pass | cleanup-calibration-no-resume-guard.log | +| Guards without Effect observer reorder | 12 construction failures, 18 admission controls pass | cleanup-calibration-guard-only.log | +| Core and Effect surrounding suites | Ten files, 951 tests pass | cleanup-surrounding.log | +| Collection, DbClient, immutable-demand and receiving query suites | Four files, 224 tests pass | cleanup-additional.log | +| Full React suite | 22 files, 441 tests pass | cleanup-react.log | +| Scoped ESLint and whitespace | No lint errors; diff check passes | Direct command output | +| Production bundle against merge-base 205ab7158 | db +12 gzip / +64 brotli bytes; React unchanged | cleanup-size.log | + +All Vitest runs report no type errors. The complete repository `pnpm test` +command was not run. The ordinary receiving checks use controlled adapter +cleanup and real Collection entry points; the Effect checks use that same +premise through real Effect construction. They establish the stated finite +admission and ownership cuts, not actual provider shutdown or every framework +schedule. The owner already has cleanup rejection and terminal-status restart +histories. Ordinary admission specifically at a terminal-status callback, +multiple overlapping cleanup failures, and arbitrary framework scheduling are +not new coverage here. The primary cleanup/restart owner owns additional core +boundary histories; framework conformance drivers own their host schedules. + +Replay the new finite selection from `packages/db`: + +```sh +pnpm exec vitest run --coverage.enabled=false \ + tests/collection-cleanup-restart-oracle.test.ts \ + -t 'closes .* admission during|releases a late Effect' +``` + +The ten-file command adds the lifecycle history, acquisition cells, subscription +reentrancy, deferred acquisition, sync reentrancy, idle cleanup, ordinary +subscription, Effect, and Effect disposal suites named in the evidence table. +The four-file command selects `collection.test.ts`, `db-client.test.ts`, and +`query/immutable-demand-boundary.test.ts`; Vitest also selects the receiving +`query/live-query-collection.test.ts` by path matching. + +| ORC requirement | Follow-up outcome | +| --- | --- | +| 001 authority and limits | The explicit rejection decision, public cleanup contract, and finite bounds are recorded above and beside the executable law. | +| 002 independent judgment | The reused three-cut model derives admission and terminality from the contract, without importing production classifiers. | +| 003 literate responsibilities | Opening prose, model, case arrays, real drivers, and checks remain distinguishable in the primary owner. | +| 004 generated grammar controls | No new generated property. The fixed products reconstruct the report and add error status, resumption, snapshot, and join-position neighbors. | +| 005 production path and observations | Public admission, callbacks, subscriber counts, statuses, replacement row, Effect handle, and cleanup observer ownership are compared at held and settled cuts. | +| 006 calibration | Original, missing-resume, and guard-only outcomes are classified above. Each reaches its named admission or construction boundary. | +| 007 campaign parity and replay | No new important generated property. The finite selection runs directly with the command above. Existing lifecycle campaigns still run fixed and random seeds. | +| 008 state minimality | No reference-model state was introduced or removed. | +| 009 vocabulary mapping | Existing cleanup-start, settlement, subscription, acquisition, and sync-run concepts are reused. The prior model's dependent abstraction is unchanged. | +| 010 failure fidelity | New drivers use the owner's cleanup aggregator to preserve primary and teardown errors and release every resource. Its existing calibration remains green. | +| 011 second formulation | No concrete shared semantic fault remains after the explicit policy choice; no additional independent formulation is claimed. | +| 012 review evidence | This appendix records applicable outcomes and exact source identities. This is a self-review, not an independent agent review. | +| 013 boundary witness | Held cleanup rejects while explicit admission after settlement succeeds. Ready, loading, and error statuses prevent a ready-only repair from defining the law. | +| 014 receiving handoff | Real Effect construction receives controlled adapter cleanup through the same owner and judgment. No real provider-shutdown or all-framework claim is made. | + +### Loss audit and reviewer assessment + +CR2-01 is the single unique behavioral finding, repeated in generated prompts. +Its admitted-but-idle counterexample is confirmed. Its proposed retry is +superseded by the user's rejection decision. The boundary repair and its finite +oracle coverage are `fixed-now`; no cleanup finding is deferred or left open. +The review body, both repeated prompts, selected-file metadata, and outside-diff +placement were reconciled. There is no associated inline finding. Loss total: +**one raw item = one fixed-now**. The original F02 hydration policy and F07 +application typing fixture remain separate open work. + +Reviewer assessment: hire for counterexample discovery, with low confidence +from one finding. The report supplies a useful public lifecycle cut and a +correct symptom. Its proposed cure lacks contract authority and misses the +Effect guarantee, so its fix guidance needs independent verification. + +Executed source identities (SHA-256): + +| File | SHA-256 | +| --- | --- | +| `packages/db/src/collection/changes.ts` | `f6b2e2fff7b782e1306838ae860e467f21fcaf9aaff5271486bec9e9a5aa4c5d` | +| `packages/db/src/collection/index.ts` | `29bf79f5b702a1bfea91040c86d08b439e99688c5bff10a5b9983f6bd652e11c` | +| `packages/db/src/query/effect.ts` | `0e7cd82b5213edf3666e8793f6bd71ffd1dd9166f7574cbf5c75920c74610f7b` | +| `packages/db/tests/collection-cleanup-restart-oracle.test.ts` | `a87b7b3fad0637a1d3cb7de831d3943304f3c8c4e84c11b558367e35be1f52c6` | + +## Eight-item review of 16824f677 — follow-up evaluation + +The user supplied eight deduplicated, unverified findings against PR #2229 at +`16824f6778bc3feb21c67d4f865c4f444e11f2e4`. The reviewer used medium effort and +reported no clear AGENTS.md violations. Those statements describe provenance, +not correctness evidence. This appendix records a self-review and executed +experiments. The earlier appendices remain historical records. + +### Laws, experiments, and inference + +The review exposed incomplete ownership handling around a deliberate rejection +boundary. It also exposed a separate resumption defect. The user's decision, +“yes reject during cleanup,” remains authoritative. Automatic admission and +restart would revise that contract. It would not establish failure atomicity +for other setup errors. + +| Law and authority | Existing coverage and precise gap | Experiment and repair | +| --- | --- | --- | +| Explicit demand can start a source independently of an abandoned render. Authority: subscriber/preload contract and the deferred-acquisition owner's opening law. | Ordinary admission released the render gate. The deferred-acquisition grammar lacked render-deferred source states and explicit subscription resumption. Stream preloads covered only an eager source without that gate. | R03 fails with zero source starts. The grammar now crosses seven source states, two query shapes, one/two levels and eleven histories. Both ordinary admission and resumption use the existing request-then-release ordering. Eight stream histories cross pending/success, eager/on-demand and idle/render-deferred. Omitting release only from resumption fails all four render-deferred stream cases; the four idle controls pass. | +| A failed subscription call transfers no listener ownership because it returns no release handle. Authority: observer subscribe/dispose contract. | The eligibility ledger covered publication and reentry after successful registration. It did not fail source admission during attachment or inspect leases before cleanup. | R07 leaves failed records and client/status/layout leases. Eight histories cross mode, cleanup/startup failure and one/two attempts. They inspect leases immediately, then explicit retry, publication and release. Two granular late-seed histories keep a healthy peer. Shared release removes only the failed record and detaches channels when the last owner leaves. A detach-only mutant fails all ten ownership histories. Synchronous setup errors still propagate. | +| Source cleanup makes a dependent pooled live query terminal and releases partial ownership. Authority: pooled/live-query lifecycle equivalence and glossary cleanup start. | The row grammar never releases a partition. The release owner covered reattachment and pending cleanup separately, but not their intersection or cleanup during startup before assignment. | R02 fails on a released partition reattaching during held cleanup. Twelve histories cross new/retained/released ownership, both observer modes and loading/ready cleanup. Two startup reentry histories check total logical ownership as well as public acquiring count. Observe cleanup before subscribing, release on failure, and unsubscribe a handle returned after synchronous retirement. | +| Cleanup callers share one operation until a replacement sync run actually starts. Authority: public cleanup promise and restart contract. | Admission validation also cleared cleanup ownership. A successful replacement was indistinguishable from a deferred request or a failed first-demand notification. | R05's successful subscriber legitimately starts a replacement. Its narrower defect appears when no replacement starts. Six outcomes compare promise identity, adapter entries and cleanup counts before settlement. Validation is now pure; entering loading transfers cleanup ownership to the actual replacement run. | +| Deferred logical demand starts no source work during cleanup. Authority: deferred-acquisition contract and the approved ordinary-admission rejection policy. | R06 was absent from the cleanup grammar: idle/cleaned-up cleanup-start callbacks could subscribe to a plain Collection, but a live-query Collection attempted local startup and threw. | Six histories cross plain/live-query and idle/ready/cleaned-up cleanup. Ordinary admission and resumption reject at the callback cut; logical demand stays deferred, then resumes explicitly after settlement. Local live-query startup now waits while cleanup is active. A universal rejection of deferred demand would violate this neighboring law. | +| Resumption cannot restore a retired subscription. Authority: subscription ownership and unsubscribe contract. | Releasing the render gate makes startup callbacks reachable during resumption. The old ownership increment followed those callbacks even when they unsubscribed. | S02 adds 28 histories from an independent token-count judgment: seven retirement/cleanup forms, both modes and both render-gate states. Start, acquisition count and the complete subscriber-count trace are checked at return and after settlement. Resumption commits its flag before notification and checks retirement on both sides. Removing the post-start guard fails 16 lifecycle and two pooled reentry assertions. | +| A deferred-source handoff preserves startup error identity and releases healthy peers. Authority: existing DbClient failure/peer contract. | After R03, independent demand may consume the render gate before the later handoff. The existing source-failure tests then exposed a lost error. | S01 preserves the current Collection sync error after the render release is already consumed. Existing exact-error, failed-stream replacement and healthy-peer witnesses pass. No automatic retry or new lifecycle state is introduced. | + +These repairs preserve cleanup rejection, terminal dependents, render-only +zero-work behavior, independent preload demand, publication eligibility and +explicit restart. The laws are encoded and reached at the listed observation +cuts within their finite domains. No known reachable counterexample remains +within these declared repair boundaries. This is not a proof over arbitrary +provider, callback or framework schedules. + +The receiving React driver adds deferred subscription resumption to the real +abandoned Suspense grammar. It reuses the core startup/cleanup judgment and +checks adapter entries, complete status traces, cleanup settlement and later +demand. Its 64 tests pass. A full-suite failure also exposed an older conflicting +expectation in `useLiveSuspenseQuery.test.tsx`: it expected no browser acquisition +while a server stream was pending. The established stream-preload law predicts +one local start, independently of stream availability. The corrected assertion +requires that start. The unchanged later assertions require the server row +until browser acquisition completes, then the browser row. This changes the +work observation to the accepted law without weakening snapshot authority. + +### Source-order item ledger + +Severity below is this evaluation's assessment. The supplied review gave no +severity except marking R06 as a design issue. Complete raw claims and the +append-only working ledger remain in the scratch root named below. + +| ID | Original claim and proposal | Technical judgment and evidence | PR action and durable value | +| --- | --- | --- | --- | +| R01 | Ordinary admission throws during GC cleanup while status remains ready; framework subscription can fail; no public promise/status exists to wait on. Proposed automatic admission/restart. | Rejection and retained status are approved. The no-promise subclaim is false: the same-path fake-timer GC probe calls cleanup(), obtains the exact pending operation, awaits it and subscribes successfully. A framework that does not handle the throw can still surface it. | `accepted-design`. Preserve the API-suitability concern and the GC timeline evidence. The public retry instruction remains valid. No automatic restart added. | +| R02 | registry.add precedes a throwing source subscription, retaining a nonterminal partition without a cleanup observer. | Confirmed, medium. Released-partition reattachment during pending cleanup exposes nonterminal status and partial ownership. Original expanded owner has four assertion failures on that boundary. | `fixed-now`. Earlier cleanup observation and complete partial release; release-owner grammar and startup reentry checks above. | +| R03 | resumeSubscriber checks admission but does not release an abandoned render gate, leaving a live query loading; changed fixtures hide it. | Confirmed, high. Both source modes have zero starts at the required demand cut. The correct React invocation fails all 16 added resumption cells before repair. | `fixed-now`. Shared subscriber start path and expanded acquisition/React grammars. Cancellation fixtures now create logical demand before explicit resumption. The DbClient fixture queues actual render-owned work directly, preserving its original cancellation assertion. | +| R04 | A startup throw consumes deferral, rejects other queued loads, and retains the first-demand flag while rolling back only counts. | These observations are real but belong to a failed sync run. The same-path probe reaches error, preserves the exact startup error for queued work, proves the old render release does not retry, and proves cleanup/new demand restores first-demand notification and readiness. Rolling back to an implicitly retryable idle run is not promised. | `accepted-design`. Preserve failure identity and explicit-restart witnesses. S01 enforces the neighboring handoff error law. This does not endorse partial ownership on failure, which R02/R07 repair. | +| R05 | Admission validation clears cleanupPromise, so concurrent cleanup can lose coalescing. Proposed a pure check. | Partly correct, medium. Successful ordinary admission starts a replacement and should receive a new operation. Deferred requests and failure before replacement startup must retain the old promise; those cases fail originally. | `fixed-now`. Pure assertion, transfer at actual loading, six distinguishing promise-identity histories. | +| R06 | Split admission differs between deferred plain/live-query Collections and requires cleanup-listener ordering. Proposed one shared method. | Narrower defect confirmed, medium: idle/cleaned-up cleanup-start reentry differs; ready cleanup does not show a blanket difference. Internal terminal dependents still need cleanup observation before source work. A common method cannot remove that ownership obligation. | `fixed-now`. Shared ordinary/resumed start logic and no local startup for deferred demand during cleanup. Six histories retain valid logical admission. No general policy/state-machine replacement. | +| R07 | Observer attach leaks client/status/layout listeners when source admission throws; should expose observer error status. | Leak confirmed, medium, including the logical listener record. A retry on the same observer originally attaches no source. Snapshot-error conversion is a separate unapproved policy. | `fixed-now`. Release failed registration and channels, preserve healthy peers and synchronous error reporting. Ten ownership histories plus existing publication campaigns. | +| R08 | An Effect created during cleanup returns disposed, never runs after restart, and gives no public reason. | Terminality is approved. The no-diagnostic subclaim is false: the same-path probe observes disposed and exactly one onSourceError with the cleanup cause, zero source ownership and no revival. The existing twelve joined-source receiving histories agree. | `accepted-design`. Keep explicit new Effect construction after settlement. No automatic revival. | +| R09 | Closing proposal: admitting then restarting would likely remove R01/R02/R07/R08; R03–R06 are separate. | This is a proposed contract revision, not a demonstrated cure. It conflicts with the user's rejection choice and does not prove setup-failure atomicity. | `accepted-design` for retaining rejection. Preserve automatic admission as an evaluated alternative, without implementation. | +| R10 | Metadata: eight deduplicated issues, medium effort, verified head 16824f67, no verification and no clear AGENTS violations. | Provenance retained. Matching HEAD was verified locally; effort and absence claims have no independent behavioral authority. | `duplicate` of the provenance recorded at this appendix's opening, with no product action or independent correctness credit. | +| S01 | Evaluation-discovered lost startup error after independent demand consumes the render gate. | Existing exact-error/healthy-peer tests fail on the first R03 repair. | `fixed-now`; deferred-source handoff now preserves that failure and peer release. | +| S02 | Evaluation-discovered resumption restores an owner retired during startup or misbalances notification reentry. | Original post-start ownership order fails pooled reentry; the calibrated guard mutant fails 18 assertions. | `fixed-now`; token-count grammar and two-sided retirement guards. | + +Loss accounting: **eight raw findings = five fixed-now + three accepted-design**. +The two additional raw prose/metadata items remain separately accounted for. +Including them gives ten raw items = five fixed-now + four accepted-design + +one duplicate provenance entry. +Two discovered siblings are fixed separately. False subclaims remain explicit +inside their source items. No item is deferred or silently merged. + +Reviewer recommendation: **hire for bug discovery, with reservations about fix +judgment**. Five of eight findings expose real defects, sometimes on a narrower +trace. R02/R03/R07 identify useful lifecycle cuts and R05 correctly questions +side effects in validation. The report does not execute them, overstates the +absence of public diagnostics in R01/R08, and treats an approved terminal policy +as a defect. Its automatic-restart proposal lacks contract authority and misses +failure atomicity. The ranking of R03 below the policy concern understates the +most direct progress failure. The reviewer is useful with independent contract +and experimental verification. + +### Executed evidence and limits + +Scratch root: `/tmp/tanstack-skill-reading-update/`. Logs retain assertion +messages and exact selected tests. No mutant remains in the working source. +Every mutation restores original bytes in `finally` and checks SHA-256 identity. + +| Experiment | Executed outcome | Log basename | +| --- | --- | --- | +| Original same-path probes | Seven assertion failures / seven passing controls; R01/R04/R08 reach the reported premises | review-eight-probes-red-2.log | +| Corrected original cleanup/observer/pool owner selection | 16 assertion failures / 125 passes: four cleanup, eight observer and four released-partition failures | review-eight-boundaries-red.log | +| Original real React receiving driver | 16 added resumption assertion failures | review-eight-react-red-proper.log | +| First repair, source handoff | Existing exact-error/peer tests expose S01 | review-eight-oracles-green-first.log | +| Startup cleanup during resumption | Two acquiring-count assertion failures expose S02 | review-eight-pool-reentry.log | +| Resumption retains render gate mutant | Four stream-preload assertion failures / four idle controls pass | review-eight-mutant-resume-keeps-render-gate.log | +| Observer detach-only mutant | All ten setup-ownership histories fail assertions, including healthy-peer preservation | review-eight-mutant-observer-detach-only-final.log | +| Retired-owner guard removed | 18 count assertion failures / 268 passes across complete lifecycle and pool files; no type or cell-audit error | review-eight-mutant-resume-retired-owner-final.log | +| Final five modified oracle owners | 741 tests pass; no type errors | review-eight-owners-final.log | +| Broad core/Effect/query surrounding run | 53 files, 2,584 tests pass; no type errors or unhandled errors | review-eight-core-final-3.log | +| Full React suite | 22 files, 457 tests pass; no type errors | review-eight-react-final-2.log | +| Scoped ESLint | Zero errors; pre-existing warnings remain | review-eight-lint-final-2.log, final command output | +| Production bundle vs merge-base 205ab7158 | db +102 gzip / +171 brotli bytes; react-db unchanged | review-eight-size.log | + +The early `review-eight-oracles-red.log` is not a clean calibration total. It +contains eight invalid virtual-field comparisons and eight progress timeouts +among 80 failures. The invalid comparisons were corrected to the declared row +projection. The clean boundary and React assertions above carry the RED claim. +`review-eight-react-red.log` selected no tests and supplies no evidence. +The first retired-owner mutant used a test-name selection that also triggered +the existing cell audit because other tests were skipped. Only its count +assertions were useful; the complete-file rerun supersedes that audit artifact. +Intermediate broad GREEN attempts passed runtime assertions but had test-fixture +type errors. They are not cited as clean GREEN runs. + +Production source weight for this follow-up is +71/-39 lines, net +32 across six +files. Shared admission startup, shared observer release and centralized pooled +status release remove duplicate control flow. The remaining added checks defend +observable partial-construction and reentry boundaries. Tests add dimensions +and assertions separately from production. Shipped growth above is +90 gzip and ++107 brotli bytes beyond reviewed head's earlier measured delta. No new stored +state, retry queue or recovery lifecycle was added. + +Replay the broad core run from `packages/db`: + +```sh +pnpm exec vitest run tests/collection tests/db-client.test.ts tests/effect \ + tests/live-query tests/query/pooled-live-query --coverage.enabled=false \ + --pool-options.threads.maxThreads=2 --pool-options.threads.minThreads=1 +``` + +From `packages/react-db`, run the same command without path selections. The +complete repository `pnpm test` was not run. These results do not establish +actual provider shutdown, every framework commit schedule, arbitrary recursive +callbacks or unbounded failure histories. Source/error and cleanup facts are +available at the asserted cut; no concrete missing-input hypothesis remains for +these repairs. The coverage map retains each owner's wider limits. + +| ORC requirement | Follow-up result | +| --- | --- | +| 001 authority and limits | Accepted rejection, demand, ownership, error and cleanup-promise laws are stated above and beside executable checks. | +| 002 independent judgment | Demand model, three-cut cleanup timeline, handle-count ownership and token-count retirement judgment do not import production semantic helpers. Private counts supplement public ownership observations; they do not compute expectations. | +| 003 literate responsibilities | Changed owners explain contract, independent judgment, finite grammar, real driver and immediate/settled comparisons. The shared grammar is named by core and React drivers. | +| 004 grammar controls | New cases are bounded enumerations, not important generated properties. Existing random campaigns are unchanged. Render deferral, explicit resume, held cleanup, released partition and failed attachment reconstruct the reports. Idle/no-retirement/successful replacement/healthy-peer neighbors distinguish their boundaries. No pending cleanup before adapter entry is claimed; callback cleanup at loading is synchronous. | +| 005 path and observations | Real Collection, observer, pooled view, Effect and React entry points run. Start counts, exact errors, handles, raw callback/count traces, rows, promise identity and resource leases reach immediate and settled assertions. Row projection omits unrelated virtual fields but retains full promised id/value fields. | +| 006 calibration | Original and mutant outcomes are classified above. Clean kills are assertion failures. Timeouts, invalid comparisons, missing selection and type errors supply no sensitivity credit. | +| 007 campaigns/replay | No important generated property added or altered. Existing observer and lifecycle fixed/random campaigns remain green with unchanged generators, recorder, budget and replay interface. Finite cases run directly through the listed test paths. | +| 008 minimal state | No new reference state. New source states project idle render deferral; explicit demand must erase its work distinction. The token model needs only retirement point and cleanup to predict counts and adapter entry. Distinguishing loading from ready is necessary because cleanup can prevent adapter entry only at loading. | +| 009 vocabulary | Canonical sync run, cleanup start, settlement, deferred acquisition, subscription and acquiring ownership remain distinct. The token-count judgment abstracts one logical owner's transfer and release, not production acquisition states. | +| 010 failure fidelity | New observer and cleanup checks use the existing primary/secondary cleanup aggregator. Pool and retirement fixtures resolve controlled gates and remove reentry callbacks before deterministic cleanup. No failure erasure or shrinking claim is made. | +| 011 second formulation | No concrete shared semantic fault requiring another formulation was identified. Terminal dependent policy is independently authorized by the user's decision and glossary, not inferred from the model. | +| 012 evidence | This versioned appendix records every applicable outcome and nonapplicable trigger, exact source identities, calibration and finite limits. This is self-review. | +| 013 boundary neighbors | Held cleanup versus post-settlement admission, logical versus acquiring demand, idle versus render-deferred source, failed validation versus actual replacement, retirement versus live owner, and failed late seed versus healthy peer all change asserted observations. | +| 014 receiving handoff | Real React abandoned Suspense resumption reuses the shared core judgment. The streaming React witness receives on-demand render deferral, starts acquisition before stream settlement and checks server/browser snapshot handoff. No actual remote-provider shutdown or other-framework schedule claim follows. | + +The eight-item implementation and bounded oracle repair are complete. Whole-PR +readiness remains separate: F02 still needs an initial-snapshot policy and F07 +still needs the reported failing application fixture. Earlier browser E2E runs +failed in dependency setup rather than demonstrating an assertion failure. +A new CI run does not erase those independent limits. + +Final evaluated source identities (SHA-256), based on reviewed HEAD +`16824f6778bc3feb21c67d4f865c4f444e11f2e4` plus this follow-up: + +| File | SHA-256 | +| --- | --- | +| `packages/db/mangle-cache.json` | `d16ffa42f01a034ee4786bc6b12e26b8b00d0e474c5ae94b8e2fa2e97883e27e` | +| `packages/db/src/collection/changes.ts` | `64e0b1d507dc9390a87c1db907bb178998f971910957ebe18a4d9565398f4433` | +| `packages/db/src/collection/lifecycle.ts` | `19025f597061032ad0dfd1fe394e6fa873af473d3b67698cfdb2e6ecb0d1ffef` | +| `packages/db/src/collection/subscription.ts` | `40d0d093671dcab5464614820de7799883577bf22a754922e29ba54f4fa3730d` | +| `packages/db/src/live-query-observer.ts` | `e212d8b1d10ed334c8268e7125a74f3de32ce7ca81c14627a3003e5c31a4b023` | +| `packages/db/src/live-query-options.ts` | `e6117272c8dfbc5d4c0636a2c3ff760a6db6b99e42e0edf8b10e36529e7a2dbf` | +| `packages/db/src/query/pooled-live-query.ts` | `2d247b2b14e9b8cc9a96e054d1b5dd4bb632b5a392086594c023263c8f98fc3e` | +| `packages/db/tests/collection-cleanup-restart-oracle.test.ts` | `65b3b32f54bc1ecaa61e62a2754142c7f5cf25ef1917df7ac14177342d094a20` | +| `packages/db/tests/collection-subscription-lifecycle-grammar-oracle.ts` | `e8144f0517fb21d55b66b3c0e63b2e628df6cc4ebda4edc8825adb384e8bf46b` | +| `packages/db/tests/collection-subscription-lifecycle-oracle.test.ts` | `f43e492ede9e000bf5c8a82687001d8b9a4c0b74c960a4022ef4708216b0dc9d` | +| `packages/db/tests/db-client.test.ts` | `e9aecb15f4bf1b29d07fb010ad6f79cc8eb379ba70b232d517f56c39b0836908` | +| `packages/db/tests/live-query-deferred-acquisition-oracle.test.ts` | `7df15face42f44bff73f7eab3e05a62c802650de9d671eedacfb1421bb9551b6` | +| `packages/db/tests/live-query-observer-history-oracle.property.test.ts` | `66a4803dd813967874f02db368595cca57cc4887208699247225ca25a27e938b` | +| `packages/db/tests/query/pooled-live-query-gc-oracle.test.ts` | `5eba86cb508775f885a9c13ec17253d04256f857002664bdeedfbce685426f89` | +| `packages/react-db/tests/descriptor-query-binding-oracle.test.tsx` | `101b8030b4d5e2ee4a090dca940c9708344fa2bdbd482ca94065969ef41478ab` | +| `packages/react-db/tests/useLiveSuspenseQuery.test.tsx` | `7d0ace536f2a4a8b6a5ccf124463ddf68cdb53ff390058a556fce3e6363bb136` | + +## Current-head summary: cleanup during ownership transfer + +CodeRabbit's summary at `da37bc9bbafbed4b2cc901adcbad9181b0aeb93d` reported no +actionable comments, but said: “The deferred-resumption cleanup edge case is +unresolved but has no established user impact.” It then paused automatic +reviews. This concern required an experiment without an inline finding. +The previous appendix's closeout is historical and does not cover this new +counterexample. The earlier eight-item ledger remains unchanged. + +### Law, experiment, and repair + +A returned acquiring subscription remains demand after cleanup. Explicit restart +must serve it. A still-deferred subscription contributes no acquisition. +Cleanup during admission retires that start. Admission must not start another +run unless a caller explicitly requested one. Authority comes from the primary +owner's surviving-subscriber contract, cleanup start, and shared startup judgment. + +Eight probes varied plain/live-query Collection, eager/on-demand source and +cleanup from loading/ready callbacks during resumption. Two on-demand live +queries remained loading after explicit restart with one acquiring subscriber. +Removing the target render gate exposed a neighbor: first-demand notification +could clean up the query, after which admission started it again. These are +concrete consequences absent from the summary. + +The old surviving-subscriber grammar cleaned up after admission settled. The +retirement grammar unsubscribed at the interruption. Neither represented a +surviving owner whose transfer was still in progress. The primary deferred- +acquisition owner now adds 192 finite histories: two modes, two shapes, one/two +levels, ordinary/resumed/deferred readers, render gate present/absent, source +loading/ready cleanup, and explicit restart later/from terminal publication. + +The independent judgment counts returned acquiring handles as next-run demand. +A deferred handle contributes none. Eager local rows can establish readiness +without acquisition. The driver reaches cleanup through real status callbacks. +It compares the complete target status trace after cleanup starts, ownership +and absence of implicit restart, then acquisition, readiness and rows after +explicit restart. Two-level histories permit an inner query's earlier accepted +acquisition to remain useful. They require acquisition to exist, without forcing +an unnecessary new physical attempt for each outer restart. + +The first draft incorrectly required that additional attempt. Its assertion +was corrected before production changed. The corrected original implementation +fails 44 histories at unintended-restart or replacement-progress assertions. +Keeping ownership retention while removing the sync-run guard fails 16 +unintended-restart assertions. Both runs have no type errors. Evidence: +`review-da37-surviving-red-original.log` and +`review-da37-surviving-red-no-generation-guard.log` in the existing scratch root. +The earlier draft total is not calibration evidence. Mutants restore source +bytes in finally and verify identity. + +The repair uses the existing sync-run generation to fence admission after a +first-demand callback cleans up. Current acquiring ownership also contributes +to the demand judgment. Ownership commitment restores a demand mark cleared +during startup cleanup, including an explicit cleanup-listener restart. +Retired owners do not commit that notification. Ordinary admission includes +its fallible ownership notification in rollback, preserving the setup error if +a release listener throws too. Rollback removes the pending owner's count, +without resetting a total that could erase a reentrant peer. No stored lifecycle +state, retry queue or recovery lifecycle was added. + +The same 53-file core selection passes **2,776 tests**. The full 22-file React +selection passes **457 tests**. Both report no type errors or unhandled errors. +Scoped ESLint has zero errors. The production bundle is **+138 gzip / +191 +brotli bytes** against merge-base `205ab7158`; React is unchanged. This adds +36 gzip / 20 brotli bytes beyond `da37bc9bb`. Additional source weight is +production +23/-6 and tests +112/-0, with documentation separate. Evidence logs: +`review-da37-core-green.log`, `review-da37-react-green.log`, +`review-da37-lint.log`, and `review-da37-size.log`. + +| Item | Technical judgment | Action and durable value | +| --- | --- | --- | +| CR3-01: unresolved resumption/cleanup edge, without established impact | Plausible concern. Same-path probes establish stalled replacement and unintended restart. Concrete diagnoses are S03/S04 below. | `fixed-now`; primary surviving-owner grammar and ownership-transfer repair. | +| CR3-02: docstring score 16.67%, below the bot's 80% threshold; generate docstrings | Reported default metric, with no named function or missing contract. Neither AGENTS.md nor ESLint imposes that threshold. The summary says no repository configuration applied. The score establishes no repository defect. | `refuted` as a required PR repair. Preserve the metric and documentation idea here. Existing contract prose remains required and was updated. No comments were generated only for the score. | +| CR3-03: no actionable comments, covered head da37, minimal risk, automatic pause | Exact covered commit verified. Risk classification is opinion and does not override F02 or the executed cleanup counterexample. | `duplicate` provenance retained here, without extra correctness authority. | +| S03: startup cleanup loses a surviving acquiring owner's demand | Confirmed on-demand progress failure after explicit restart. | `fixed-now`; current ownership and demand commitment survive the transfer. | +| S04: first-demand cleanup is followed by implicit startup | Confirmed ordering failure with an already-running local query. | `fixed-now`; existing sync-run generation fences admission. | + +New-summary accounting: three raw items = one fixed-now + one refuted repository- +requirement claim + one duplicate provenance entry. S03/S04 remain separate +violations. No finding is deferred. Reviewer recommendation for this summary +alone: insufficient evidence for a hire decision. It preserved a useful concern, +but gave no trace and missed the demonstrated consequence. + +ORC-001–014: authority, limits, independent handle judgment, literate layers, +real entry points and immediate/settled comparisons are stated above and beside +the grammar. New cases are bounded enumeration, so generated controls and new +campaign/replay interfaces are not triggered (004/007). Existing fixed/random +campaigns remain unchanged and green. No reference state was added (008). +Glossary terms and the existing projection remain unchanged (009). Controlled +teardown removes callbacks before cleanup, with the preceding failure-fidelity +limits (010). No concrete shared semantic fault calls for a second formulation +(011). Original and narrow-mutant assertion failures calibrate both ownership +retention and startup fencing (006/013). This versioned appendix and the source +identities below supply 012. These cases claim controlled core callbacks only, +so no additional real-provider/framework handoff is claimed (014). The React +run is compatibility evidence, not a receiving witness for this new premise. + +The law is encoded and asserted across the stated 192 histories, including +no-acquisition and explicit-listener-restart neighbors. No counterexample remains +within this declared boundary. Arbitrary recursive cleanup, asynchronous +provider work before acquisition and other framework schedules remain outside +these finite histories. The primary owner's coverage-map entry retains those +wider witnesses. No universal history or provider-shutdown proof is claimed. + +Source identities for this follow-up on `da37bc9bb` (SHA-256): + +| File | SHA-256 | +| --- | --- | +| `packages/db/src/collection/changes.ts` | `4d0849637a03e8012ee965191d9ed9332957e0bdf07b19d25d4deb0e400e9d34` | +| `packages/db/tests/live-query-deferred-acquisition-oracle.test.ts` | `0198add12e3c185aaa70a378bdfe115632686f864a178a42cbaa934809d387bd` | + + +## Final-head CI: subscription-specific replay classification + +Core shard 1 at `e852b0accf9971f61348066277290b010a7b016e` found CI-01 in +`replay.ordered-window-faults`: seed `-1751969525`, shrink path +`67:11:10:12:1:8:7:7`. The five-action reduction holds work, moves row 1 to +rank 1, truncates markers, then settles one request successfully and one with +failure. The query is ascending, offset zero, limit one, with an anti-join, +on-demand markers and an inline include. No peer participates. + +### Law and competing accounts + +A failed ordinary lazy demand is terminal. A failed truncate replay retains the +last complete publication until a later authoritative replay succeeds. Authority +is the architecture's demand, replay and publication contracts and this owner's +stated terminal-error law. Those contracts apply to each subscription's demand; +a shared source's replay does not classify every other subscription's load. + +The existing grammar already generated the case. Its source-wide +`markerReplaying` flag mistook a new include subscription's first request for +renewal of an older demand. The real adapter diagnostic reaches a new lexical +subscription without a replay hold. Its ordinary request fails and the query +correctly enters terminal error. The oracle rejected that allowed result. +The same exact seed fails with all six changed production files restored to +`16824f67`, so this classifier defect predates the eight-item repairs. + +An initial hypothesis blamed production's lazy-demand failure path. A guard +experiment did not repair the failure and was removed. It supplies no GREEN +credit. A trial that inspected a nonpublic Collection property could not +classify provider ownership and was also discarded. A test-name selection that +matched zero tests supplies no evidence. Final evidence below uses only adapter +inputs, recorded lease ownership, semantic predicates and public query status. + +### Model, observations and calibration + +Before truncate, the adapter records its held leases. A new acquisition renews +one captured lease only when its subscription token and independently evaluated +key predicate match. Each lease is consumed once. A new owner or different keys +remain ordinary demand even during another owner's replay. Separate source and +marker snapshots retain the provider boundary; a WeakSet records which adapter +received each options object. The five-key grammar makes predicate membership a +complete finite demand projection here. No production replay classifier or +private state computes the expected result. + +Terminal-error assertions now run after each history action, before final +recovery, as well as at convergence. The named CI reduction requires terminal +error. A settled joined-demand replay neighbor requires nonterminal recovery. +Existing fixed and random campaigns use the unchanged generators and 300-run +budget. The direct seed/path replay still runs without a normal campaign. + +| Experiment | Result | Log in existing scratch root | +| --- | --- | --- | +| Original final-head seed replay | One convergence assertion fails; 23 controls pass | final-db1-replay-current.log | +| Reviewed-head production replay | Same convergence assertion fails; 23 controls pass; all six files byte-restored | final-db1-replay-16824.log | +| Added intermediate cut and pinned reduction, old classifier | Two terminal-error assertions fail; 23 controls pass | final-db1-expanded-red.log | +| Subscription-specific classifier, direct replay | 26 tests pass, no type errors | final-db1-classifier-second.log | +| Owner-blind classifier mutant | New-include assertion fails, replay neighbor passes; no type or setup error | final-db1-mutant-owner-blind.log | +| Production mutant makes every subset error terminal | Replay-neighbor assertion fails, ordinary-failure neighbor passes; no type or setup error | final-db1-mutant-fatal-replay.log | +| Final surrounding selection | Three files, 962 tests pass; includes both normal campaigns; no type errors | final-db1-classifier-surrounding-final.log | + +All mutant source bytes were restored and verified. This correction changes no +production file. The original eight-item dispositions and S01–S04 remain intact. +CI-01 is `fixed-now` as an oracle-classification defect, with the generated +witness, distinguishing neighbors and calibration retained in the primary owner. +No product contract or approved cleanup policy changed. + +ORC-001–014: the law and finite projection are stated above and in the owner; +expected classification uses adapter-observed ownership and an independent +predicate evaluator (001–003/009). Existing generated domains, exclusions, +fixed/random parity and replay interface are unchanged; the CI reduction and +settled-demand neighbor supply reconstruction and ablation controls (004/007). +The driver invokes real Collections and checks status at each action and +convergence, with no missing-input hypothesis for this controlled boundary +(005/013). The two assertion failures calibrate owner identity and replay +terminality separately (006). Captured ownership and provider identity are +necessary to distinguish a new demand from renewal of an existing one (008). +Existing cleanup fidelity is unchanged; experimental mutations restore bytes +(010). No additional shared-fault hypothesis calls for a second formulation +(011). This versioned record and source identity supply 012. No provider or +framework handoff claim is made (014). + +Limits remain five numeric keys, one-level includes and controlled adapter +schedules. This is self-review. The coverage map names the expanded observation +cuts. F02's initial-snapshot decision and F07's application fixture remain +separate readiness obligations. + +Oracle source SHA-256 at this follow-up: `86f652fadc3ddeddfd5188d18b049969293209efdda51641e28ec38122884ea2`. + + +## Audit against the declared-reach rules from PR #2233 + +This self-review starts at `38eb2831f6005faa51f4021f0c29dd532c3a6cf4`. +It uses rule proposal `91ee6db4f1e37fffb44ca4432d01e27c63bed45c`, now merged +as `65d9ada508114f199464f753d50eb3447eb0eaf9`. The audit changes tests and +coverage evidence only. It preserves the maintainer's cleanup-rejection decision. +The scratch ledger and raw logs are in `/tmp/tanstack-rule-2233/`. + +### Law, experiment and inference + +**Law.** An ordinary lazy demand failure is terminal. A settled demand's failed +truncate renewal preserves the last complete publication and permits a later +authoritative replay. Authority is the live-query architecture's demand and +replay-participant rules, publication law 9, and this owner's terminal-error +contract. Subscription identity and immutable predicate membership distinguish +renewal from new demand; sharing a Collection does not establish renewal. + +**Reach.** The existing 300-history fixed campaign reached zero failures of new +demand during marker replay and one failed settled renewal. A calibrated random +campaign reached zero of either. Both fail when both states are required. +The old grammar can express the CI counterexample, but its normal budget does +not reliably enforce that interaction. More named examples would not fix this +missing sampling dimension. + +The primary replay owner now runs `replay.subscription-demand-origin`: 75 fixed +histories with seed `2233`, then 75 random histories with the same grammar and +judgment. It varies ordinary versus renewal origin, zero to three prior +successful replays, a marked row from 1/3/4/5, and its rank from 1 to 9. +The ordinary branch holds work, moves the row, truncates markers, succeeds one +held request, then fails the next. The renewal branch holds, truncates, and fails +the held renewal. Both use the existing production driver and independent +five-key predicate projection. Actual adapter entry, prior demand, public +readiness and a non-aborted failure establish reach, independently of the +expected terminal status. Named neighbors calibrate the recorder. + +The original broad grammar, seed, 300-run budget and replay coordinates remain. +Its seventeen existing required dimensions now count histories, once per state, +instead of events. Both new states require at least one history per campaign. +Explicit seed/path replay skips campaign minima. Public terminal status is +checked after actions and at convergence. A new positive assertion also requires +an observed ordinary failure to become terminal at convergence. + +**RED.** `replay-broad-reach-calibrated-red.log` records both broad campaigns +failing their added critical reach requirements on unchanged production. +This is a reach failure, not a product defect. The initial +`replay-reach-original.log` used an uncalibrated new-subscription witness that +missed the named regression. It supplies no reliable zero-reach claim and is +superseded. A new logical demand can use an already-seen subscription token. +The calibrated recorder checks demand renewal rather than token novelty alone. + +**GREEN.** `final-owners-green.log` passes 1,006 tests in eight files, with no +type errors. It includes the replay owner, reach helper, subscription lifecycle, +cleanup, deferred acquisition, observer construction/publication, partition +release and DbClient tests. Scoped ESLint passes. Production source is unchanged +in this audit. Earlier full core and React counts remain evidence at their +recorded heads, not new runs against an integrated tree. + +Each count below is the number of histories in that normal GREEN run. + +| Property / declared state | Fixed | Random | +| --- | ---: | ---: | +| `replay.ordered-window-faults` / `settlementThrows` | 12 | 23 | +| `replay.ordered-window-faults` / `throwsAfterReplay` | 50 | 53 | +| `replay.ordered-window-faults` / `replayStartThrows` | 56 | 64 | +| `replay.ordered-window-faults` / `heldAcrossTruncate` | 26 | 19 | +| `replay.ordered-window-faults` / `failedLoads` | 9 | 6 | +| `replay.ordered-window-faults` / `settledWindowChecks` | 246 | 238 | +| `replay.ordered-window-faults` / `tiedWindows` | 15 | 13 | +| `replay.ordered-window-faults` / `peerChecks` | 127 | 107 | +| `replay.ordered-window-faults` / `windowMoves` | 92 | 86 | +| `replay.ordered-window-faults` / `descendingRuns` | 139 | 149 | +| `replay.ordered-window-faults` / `joinedRuns` | 225 | 238 | +| `replay.ordered-window-faults` / `includeChecks` | 152 | 149 | +| `replay.ordered-window-faults` / `markChanges` | 87 | 79 | +| `replay.ordered-window-faults` / `antiJoinedRuns` | 76 | 87 | +| `replay.ordered-window-faults` / `innerJoinedRuns` | 81 | 74 | +| `replay.ordered-window-faults` / `markerLoads` | 132 | 120 | +| `replay.ordered-window-faults` / `markerTruncates` | 80 | 71 | +| `replay.subscription-demand-origin` / `new-demand-failed-during-marker-replay` | 9 | 10 | +| `replay.subscription-demand-origin` / `settled-marker-demand-replay-failed` | 32 | 38 | + +**Mutant.** The generated focused campaigns reject all four semantic controls +at assertions, with no setup, timeout or type failure: + +| Exact semantic edit | Distinguishing assertion | Evidence log | +| --- | --- | --- | +| Classify marker loads from the source-wide synchronous truncate flag | Illegal terminal error after ordinary demand is mislabeled renewal | `replay-mutant-source-wide.log` | +| Remove subscription identity from the captured-demand match, retaining predicates | Same illegal terminal classification | `replay-mutant-no-subscription.log` | +| Make `recordSubsetError` terminal regardless of `fatalBeforeReady` | Settled renewal must remain nonterminal | `replay-mutant-fatal-renewal.log` | +| Remove `failDemand`'s terminal transition | Ordinary failure must be terminal: zero observed versus one required | `replay-mutant-ignore-ordinary-failure.log` | + +The first control shrinks at seed `2233`, path `1:0`, to ordinary origin, zero +previous replays, row 1 and rank 1. Direct replay rejects that mutant and passes +after byte restoration (`replay-direct-mutant-red.log`, +`replay-direct-green.log`). The all-terminal control shrinks at the same seed, +path `2:1:1:1`, to renewal with zero previous replays. Removing the rank move +from the grammar leaves ordinary-demand reach at zero and fails both campaign +minima (`replay-mutant-no-rank-move.log`). All mutated files were restored in +`finally`, with byte identity verified. The new positive assertion was added +before its ignored-failure production mutant ran. + +**Limits.** Five numeric keys, one-level inline includes, one live query in the +focused grammar, ascending offset-zero limit-one windows and controlled adapter +settlement. The unchanged broad grammar covers other window/order/peer forms. +Neither property proves arbitrary predicates, actual provider cancellation, +unbounded replay histories or framework scheduling. The coverage map names the +same primary owner and these remaining receiving boundaries. + +### Audit of the seven shortcuts + +| Obligation | Result and remaining qualification | +| --- | --- | +| A01 production-first | No new production repair in this pass. Earlier entries retain their recorded finite-oracle REDs and original/narrow-mutant evidence. The new generated reach was added after those historical product commits. It cannot establish historical generated-oracle-first chronology. Under a literal requirement for generated RED before every historical repair, that chronology remains a deviation. A retrospective run cannot erase it. | +| A02 model authority | No reference semantics changed in this pass. Earlier rejection, explicit-restart and terminal-Effect rules follow the approved cleanup decision. The stream-preload expectation follows demand law 15, while server rows remain protected until local acquisition settles. Replay classification follows captured logical demand, not observed terminal output. F02's competing snapshot policies remain undecided. | +| A03 unmeasured reach | Confirmed gap in the normal replay campaigns, repaired with a focused process grammar, mandatory minima and reported history counts. Broad publication campaigns in other owners remain compatibility evidence only for the new construction cuts. | +| A04 quiet exclusions | No comparison was relaxed. Existing broad aborted-move and terminal-error reach exceptions remain explicit, with named witnesses. The new property requires the two terminality premises. The deferred owner's unchecked status/rows apply only while no live-query sync run exists, not after an owed ready result. Changed cancellation fixtures retain queued-load and release assertions. No new product exclusion or retired promise was introduced. | +| A05 pinned-only coverage | Replay terminality needed a focused generated interaction and now has one. The other review repairs use bounded enumerations: admission/status/mode, six promise outcomes, 28 retirement forms, 192 interrupted ownership transfers, eight observer construction forms plus two healthy-peer seed cases, twelve partition forms plus two startup cases, and twelve late-Effect receiving cases. Those are finite boundary matrices with original or hostile controls. They do not claim arbitrary generated construction histories. The deferred owner now calls its eleven command histories finite. | +| A06 older counterexamples | F02 remains a demonstrated warm-source SSR mismatch with a real hydrateRoot trace and a pending initial-snapshot decision. Its descriptor receiving owner and missing witnesses remain in the coverage map. F07 remains an unconfirmed Date typing report whose reliable strict compiler probe passes. It needs the failing application assignment and resolved dependency versions. Neither is dismissed because it predates this audit. | +| A07 no failing mutant | Earlier finite owners retain the exact assertion controls documented above. The new generated property rejects source-wide, identity-blind, always-terminal and ignored-ordinary-failure designs, plus grammar ablation. No setup-only failure receives calibration credit. | + +These results preserve the eight-item ledger: five fixed-now and three +accepted-design, with S01–S04 separately fixed in their declared finite bounds. +The audit adds a reach-enforcement repair, not another product-policy change. +Whole-PR readiness remains **not ready** because F02 needs a product decision. +F07 remains an evidence gap. Green finite evidence does not resolve either. + +### ORC-001–015 result for this repair + +Authority, limits and the unchanged independent projection satisfy 001/002. +Prose, grammar, driver and comparison remain beside executable code (003/009). +The shrunk ordinary and renewal histories reconstruct the known witnesses. +Removing rank movement loses the ordinary premise. Rank bounds avoid existing +starting ranks, and the constructive grammar places failure after holding work +rather than requesting failure without a pending request (004). Real Collection +entry points, action cuts and convergence supply 005/013. The four assertion +controls and reach ablation supply 006. Equal fixed/random budgets and the +checked direct replay supply 007. No model state or equivalence class changed +(008). Existing primary/secondary cleanup fidelity remains, and experimental +mutations restore exact bytes (010). No new shared semantic hypothesis calls +for a second formulation (011). This versioned record, starting revision and +source identities supply 012. Controlled-boundary claims require no new +provider handoff (014). Mandatory campaign minima and the table supply 015. + +SHA-256 identities for the repaired executable sources: + +| File | SHA-256 | +| --- | --- | +| `packages/db/tests/query/load-subset-replay-refinement-oracle.test.ts` | `34aec1038062a15957642c35de2fafe2576b2941a326f449c75b6a3b28cb9da1` | +| `packages/db/tests/oracle-config.ts` | `a3464344b04bfdc59bc4ab42489eeea540aed1461f9fb440e2da0f5b0a0b0c46` | +| `packages/db/tests/oracle-reach.ts` | `d38c86d0a2d197d566108cfb59cdd6cb624619df8ab570afa2c7e09b7f00ba63` | + + +### Integration with merged PR #2233 + +Oracle repair commit `a39a07d21` was followed by merge commit `e32ac735c`, which +includes `origin/main` at `65d9ada50`. The authoring skill conflict retains both +the oracle-first sequence and the branch's full-source reading requirement. +The review skill retains both sets of instructions as well. Upstream's persisted +readiness extraction merges with the observer ownership repair without a manual +production edit. No branch history was rewritten. + +The first combined core command passed 2,808 runtime tests but failed with two +test-source type errors. The filtered dimensions have a narrower union than +`keyof OrderedReach`; the loop had continued to iterate all keys. Iterating the +declared dimensions fixes those errors and removes the unused event aggregation. +This does not alter generated histories, public judgments or reach minima. +`integrated-core.log` is not a clean GREEN result. The final command in +`integrated-core-fixed.log` passes all 2,808 tests in 55 files, including type +checks, with no unhandled errors. The smaller earlier selection did not expose +these test-source diagnostics. + +The merged React suite passes 458 tests in 23 files. The persistence readiness +owner passes 64 runtime and type tests in two runner entries. Evidence: +`integrated-react.log` and `integrated-persistence.log`. Scoped ESLint, +Prettier, `git diff --check`, and the 418-name mangle-cache check pass. +The complete repository command remains unrun. + +The source-wide, identity-blind and always-terminal mutants again fail both +focused campaigns at the terminal-status assertion on the combined tree. +Removing rank movement again fails reach. Ignoring ordinary failure again fails +the positive terminality assertion. The `integrated-replay-*.log` files preserve +these runs. Every production and test mutation was restored before the final +checks. The direct seed/path replay passes all 26 selected tests +(`integrated-direct-green.log`). + +Final normal core-run history counts follow. Fixed seeds and budgets are +unchanged, and the minimum is one history for each declared state. + +| Property / declared state | Fixed | Random | +| --- | ---: | ---: | +| `replay.ordered-window-faults` / `settlementThrows` | 12 | 22 | +| `replay.ordered-window-faults` / `throwsAfterReplay` | 50 | 46 | +| `replay.ordered-window-faults` / `replayStartThrows` | 56 | 55 | +| `replay.ordered-window-faults` / `heldAcrossTruncate` | 26 | 14 | +| `replay.ordered-window-faults` / `failedLoads` | 9 | 12 | +| `replay.ordered-window-faults` / `settledWindowChecks` | 246 | 245 | +| `replay.ordered-window-faults` / `tiedWindows` | 15 | 13 | +| `replay.ordered-window-faults` / `peerChecks` | 127 | 120 | +| `replay.ordered-window-faults` / `windowMoves` | 92 | 88 | +| `replay.ordered-window-faults` / `descendingRuns` | 139 | 141 | +| `replay.ordered-window-faults` / `joinedRuns` | 225 | 230 | +| `replay.ordered-window-faults` / `includeChecks` | 152 | 131 | +| `replay.ordered-window-faults` / `markChanges` | 87 | 83 | +| `replay.ordered-window-faults` / `antiJoinedRuns` | 76 | 70 | +| `replay.ordered-window-faults` / `innerJoinedRuns` | 81 | 77 | +| `replay.ordered-window-faults` / `markerLoads` | 132 | 138 | +| `replay.ordered-window-faults` / `markerTruncates` | 80 | 80 | +| `replay.subscription-demand-origin` / `new-demand-failed-during-marker-replay` | 9 | 9 | +| `replay.subscription-demand-origin` / `settled-marker-demand-replay-failed` | 32 | 40 | + +Against merged main `65d9ada50`, `pnpm size:compare --packages db,react-db --files` +reports db +139 gzip / +238 brotli bytes and React unchanged. Tree-shaken app +scenarios range from +136 to +148 gzip bytes. This is the existing production +repair's cost on the integrated baseline. The declared-reach audit adds zero +production source lines or shipped bytes. `integrated-size-final.log` records +the final matching-source cached comparison. Earlier bundle figures remain tied +to their original bases. + +Final repaired replay-owner SHA-256: +`7b47e515ce94ec26f60fd78b400ddb2ea104abd9eb3bd8978a098c5efd7662e8`. +The six-part evidence block and the stated finite limits above still apply. +The historical generated-RED chronology deviation, F02 decision and F07 evidence +gap remain visible. Integration GREEN does not turn this into a ready verdict. +## High review of ten findings — 2026-10-23 + +The review checks `929c1d418a84c2d9ed57fb68aa829ba8c85c5ab7`, the head supplied +by GitHub when this evaluation began. The reviewer did not supply its own +reviewed SHA. Production and test repairs below follow that head. Earlier +entries, decisions, counterexamples, and evidence remain unchanged. + +The review exposed two distinct laws. A recorded Collection error does not +prove that a particular handoff threw. A failed subscription setup transfers no +resource ownership to a caller that received no release handle. Those laws +need separate error-provenance and partial-construction observations. + +### Laws, experiments, and inference + +| Law and authority | Previous coverage and missing distinction | Decisive experiment | Repair and enforcement boundary | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Ordinary React hooks expose a returned source error through `status` and `isError`. A handoff propagates an exception from the start it performs. The shared conformance contract and subscription ownership contract supply authority. H01/H06. | Healthy deferred starts and explicit preload rejection did not distinguish recorded error from this call's throw. React did not observe owners after failed commit. | Original helper fails generated error-origin/timing histories. Both real hooks enter the boundary on returned `markError`, then retain one Collection subscriber after unmount. | Default handoff reports only its actual throws. Explicit DbClient preload still requests source-error reporting. Core compares exact error identity, status, one start, and drained handoff at return. React compares status, `isError`, boundary capture, and zero owners immediately after unmount. | +| Failed construction releases all channels acquired before failure. Real cleanup callbacks may run during registration. H05/H07. | Pooled cleanup cases omitted a throwing late status callback. Observer setup cases omitted earlier capability failure and reentrant peer ownership. | Original pooled code retains one source status lease. Original observer retains status/layout leases. A narrow observer repair still leaks when a reentrant peer survives. | Own pooled status release before cleanup registration. Observer installs one release list before registration and detaches the failed physical attachment even with surviving logical records. Check immediately after every failed attempt, then explicit retry, publication, and final release. | +| Ordinary demand is independent of an abandoned render. Deferred acquisition alone starts no provider work. Law 15 and the shared startup judgment supply authority. H02. | Existing real React abandoned-render histories already distinguish ordinary, resumed, preload, and deferring readers, with both source modes and `startSync` values. | All real abandoned-render cases retain zero adapter starts before commit/direct demand. Ordinary demand subsequently starts once. Deferred demand retains zero. | Retain behavior. Change the comment from “Committed demand” to “Independent demand.” This rule does not promise that an arbitrary ordinary subscription is a render-only read. | +| First-demand history, current acquiring ownership, and cleanup-surviving logical demand are different facts. H03. | The retirement matrix omitted the retained first-demand mark. Ordinary admission lacked an observation during its acquiring-count callback, before publication registration. | Removing the current-count OR fails one ownership assertion. Removing the positive-count guard fails four reentrant retirement assertions. | Preserve the representations. Extend the existing token judgment and callback observation. An acquiring notification can unsubscribe synchronously and make the count zero even when `delta === 1`. | +| Retained logical demand can acquire after explicit replacement readiness. Cleanup invalidates old acquisition authority. The glossary supplies authority. H08. | A synchronous second first-demand notification was not generated at the ownership-transfer cut. | Real `notifyResumed` throws after transfer, including while held cleanup leaves public status ready. Retained demand still acquires after explicit replacement readiness. A mutant suppressing readiness-driven restart fails the acquisition-count assertion. | Add generated coverage. Do not add recovery machinery for an unproved permanent failure. The notification-error consequence remains an evidence gap beyond these histories. | + +H01's reference judgment is shared by the core and real React driver. It retains +callback outcome, handoff timing, and defined/undefined cause. It combines no +production queues or classifiers. The React driver supplies only host scheduling +and observations. Explicit preload has a distinct error-reporting obligation: +it must reject if its source startup failed even when graph demand consumed the +gate first. Existing exact-error, healthy-peer, stream and replacement cases +continue to check that obligation. + +H07 uses a controlled persisted-readiness capability that can fail registration +before returning a handle. First-party readiness registration does not currently +throw. The driver also uses real sync startup throws. This proves cleanup at the +core capability boundary, not a first-party persistence outage or every possible +provider failure. A failed registration itself transfers no unreturned handle. +A returned reentrant peer remains a logical record. A later explicit subscription +reattaches that record and receives subsequent publication. + +### Lossless source-order ledger + +| ID and original location | Claim and technical verdict | PR action / disposition | Durable value and destination | +| ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| H01 `live-query-options.ts:47`, correctness | Confirmed. The helper rethrows unrelated recorded errors, including `undefined`. The summary's error-boundary consequence is reproduced with real React. | `fixed-now`: distinguish readable status from an actual thrown start. Explicit preload retains source-error reporting. | Shared start-handoff judgment, deferred-acquisition owner, and both React receiving drivers. | +| H02 `collection/changes.ts:427`, correctness | Default admission releases the gate by design. The alleged ordinary-hook render work is not observed: real abandoned renders start no adapter work. A default subscriber is independent demand. | `accepted-design`: retain law 15 and clarify the comment. | Existing real React/core startup matrices remain. The separate test-integrity allegation is checked below. | +| H03 `collection/changes.ts:436`, simplification | Refuted. Sticky first demand, current ownership, and surviving logical demand differ. The count can become zero during a positive-count event. | `refuted`: do not remove either count check. | Added mark comparison and admission-reentry witness in the lifecycle owners. Both proposed removals fail assertions. | +| H04 `query/pooled-live-query.ts:194`, correctness | The catch terminates the partition. A legal recoverable source error that reaches that catch remains unproved. Eager `markError`/`markReady` before or after view construction recovers both shared views without throwing. | `confirmed-open`, evidence gap. No speculative partition recovery added. | Pooled lifecycle owner retains two finite same-run recovery cases. Needed: smallest legal eager-source sequence where `subscribeChanges` throws yet the same partition must recover. | +| H05 `query/pooled-live-query.ts:173`, correctness | Confirmed. A released partition can register during held cleanup; a retained view status callback throws before registration returns and leaves a status lease. | `fixed-now`: assign the status release before cleanup registration. | Generated late-cleanup property in pooled lifecycle owner, with returned/throwing and defined/undefined neighbors. | +| H06 `react-db/useLiveQuery.ts:1044`, `useLiveInfiniteQuery.ts:438`, correctness | Confirmed on the original helper for both hooks. A post-acquisition throw prevents the unsubscribe handle from reaching React. The footnote is correct: the hook pattern predates this PR, but the broad helper throw makes it reachable. | `fixed-now` through H01. Separate hook rollback guards were tried and removed after their removal survived repaired-helper histories. Both hook source files remain unchanged in this pass. | Keep separate error-surface and post-unmount ownership properties through the actual hooks. Do not dismiss the finding as outside the diff. | +| H07 `live-query-observer.ts:643`, correctness | Confirmed at the controlled capability and real sync boundaries. Partial registration and a surviving reentrant peer defeat the older release placement/record-count rule. | `fixed-now`: one early-owned release list and explicit failed-attachment detach. | Observer history owner generates failure boundary, mode, peer presence, and repeated failed attempts, then checks successful next use. | +| H08 `collection/subscription.ts:303`, correctness | A second first-demand callback can throw after ownership transfer. `subscribers:change` callback errors are reported asynchronously and cannot skip the next statement. Generated synchronous-throw histories retain demand and acquire after replacement readiness. Permanent stranding is not reproduced. | `confirmed-open`, evidence gap. No retry/finally state added without a legal failing trace. | Lifecycle owner now generates actual notification throws across held/immediate cleanup and 1–3 distinct demands. Needed: smallest source-ready sequence that skips restart and remains stuck after the supported readiness checkpoint. | +| H09 `collection/subscription.ts:304`, simplification | Confirmed. The outer unsubscribed guard duplicates `restartDetachedDemands`' first authority check. | `fixed-now`: remove the guard and cast. | Whole lifecycle, retirement and deferred-acquisition owners protect the inner check. | +| H10 `db/mangle-cache.json:419`, code weight | Confirmed reporting gap. The reported +139 gzip/+238 brotli package figures match the previous head against merged main. | `fixed-now`: perform and record actual refactor experiments and final shipped-byte measurement below. Growth remains explicit. | This versioned record and PR body retain accepted/rejected alternatives and their evidence. | + +The H02 provenance claim is separate from its product claim. Commit +`2151d9f1f` did change cancellation fixtures to create deferred logical demand +and then resume it. That change does not establish permission for ordinary React +render work. At this evaluation's parent, the DbClient queued-load fixture still +checks no adapter work before release, using an explicit queued load. The real +React startup matrix separately checks zero work for abandoned renders and +later acquisition for direct demand. The high review supplied no failing render +fixture. These assertions remain intact, and all actual render-cut suites pass. + +The ten panel entries and both summary bullets are accounted for above. H01's +`undefined` cause, H02's test-integrity subclaim, both H06 hooks, and H06's +outside-diff provenance remain distinct. No additional product sibling was +confirmed. Fixture setup and replay-registration mistakes found during this +pass are recorded below rather than counted as product defects. + +### Generated grammar and calibration + +All six properties run fixed seed `2229` and an unseeded campaign with equal +budgets. Each declares reach with a minimum of one history per named state. +The state counts below come from the final normal runs. Observer reach uses an +actual returned peer handle, checked against the generated premise. + +| Property / state | Fixed | Random | +| ------------------------------------------------------------------- | ----: | -----: | +| `live-query.start-handoff` / prior-error | 33 | 27 | +| same / marked-error | 29 | 29 | +| same / own-throw | 18 | 15 | +| same / undefined-cause | 43 | 57 | +| same / healthy | 35 | 40 | +| `pooled-live-query.late-cleanup` / return | 22 | 22 | +| same / throw | 18 | 18 | +| same / undefined-cause | 18 | 21 | +| `live-query-observer.partial-attach` / early-failure | 29 | 27 | +| same / sync-failure | 31 | 33 | +| same / surviving-peer | 26 | 32 | +| same / no-peer | 34 | 28 | +| `subscription-lifecycle.notification-error` / throwing-notification | 31 | 32 | +| same / retained-owner | 60 | 60 | +| same / held-cleanup | 30 | 36 | +| same / replacement-ready | 60 | 60 | +| `react.descriptor-error-surface` / ordinary | 16 | 16 | +| same / infinite | 14 | 14 | +| same / mark-error | 16 | 16 | +| same / throw | 14 | 14 | +| same / undefined-cause | 15 | 13 | +| `react.descriptor-ownership` / ordinary | 16 | 11 | +| same / infinite | 14 | 19 | +| same / mark-error | 16 | 21 | +| same / throw | 14 | 9 | +| same / undefined-cause | 15 | 12 | + +The core handoff budget is 100, pooled cleanup 40, observer attachment and +notification error 60, and each React observation 30. These are sampled finite +histories, not universal proofs. The domains retain the distinctions needed to +reconstruct the reports and the following neighboring cases: + +- Handoff: ready versus returned error versus throw, before versus during + release, and defined versus absent cause. Timing distinguishes another + reader's failure. Outcome distinguishes readable error from exception. + Calling an old render handle again must not start a second run. +- Pooled registration: returned versus thrown late callback, defined versus + absent cause, and 1–3 healthy peer status listeners. Removing release-before- + cleanup prevents the late-registration premise. On-demand sources are + excluded by production's pool eligibility, not a test waiver. +- Observer: granular/wholesale, early capability/real sync failure, one/two + failed attempts, and absent/present reentrant peer. Removing early failure + loses partial-registration reach. Removing peer presence accepts the narrow + record-count repair. Successful retry/publication/release follows every case. +- Notification: held/immediate cleanup, returned/thrown second notification, + and 1–3 distinct predicates. Cleanup is required to clear the first demand + mark and reach the second notification. Distinct predicates retain separate + demands. Replacement readiness is explicit because cleanup promises no + automatic restart. +- React: both hooks, returned error versus actual throw, and defined/absent + cause. A healthy source is covered by the surrounding host suite. Direct + QueryBuilder input to the infinite hook is outside its accepted input form. + +Original RED and hostile results are assertion failures at the claimed cuts: + +| Experiment | Captured failure | +| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Original H01 helper | Seed `2229`, path `0:1`: `mark-error/before-release/error` unexpectedly throws. A random original replay is `1735232938`, path `1`, with an earlier thrown start. | +| Original real React, ordinary hook | Error-surface seed `-1964136193`, path `2:0`: boundary capture replaces readable error. Ownership seed `-1619664311`, path `0:0`: one owner remains after unmount. Fixed seed `2229`, path `0:0` also fails both observations. | +| Original real React, infinite hook | Restricting the same grammar to the infinite hook reproduces both boundary and retained-owner assertions. The receiving input is `() => query`. | +| Original H05 placement | Seed `2229`, path `2:1:1`, `throws=true/cause=error/peers=1`: one status lease remains. | +| Original H07 attachment | Seed `2229`, path `0:0:0`, and random `-1896275468`, path `0:0:0:0`: early capability failure retains status/layout leases. | +| Narrow H07 repair without failed-attachment detach for a surviving peer | Seed `2229`, path `0:1`, and random `754580778`, path `0:0:0`: peer=true retains failed physical ownership. Both campaign assertions fail. | +| Restore H01's broad recorded-error throw | Both final core campaigns fail the handoff throw assertion. Random seed `1085601716`, path `0:1`. | +| Delete H05's early status ownership | Both final pooled campaigns fail the zero-status-lease assertion. Random seed `-395346131`, path `0:1`. | +| Suppress readiness-driven restart after transfer | Both notification campaigns fail the acquisition-count assertion. Random seed `-843337973`, path `0:0:1`, reaches a real thrown notification and observes zero loads instead of one. This calibrates retained-demand coverage, not an H08 production repair. | +| Delete H03 current-count OR | One assertion fails during acquiring-count cleanup before the new subscription joins publication registration. | +| Delete H03 positive-count conjunct | Four assertions fail in reentrant retirement histories. | + +Every production mutation was restored byte for byte before the final GREEN. +Removing the hook rollback guards survives both direct-source and exploratory +lazy-child histories with the repaired helper. That surviving mutant supplies +no evidence that rollback guards are necessary. The unproved lazy-child +extension is not part of the final claimed grammar. Original helper plus guards +passes both hooks' ownership checks, but the upstream provenance repair also +passes without adding those guards. + +Early H04 on-demand throw probes did not reach pool subscription: the pool +explicitly rejects on-demand sources. They are setup failures, not RED evidence. +An early infinite-hook fixture also supplied an unsupported direct QueryBuilder. +Its setup failures are withdrawn. The corrected real-hook original assertions +above supply the evidence. The first combined core run passed runtime tests but +reported two test-source builder type errors. The final run fixes the narrowing +without changing the grammar or expected results. A replay manifest test also +caught dynamically constructed React property IDs. Literal IDs repair the +registry check without changing behavior. + +### GREEN commands and direct replay + +From `packages/db`: + +```sh +pnpm exec vitest run tests/collection tests/db-client.test.ts tests/effect \ + tests/live-query tests/query/pooled-live-query \ + tests/query/load-subset-replay-refinement-oracle.test.ts \ + --coverage.enabled=false \ + --pool-options.threads.maxThreads=2 --pool-options.threads.minThreads=1 +``` + +This final run passes 2,816 runtime/type tests in 54 runner entries, with no type +or unhandled errors. The final observer reach check separately passes all 30 +owner tests. The complete replay-wrapper run passes 34 protocol tests, while +its three manifest tests initially fail on the dynamic IDs. After correction, +all three manifest tests pass. From `packages/react-db`, the full test command +passes 462 tests in 23 files. A final four-campaign run adds nonvacuous Collection +ownership and `isError` comparisons and passes. No full repository `pnpm test` +claim follows from these scoped runs. + +All six guarded replays run only the manifest's random lane and report one +completed exact-property witness. Use these seed/path pairs: + +| Property | Seed | Path | +| ------------------------------------------- | ----------: | --------- | +| `live-query.start-handoff` | 2229 | `0:1` | +| `pooled-live-query.late-cleanup` | 2229 | `2:1:1` | +| `live-query-observer.partial-attach` | -1896275468 | `0:0:0:0` | +| `subscription-lifecycle.notification-error` | -843337973 | `0:0:1` | +| `react.descriptor-error-surface` | -1964136193 | `2:0` | +| `react.descriptor-ownership` | -1619664311 | `0:0` | + +For example, from `packages/db`: + +```sh +TANSTACK_DB_ORACLE_SEED=2229 \ +TANSTACK_DB_ORACLE_PATH=0:1 \ +TANSTACK_DB_ORACLE_PROPERTY=live-query.start-handoff \ +node --import tsx tests/oracle-replay.ts \ + tests/live-query-deferred-acquisition-oracle.test.ts --coverage.enabled=false +``` + +For React, run from `packages/react-db` and use +`../db/tests/oracle-replay.ts` with the descriptor test file. None of the new +properties uses `fc.commands`, so no commands replay path applies. + +### Shipped weight and refactor experiments — H10 + +The target remains merged main `65d9ada508114f199464f753d50eb3447eb0eaf9`, verified +by a fresh fetch. `pnpm size:compare --packages db,react-db --files` reports: + +| Scenario | gzip delta | brotli delta | +| --------------------- | ---------: | -----------: | +| db package | +175 | +233 | +| React package | 0 | 0 | +| app | +175 | +294 | +| app-effect | +164 | +138 | +| app-group-by | +174 | +100 | +| app-includes | +170 | +66 | +| app-join | +173 | +170 | +| app-local-storage | +166 | +219 | +| app-optimistic-action | +171 | +248 | +| app-paced-mutations | +160 | +65 | + +Actual alternatives tried during this pass: + +1. Replace separate observer channel locals and release closures with one + early-owned list. Keep the existing callback runner so every acquired + channel gets a release attempt. This repair removes parallel ownership + mechanics and supports early registration failures. +2. Remove the duplicate outer unsubscribed guard. Existing inner authority + checks preserve behavior through the full lifecycle suite. +3. Remove current-demand count from `hasSubscriberOrPreload`. Reject this + candidate: it fails one admission-reentry ownership assertion. +4. Remove the positive-count conjunct from positive ownership notification. + Reject this candidate: four reentrant retirement assertions fail. +5. Add then remove rollback guards in both React hooks. Keep their removal: + repaired-helper ownership histories pass without them. Measured React cost + falls from +22 gzip/+26 brotli to zero. The typical app falls from +186 gzip + to +175. Brotli changes are nonlinear and not a sum of source-file deltas. + +The db package remains 36 gzip bytes larger and 5 brotli bytes smaller than +this review's parent comparison. Its remaining growth pays for error-provenance +separation, early resource ownership, and the prior demand/admission repairs. +The rejected count simplifications violate concrete ownership laws. No extra +React rollback or replacement lifecycle remains. The new production diff is +net +7 source lines, reported only as context. Shipped bytes above are the +weight measure. The test/model/replay changes add roughly 840 lines separately +from production and contract documentation. + +### ORC-001–015 assessment and limits + +001/002: The conformance error surface, handle ownership, glossary cleanup +rules and architecture law 15 supply authority. Small error-origin and +resource-token judgments stay independent of production classifiers. +003/009: Each changed owner states its law and limits before mechanics. +The shared handoff judgment names its projection. Vocabulary follows the +glossary. 004: The grammar controls above name reconstruction, axis ablation, +finite ranges and nearby exclusions. 005/013: Real Collection/observer/pool +entry points and real React commit/unmount paths reach the stated cuts. +Returned versus thrown starts and cleanup callbacks are distinguishing +neighbors. No concrete missing source fact is identified for these cuts. +006: Original and narrow-mutant assertions above reject the wrong designs. +H04/H08 gaps receive no closure credit. 007: Equal fixed/random budgets and six +guarded direct replays pass. 008: The handoff projection retains timing and +outcome because each changes allowed throw behavior. The retirement model's +new mark observation distinguishes retired ownership after cleanup. No new +stateful reference machine was introduced. 010: Driver cleanup is deterministic +within these fault domains. Experimental mutations restore exact bytes. +Cleanup-throwing capabilities are outside this grammar and are not claimed. +011: No new shared semantic-fault hypothesis needs a second formulation. +012: This versioned record preserves verdict-critical evidence and source +identities. 014: React receives the shared handoff judgment through actual host +scheduling. Controlled capability failure remains a core-only claim. +015: Mandatory minima and every declared state count appear above. + +The encoded laws are enforced at their stated return, failed-construction, +retry-publication and host-unmount cuts for these finite grammars. They do not +establish arbitrary provider outages, every callback program, every React +schedule, or other frameworks' error policies. Ordinary hook/error ownership +and explicit preload rejection are separate observations. The pooled and +observer owners retain their broader existing lifecycle histories. + +H04 remains open until a legal eager-source subscription throw with promised +same-run recovery is supplied. H08 remains open until a retained demand is +shown stuck past supported replacement readiness. Both questions were sent +with the tested path and observed results. Neither is silently deferred or +refuted by a different-path green test. The coverage map names those owners +and required witnesses. + +### Reviewer assessment, loss audit, and readiness + +**Hire recommendation: hire for code review.** The review found four reproduced +behavioral findings, including two subtle partial-construction leaks, plus a +valid simplification and the missing size rationale. Its strongest contribution +was tracing a new throw into unchanged receiving code. Its recovery and render +claims need better legal-path evidence. One simplification contradicts reentrant +ownership, and the review did not supply executable fixtures for its two +remaining failure consequences. Those limits reduce confidence in autonomous +design recommendations, without erasing the useful discoveries. + +Ten raw items = six `fixed-now` (H01/H05/H06/H07/H09/H10), one +`accepted-design` (H02), one `refuted` (H03), and two `confirmed-open` evidence +gaps (H04/H08). No items are deferred or merged away as duplicates. Accounting +is complete. Confirmed authorized repairs and their declared oracle work are +complete in the bounded domains above. The two unproved consequences remain +open investigations, rather than confirmed unfixed defects. + +Whole-PR verdict remains **not ready** because the earlier confirmed F02 warm- +source hydration mismatch still needs its initial-snapshot policy. H04/H08, +the earlier F07 typing report, and final-head CI also limit readiness evidence. +The earlier 24 passing checks at `929c1d418` do not validate this new tree. +Browser dependency installation timed out at that head and on merged main; +that infrastructure failure did not execute application assertions. CodeRabbit +remains paused and last reviewed `da37bc9bb`. No bot command or review comment +was sent during this evaluation. + +### Repaired source identities + +The implementation/test revision is identified by its parent above and these +SHA-256 values. The subsequent closeout entry pins the resulting commit. + +| File | SHA-256 | +| --- | --- | +| `packages/db/src/client.ts` | `72fb0652c06885430bdb0554277de1c2e2c25b6cfa52105fb4e120feefd730b1` | +| `packages/db/src/collection/changes.ts` | `0c07c750b94a97773bd707975a7ec7a41959ca7271112c4c0efeb79763a52b70` | +| `packages/db/src/collection/subscription.ts` | `e20dee08fe0a9c952f4b0b0e20cc1d95890559e5e980665926afd3f23148a900` | +| `packages/db/src/live-query-observer.ts` | `ddc6f6c2a13df4b919222ba7618626c36209079b7c3e1252f9fd0275211f576a` | +| `packages/db/src/live-query-options.ts` | `49d617f85b1a5cc09c4815130db703baa45fb7b5915b5a0973b77c662dbcf26b` | +| `packages/db/src/query/pooled-live-query.ts` | `01c253b1b781d78f570d3dbb7048cf90f5bc05a4292ee3ee623a9661463204d3` | +| `packages/db/tests/collection-subscription-lifecycle-grammar-oracle.ts` | `b6e5752c5f78312971667dd9c0e6ea7af7fd5d9a9db18b29a20be1b68c75325a` | +| `packages/db/tests/collection-subscription-lifecycle-oracle.test.ts` | `a344eecab6e4b1220f97249ae7de0feb905bf23af07cb6ca5926c1e387373a63` | +| `packages/db/tests/live-query-deferred-acquisition-oracle.test.ts` | `818998ac2d4049475d95d08d75b2a8c3321ec10ee3c073c5778017308d8147cd` | +| `packages/db/tests/live-query-observer-history-oracle.property.test.ts` | `6e1caf13a0eec60b530a1d31708b1e9c0ec5e636bd97d211d004f1f24c7678eb` | +| `packages/db/tests/oracle-config.ts` | `c1e6b14b96491d266571d65913f0bfb3acc98cdc3b9544a95317ae1b0b18970b` | +| `packages/db/tests/oracle-replay-manifest.ts` | `eef645f84c09044ac06bbd369b237b427a3cee1ab5bcb198020abc4d3724ba9b` | +| `packages/db/tests/query/pooled-live-query-gc-oracle.test.ts` | `029a28a9210c7c8981994f83044a454b6934c3cd61e29ae3e5c079c167ef6507` | +| `packages/react-db/tests/descriptor-query-binding-oracle.test.tsx` | `979091a554ac15c6f24f8b1d81112473c1e1b9db048e25a3031095e3a36cb4b5` | + +### Ten-finding implementation closeout + +Implementation and oracle commit: `83bd08ded8b74254515536233d7d82a32a85d3c3`. The source +identities above match this commit after the commit hook. The following +closeout commit changes documentation only. The earlier REDs target parent +`929c1d418a84c2d9ed57fb68aa829ba8c85c5ab7`; the final GREEN commands and six +guarded replays target the implementation identified here. + +Scoped ESLint passes with zero errors and four warnings on unchanged lines. +Prettier, `git diff --check`, and the 418-name mangle-cache check pass. +The historical audit prefix remains byte-identical. The changeset now records +readable source errors and partial-attachment release. No published commit +was amended or rebased. The production changes preserve the approved cleanup +rejection policy. The loss totals and non-ready verdict above still apply. + +### Current receiving-gate enforcement — 2026-10-23 + +This pass starts at `1d1ac0170d5520ff21b54aee90c006382841150d` and changes no +production source. It strengthens the current receiving comparisons before +considering any further repair. It does not repair historical work order. + +**Law and authority.** The shared conformance contract exposes recorded source +errors as readable React status. The shared `expectedStartHandoff` judgment +permits an exception only from startup performed by that handoff. Both hooks +must release every subscriber after unmount, before GC can conceal ownership. +Explicit preload failure reporting and abandoned-render zero-start cuts remain +neighboring guarantees in their existing owners. + +The pre-edit inventory names both public hooks, failure before/during commit, +returned `markError`/thrown startup, defined/absent causes, error/boundary after +commit, and ownership immediately after unmount. Each hook now has independent +fixed/random campaigns and reach minima. Independent DbClient startup supplies +the earlier-failure premise: its throw is observed before render, the source +already reports error, and commit must not start it again. No model rule or +production expectation changes. + +**Reach.** Each row contains 30 histories. Columns count returned errors before +and during commit, thrown starts before and during commit, and absent causes. +Every minimum is one; counts are per history, not callback count. + +| Hook / observation / campaign | Returned before | Returned during | Thrown before | Thrown during | Absent cause | +| --- | ---: | ---: | ---: | ---: | ---: | +| ordinary / error-surface / fixed | 7 | 9 | 8 | 6 | 14 | +| ordinary / error-surface / random | 10 | 10 | 6 | 4 | 15 | +| ordinary / ownership / fixed | 7 | 9 | 8 | 6 | 14 | +| ordinary / ownership / random | 5 | 6 | 8 | 11 | 15 | +| infinite / error-surface / fixed | 7 | 9 | 8 | 6 | 14 | +| infinite / error-surface / random | 5 | 4 | 9 | 12 | 19 | +| infinite / ownership / fixed | 7 | 9 | 8 | 6 | 14 | +| infinite / ownership / random | 7 | 6 | 8 | 9 | 14 | + +**RED and mutant.** Temporarily removing `reportSourceErrors &&` from the helper +restores the reviewed defect. After rebuilding db, all eight receiving campaigns +fail at the intended comparisons. Error-surface campaigns observe one boundary +error instead of a readable result. Ownership campaigns observe one retained +subscriber after unmount instead of zero. These are assertion failures, not +setup errors. The mutation is restored byte for byte and db rebuilt. +This calibration tests current enforcement; it is not a new pre-fix RED claim. + +Four new guarded replays preserve the changed grammar. The property IDs remain +`react.descriptor-error-surface` and `react.descriptor-ownership`; the additional +`-t 'ordinary '` or `-t 'infinite '` selects the receiving hook independently. +The runner's manifest still selects the random lane. All four exact witnesses +execute and pass after restoration: + +| Hook / observation | Seed | Path | +| --- | ---: | --- | +| ordinary / error-surface | -2062994310 | `0` | +| infinite / error-surface | -1755367813 | `4` | +| ordinary / ownership | -44638460 | `5:0` | +| infinite / ownership | -938612395 | `3` | + +**GREEN.** The full React package passes 466 tests in 23 files, including type +checks. The deferred-acquisition owner and DbClient pass 580 tests in two files. +The four guarded replays pass. Scoped ESLint and Prettier pass. All three edited +skills validate with `quick_validate.py`, using cached PyYAML. A broader replay- +protocol run was interrupted; its partial output is not a clean GREEN claim. +The protocol itself is unchanged, and its previous results remain historical. + +The first surrounding React run exposed a real-clock test gap: a nominal +10-millisecond wait overshot the abandoned-render GC cut under load. The two +infinite-query GC tests now advance a controlled clock by exactly 10 milliseconds, +check the same live-state expectation, then run pending timers and check cleanup. +All 12 tests in that file pass separately and in the full package. No status +comparison was relaxed and no product behavior changed. + +**Grammar and limits.** Both hooks independently sample the same bounded +2 × 2 × 2 failure grammar. Removing timing loses the unrelated-error case; +removing either hook loses its host path; absent causes distinguish a missing +error value from absence of failure. The grammar reconstructs the original +returned-error/during-commit witness. The driver excludes a claimed pre-commit +failure without prior startup by checking source error and one start before +render. Healthy startup retains its core-owner coverage. Source startup is +synchronous and eager. This does not prove +arbitrary React schedules, asynchronous outages, error recovery, Suspense error +policy, or another framework's receiving boundary. + +For ORC-001–015, authority/model independence and canonical terms remain +unchanged (001/002/009). Prose, generated timing, real receiving drivers and +commit/unmount assertions are colocated (003–005). The eight intended assertion +failures calibrate the checker (006); paired campaigns and exact guarded replays +satisfy 007. No reference-model state changes (008). Cleanup exceptions remain +outside this grammar (010), and no new shared semantic-fault hypothesis requires +a second formulation (011). This versioned entry records the evidence (012). +Before/during timing distinguishes the exception boundary (013), both real hooks +receive the startup premise (014), and each path has mandatory reach (015). +The coverage map identifies the same owner and finite limits. + +The oracle-authoring, oracle-review and personal evaluate-review instructions +now require the receiving-path inventory and comparisons before production edits. +Missing required witnesses and setup failures block that gate. Aggregate reach +cannot substitute for each receiving path. Retrospective verification and a +recorded deviation cannot satisfy the gate. + +The React owner SHA-256 is +`e9bff514ae6bf023ec5d310a7848fb6b817132dad6fb9cc2dc1848d5d3ecd550`. +Production remains pinned to the starting head. All 26 CI checks passed on that +head; later-head CI must be assessed separately. F02's initial-snapshot decision +still prevents a ready verdict. H04/H08 and F07 retain their evidence gaps. + +## SSR preparation admission — 2026-10-23 + +This entry implements the maintainer's decision: fail on programming errors that +expose incidental server-only rows or readiness. An ordinary cold/loading render +remains legal. The SSR guide already permits normalized source rows and prepared +query results. Normalized rows carry no query readiness. A completed query result +can carry readiness. Runtime provider failures retain their existing error surface. +This is a product decision, rather than authority inferred from production output. + +**Pre-edit gate.** Production was unchanged at `90a30827fd32c5444243206f136a89e738ee6099` +while the shared SSR judgment and receiving drivers were added. The inventory named +React ordinary and infinite reads, Svelte server reads, normal browser warm reads, +normalized hydration, and cold/warm Suspense discovery. Named replays ran all seven +preparation boundaries before either generated campaign could stop at its first +mismatch. The React drivers use real `renderToString`, `hydrateRoot`, commit, and +unmount. Svelte uses real server compilation and its native browser effect root. +A new real `renderToPipeableStream` witness checks Suspense discovery. + +**RED.** In `packages/react-db`: + +```sh +pnpm exec vitest run tests/descriptor-query-binding-oracle.test.tsx \ + -t 'SSR snapshot admission|SSR Suspense discovery' --coverage.enabled=false \ + --pool-options.threads.maxThreads=2 --pool-options.threads.minThreads=1 +``` + +In `packages/svelte-db`: + +```sh +pnpm exec vitest run tests/ssr-snapshot-admission-oracle.svelte.test.ts \ + tests/hydration.svelte.test.ts --coverage.enabled=false \ + --pool-options.threads.maxThreads=2 --pool-options.threads.minThreads=1 +``` + +All three receiving paths accepted unsupported ready-empty snapshots and incidental +rows. Pending query registration did not transport partial rows. Normalized ready +sources differed at the first browser status observation. React infinite reads also +ignored a completed query result. Cold/loading, ordinary completed query transport, +normalized partial-loading rows, and Svelte browser initialization supplied passing +neighbor controls. The first streaming control had a missing test import. Correcting +that setup before production edits produced a passing cold discovery control and an +assertion RED for the warm query's absent transport entry. The setup error receives +no defect or calibration credit. + +**Repair.** The shared observer checks server reads against completed query results +or explicit/hydrated normalized sources. Unsupported server reads throw the coded +preparation error. Normalized source snapshots report loading on both hosts until +browser acquisition proves readiness. A Collection record remembers hydration, +including an empty chunk. It preserves the existing dehydration selection policy. +The infinite controller consumes a matching result through the same observer and +uses its own server snapshot. Streaming Suspense preloads before its server snapshot, +including when a peer warmed the source. Svelte distinguishes server reads from +ordinary browser initialization. + +**Model and grammar.** The pure judgment reads caller preparation, authored source +state, and zero to three authored rows. It imports no production classifier. The +source-state abstraction combines synchronous eager startup and readiness. Query +preparation distinguishes completed and pending preload. Removing that distinction +admits partial untransported data. Removing rows misses partial-loading data. +Removing readiness misses ready-empty markup. Removing source preparation rejects +valid normalized hydration. Idle plus nonempty rows, a completed preload with an +unready source, and a pending preload over a synchronously ready source are outside +this grammar. Named examples reconstruct F02 and every admission boundary. Campaign +examples preserve boundary reach even when random sampling omits one. Fixed and +random campaigns execute the same property and budget. + +**Self-review.** ORC-001/002: maintainer decision and SSR guide authorize the judgment, +which stays independent. ORC-003/005/009: literate model and directly named receiving +drivers keep the grammar, paths, observations, and canonical terms visible. ORC-004: +the reconstruction, ablation, bounded range and invalid neighbors are stated above. +ORC-008 does not apply to this stateless judgment. ORC-010: cleanup does not throw in +the controlled grammar, including the calibrated production mutants. ORC-011: +server/browser markup equality independently challenges the preparation judgment's +claim that accepted state is reproducible. ORC-013: partial-loading rows and +ready-empty status distinguish row-only and readiness-only checks. ORC-014: both +React hooks and Svelte each receive their premise through their real host paths. +ORC-006/007/012/015 results, counts, replays and artifact identities follow below. + +**Limits.** This closes F02 for the bounded preparation histories and named receiving +cuts. It is not a proof of arbitrary providers or schedules. The coverage map keeps +asynchronous providers, persistence readiness, pending-stream page-shape changes, +mixed plain/Suspense streaming, and real Start/Electric execution with their owners. +Custom filters that deliberately discard needed state and rendering local Collection +identity are outside portable hydration. H04/H08 still need legal failing traces. +F07 still needs the failing application type assignment and dependency versions. + +**GREEN and reach.** React passes 486 tests in 23 files. After the final reach +classifier correction, its SSR owner passes all 20 selected tests again. Svelte +passes all 146 tests in 10 files, including type checks, with no unhandled errors. +Core observer, snapshot, controller, DbClient, hydration-authority, preload-identity, +and production-error owners pass 274 tests in eight files. The unchanged replay +protocol and manifest suite also passed in the preceding 311-test core run. +The final scoped ESLint and Prettier checks pass. Generated error documentation +matches its fixture. The private-member cache validates all 420 names. The consumer +minified check passes 107 error names, index metadata, query rows and live updates. + +Every campaign executes 120 property samples at fixed seed 2229 or without a seed. +The seven boundary examples are samples in each campaign. Counts below include +those examples. `normalized-ready` now requires an actually ready source, rather +than counting idle normalized sources. No observation expectation changed. + +| Receiving path / campaign | Cold/loading | Unbacked ready-empty | Unbacked rows | Query result | Normalized ready | Normalized partial | Pending query | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | +| ordinary / fixed | 23 | 4 | 38 | 23 | 18 | 14 | 16 | +| ordinary / random | 26 | 2 | 38 | 19 | 18 | 17 | 19 | +| infinite / fixed | 23 | 4 | 38 | 23 | 18 | 14 | 16 | +| infinite / random | 17 | 5 | 45 | 21 | 19 | 13 | 22 | +| svelte / fixed | 23 | 4 | 38 | 23 | 18 | 14 | 16 | +| svelte / random | 18 | 12 | 44 | 14 | 16 | 16 | 18 | + +**Calibration.** These are assertion failures at the stated public comparisons: + +| Hostile change | Distinguishing comparison | +| --- | --- | +| Check only nonempty rows | React ordinary, React infinite and Svelte all accept an unsupported ready-empty snapshot, failing the rejection assertion. | +| Bypass admission | React accepts incidental rows/readiness and fails 10 admission assertions. | +| Preserve live readiness for normalized SSR | Both React paths fail server/first-browser markup equality, six assertions. | +| Omit the infinite controller's client binding | Infinite admission and prepared-result handoff fail seven assertions. | +| Skip streaming Suspense preload | The warm real-stream result is absent from transport. The cold discovery control still passes. | +| Apply the server getter to Svelte browser initialization | Both warm-browser controls fail the explicit no-initialization-error assertion. Normalized controls still pass. | + +An initial constant-false bypass failed TypeScript compilation. It receives no +calibration credit. The corrected bypass retained type-valid control flow and +failed the intended assertions. The first browser-guard mutant threw directly +at the intended public call. A later run captures that call and fails the explicit +no-error comparison instead. All temporary source mutations were restored, db was +rebuilt, and the same receiving checks passed. + +Guarded replays each execute one selected receiving campaign and pass on restored +production. React ordinary uses seed `451253809`, path `1`. React infinite uses +seed `-348951204`, path `1`. Svelte uses seed `2229`, path `1`, captured by its own +ready-empty hostile run. The old Svelte path `0:0` no longer selects a sample after +adding campaign examples. The replay guard correctly rejected its zero-execution +run. Only the replacement replay receives evidence credit. + +From the relevant framework package, the guarded command is: + +```sh +TANSTACK_DB_ORACLE_PROPERTY= TANSTACK_DB_ORACLE_SEED= \ +TANSTACK_DB_ORACLE_PATH= node --import tsx ../db/tests/oracle-replay.ts \ + tests/ --coverage.enabled=false \ + --pool-options.threads.maxThreads=2 --pool-options.threads.minThreads=1 +``` + +Property IDs are `react.ssr-ordinary`, `react.ssr-infinite`, and +`svelte.ssr-snapshot`. The React owner is `descriptor-query-binding-oracle.test.tsx`. +The Svelte owner is `ssr-snapshot-admission-oracle.svelte.test.ts`. + +**Production weight.** `pnpm size:compare --packages db,react-db --files` compares +the full branch with merged main `65d9ada50`. db grows 372 gzip / 345 brotli bytes. +React grows 103 gzip / 106 brotli bytes. The typical app grows 294 gzip / 346 brotli +bytes. Other app scenarios grow 272–297 gzip bytes. These totals include the earlier +lifecycle repair, rather than measuring only this follow-up. + +The repair reuses one observer snapshot builder and one window pagination builder. +It combines the persisted-readiness condition instead of adding another publication +path. Private-member minification includes both new private names. Narrower +alternatives were tried as the row-only, source-readiness, and omitted-window-binding +mutants above. Their intended assertion failures rule out those byte reductions. +A separate hydration fact preserves existing dehydration selection and represents +empty hydration chunks. Source rows and query readiness require distinct evidence. +The remaining shipped growth pays for admission, that provenance, the matching +infinite observer, and host selection. It adds no retry or recovery lifecycle. +Tests, oracle metadata and contract documentation grow separately from production. + +This entry is tied to the following artifact hashes and the commit that contains it. +The starting production head is recorded above. Hashes cover final oracle artifacts: + +| Artifact | SHA-256 | +| --- | --- | +| `packages/db/tests/ssr-snapshot-admission-oracle.ts` | `6da03f17b4b8244f0c7b60825f33b42188faa457582eecfadc5a71d3c83d85d3` | +| `packages/react-db/tests/descriptor-query-binding-oracle.test.tsx` | `ac2b11d9894fbf4df35432f9f4a62ffaeb5081021e55e5f873846477b9411ca3` | +| `packages/svelte-db/tests/ssr-snapshot-admission-oracle.svelte.test.ts` | `9a8fc2ad26688559887d6b37cd17bf43bfb8dcbfb887abe6e13981900f120789` | +| `packages/svelte-db/tests/hydration.svelte.test.ts` | `6ef9c10936885165d9ace242e9caf1846b83fa1c439fd124288ba53b6f634379` | + + +### Final SSR cache check and publication evidence + +The preceding SSR results and size figures describe the first candidate. This +entry records the final cache design and supersedes those size figures. The +maintainer's programming-error policy and the bounded SSR judgment are unchanged. + +**Adjacent cache law.** With no write between reads, an ordinary observer getter +retains its snapshot identity. Without a DbClient, its server getter exposes that +same snapshot. A completed client query also shares its ready snapshot. Reading +the server view cannot invalidate the ordinary view's cache. This follows from the +existing retained-snapshot contract, independently of SSR admission. + +The markup comparisons did not observe cross-getter object identity. Before the +cache fix, the retained-snapshot owner added those observations to its existing +sorted-map histories. Its four ordered/single-result × attached/detached paths +all failed on the global server-mode cache. The focused replay also failed. A +narrower client-only mode passed the no-client checks, but all four prepared-client +paths and the focused replay still failed their identity comparisons. Each RED +run had five assertion failures at the promised getter checkpoint. A malformed +preload fixture failed setup before that corrected RED and receives no credit. + +The final observer leaves `getSnapshot()` unchanged. It caches only provisional +server copies in a separate WeakMap keyed by the ordinary snapshot. Completed +query results and no-client reads use the original object. Weak keys allow obsolete +revisions to retire. This adds no retry, rollback, or recovery lifecycle. These +failed candidates supply hostile controls for cache invalidation. The previously +recorded admission mutants still challenge the unchanged admission judgment. + +**Cache oracle conformance.** ORC-001/002: the retained-snapshot promise authorizes +identity at an unchanged revision, independently of production cache fields. +ORC-003/005/009: the literate owner explains its sorted-map model, history, real +observer/client preload path and getter checkpoint. ORC-004: the existing one-to-24 +command grammar keeps keys a..d, versions 0..9, and delayed read orders. Both query +shapes and both attachment modes reconstruct the reported failures. Removing +client preparation misses the narrower failed candidate. No-write capture is the +marginal identity case. Deletes of absent keys do not mutate the model or source. +ORC-008: the reference model retains its existing state. ORC-010: the recorded +controlled runs had no cleanup failure that replaced an assertion. ORC-011: no +separate shared semantic-fault hypothesis applies to object identity. ORC-013: +no-client and prepared-client observations distinguish the two failed cache modes. +ORC-014: these are core getter claims, with no host handoff inferred. ORC-006/007/015: +each of eight normal campaigns reaches both `server-getter` and `client-getter` +40 times. Fixed seed 2043 and an unsupplied seed use the same property and budget. +The existing wrong-lazy-snapshot calibration still fails its row comparison. + +The guarded cache replay uses property `live-query-observer.retained-snapshot`, +seed `2043`, and captured shrink path `0:0:0`. It executes all four random/replay +variants and passes. Separating the campaign test cases lets the manifest select +those variants without running fixed campaigns. A preliminary guarded command +rejected the missing owner mapping, which was then registered. That rejected +command is not successful replay evidence. The final run reports four nonzero +executions, four passing tests, and six skipped unrelated/fixed tests. + +**Final receiving reach.** Each SSR path again runs 120 samples per campaign, +including the seven boundary examples. The fixed campaigns retain their counts +above. Final random counts, in the same column order, are: + +| Receiving path | Cold/loading | Unbacked ready-empty | Unbacked rows | Query result | Normalized ready | Normalized partial | Pending query | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | +| React ordinary | 18 | 4 | 42 | 21 | 17 | 18 | 16 | +| React infinite | 18 | 8 | 34 | 24 | 21 | 15 | 15 | +| Svelte | 13 | 4 | 48 | 13 | 20 | 22 | 16 | + +React passes 486 tests in 23 files and Svelte passes 146 in 10 files on the final +db build. Both include type checks. The core run passes 311 tests in nine files, +including 37 guarded-replay protocol/manifest tests. After splitting the cache +campaign cases, the eight core behavioral owners pass 278 tests. This is four +more test cases, with the same eight campaigns and history budget. Svelte builds +successfully. The minified consumer passes 107 error names, index metadata, rows +and live updates. The production-error bundle check verifies 248 literals, with +20 shared literals, in ESM and CommonJS development and production builds. + +**Final production weight.** Against merged main `65d9ada50`, db grows 378 gzip / +460 brotli bytes. React grows 103 gzip / 106 brotli bytes. The typical app grows +322 gzip / 326 brotli bytes. Other scenarios grow 298–322 gzip bytes. These are +full-branch figures. The row-only, readiness-preserving and omitted-window-binding +alternatives failed the earlier calibrated comparisons. The global and client-only +cache alternatives above failed snapshot identity. The separate weak cache retains +one ordinary builder while preserving both public getter contracts. Remaining +bytes pay for preparation provenance, provisional snapshots and host selection. +Tests, oracle metadata and contract documentation are separate from shipped code. +This SSR follow-up adds 124 production-source lines and removes six. Its test +source and fixtures add 806 lines and remove 28. Those counts exclude contract +documentation and build metadata. + +The bounded limits and H04/H08/F07 evidence gaps above still apply. The coverage +map names the cache owner and its limits alongside the SSR receiving owners. +The following artifact hashes and the containing commit identify the final evidence: + +| Artifact | SHA-256 | +| --- | --- | +| `packages/db/tests/ssr-snapshot-admission-oracle.ts` | `6da03f17b4b8244f0c7b60825f33b42188faa457582eecfadc5a71d3c83d85d3` | +| `packages/db/tests/live-query-observer-snapshot-oracle.property.test.ts` | `a8674794624052ba2b76aa10b47af19a0e773cc029cf263c9e4c504fe673484a` | +| `packages/react-db/tests/descriptor-query-binding-oracle.test.tsx` | `ac2b11d9894fbf4df35432f9f4a62ffaeb5081021e55e5f873846477b9411ca3` | +| `packages/svelte-db/tests/ssr-snapshot-admission-oracle.svelte.test.ts` | `9a8fc2ad26688559887d6b37cd17bf43bfb8dcbfb887abe6e13981900f120789` | +| `packages/svelte-db/tests/hydration.svelte.test.ts` | `6ef9c10936885165d9ace242e9caf1846b83fa1c439fd124288ba53b6f634379` | +| `packages/db/tests/oracle-replay-manifest.ts` | `3fd499676ba595cba4220e1d728760df8482ef671bd6e889592b0ed209087e82` | diff --git a/docs/contributing/oracle-reviews/2026-10-22-sync-metadata-session.md b/docs/contributing/oracle-reviews/2026-10-22-sync-metadata-session.md new file mode 100644 index 0000000000..036de3f8d9 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-22-sync-metadata-session.md @@ -0,0 +1,1100 @@ +# Sync metadata session: borrowed laws and independent transactions + +Base revision: `30175be48` (`main`). Opened by +[issue #2220](https://github.com/TanStack/db/issues/2220). Every lane below +is RED on the base revision. The same pull request repairs production; see +"Production repair" for the evidence block. + +## Decisions + +The maintainer made five decisions on 2026-10-22; the fifth replaced an +encoding the author had extended without asking. Three more followed on +2026-10-23; the sixth withdrew the second: + +1. **Borrow standard theory.** "this is a clear case where we borrow laws"; + "Generally we want to follow standard theory here". One sync run is a + session. +2. **Closed nesting (withdrawn by decision 6).** "closed nesting is what the + api suggests and what we're partially enforcing so let's go with that". A + sync transaction begun while another is open was to be a closed-nested + child of the innermost open one. +3. **Read your writes in its standard form.** A read includes the source's last + surviving write: it returns that write or a write ordered after it. +4. **Seeds are older than every source write.** A hydration seed never + supersedes a source write to the same object, whatever their arrival order. +5. **D5: a source insert replaces a seed, in either order.** The first K4 + question asked about a seed arriving while the source's open transaction + had inserted the same row. The maintainer answered "yeah seeds are older". + The author then extended that answer, without asking, to seeds arriving + after the insert committed, and encoded it as timestamp order: the + source's insert of a seeded row was an update, so the seed's metadata and + fields survived it. A fresh adversarial reviewer showed that this reading + went beyond the question asked, broke the monotonic-reads transfer (a read + changed from absent to the seed's metadata with no new source write), and + adopted a partial merge of stale fields that review R9 of 2026-10-08 had + called speculative. The author put two arrival-independent readings to the + maintainer: A, a seed as a base layer that the source's insert merges + over; and B, a source insert that names the whole row and its metadata + and so replaces any seed of that row. The maintainer asked how a seed could + arrive after the insert. The realistic case is a `HydrationBoundary` that + renders on a later navigation, while the collection already syncs the row. + The maintainer chose "yup, b". + + Under B a seed supplies only objects that no surviving source write has + written. A source insert replaces a seeded row and its metadata without a + duplicate-key error; an update merges over it; an insert equal to a row + only a seed supplied is an insert, not an equal re-insert. + `docs/guides/ssr.md` ("Initial Data") is revised to say so. + + A third review round found that D5 implied two answers when a seed + arrives after a partial source update of the same row: seeds beneath + field by field (arrival-independent; a late seed can drop a field under + a live row), or ignored once the source has written the row (a partial + update merges over the row as it stood). The maintainer chose the second + ("b"). A seed that arrives after a surviving source write to an object is + ignored for that object; only a partial update's merge base depends on + arrival order. Metadata is unaffected, because metadata writes name the + whole object. The seed-order property therefore compares consumers' rows + only under full row updates. The glossary gains "hydration seed". +6. **Independent transactions (2026-10-23).** A first closed-nesting repair + passed every oracle in this record and broke eight existing tests in three + files: `collection-truncate.test.ts`, `collection-sync-reentrancy-oracle.test.ts`, + and `includes-collection-oracle.property.test.ts`. Each relies on a + transaction begun inside an open one committing and applying on its own. + Handler write-backs, change listeners, and live-query facades share the + sync API with the source, and the API cannot tell their `begin` from a + source's nested one. Under closed nesting a handler's confirmation would + merge into the source's open transaction, and a handler that awaited its + receipt could deadlock. The author put the conflict to the maintainer, with + B (independent transactions) recommended. The maintainer answered: "yeah B + is fine -- I don't have any real use case for nested transactions to begin + with and you're right that to add this properly we'd need clear transaction + ids to support the nesting". + + Every sync transaction is therefore independent. `write`, `truncate`, + `commit`, and abort act on the innermost open transaction. Accepted + transactions apply in commit order, and a hold delays application without + changing that order. A read orders the session's writes by commit: accepted + transactions in commit order, then the open transactions in begin order. An + abort discards only its own transaction's writes. An inner transaction + still reads the open writes of the transactions it began inside; the + author's proposal said so, and the maintainer accepted it. +7. **Cover every nested history (2026-10-23).** The first encoding of + decision 6 excluded an inner row write or truncate that conflicts with an + enclosing open transaction's row writes. The maintainer asked to "cover + everything with the grammar". The model now judges those histories. An + open transaction's inserts follow the rows before them in commit order; + a commit fixes their kinds. An outer insert that lands on a different row + the source wrote makes its transaction invalid, as a duplicate-key insert + would. No read includes an invalid transaction, it ignores later writes, + and its commit rejects. Two rules came from existing authority, not from + the maintainer. Later writes cannot revive an invalid transaction, a + truncate included: core's own comment says so ("later writes cannot + revive it"), and core's `truncate` contradicted it. A seed accepted after + an insert was written cannot change the insert's kind, because a seed is + older than every source write (D4). +8. **Cached rows are seeds, and wrapper inserts replace (2026-10-23).** The + persisted wrapper wrote every source insert to core and storage as an + update. The SQLite persistence plan (`PERSISTNCE-PLAN-SQLITE-ONLY.md`, + 2026-02-10, §D) gives the reason: a remote snapshot that overlaps the + local cache must not raise `DuplicateKeySyncError`. Under partial row + updates the update also merged a cached row's omitted fields under the + insert. No document argued for that merge; the test that pins it + (#2037) calls it pre-existing behavior. The maintainer chose option C: a + cached row is a snapshot older than every source write, like a hydration + seed, so a source insert replaces it and its metadata. While the change + was being implemented, three existing persisted owners showed that the + wrapper's inserts also replace rows the source itself wrote earlier in the + run (the hydration FIFO and hydration-failure tests, and the persisted + optimistic-history oracle). The maintainer then chose C1: in the wrapper, + an insert replaces whatever row it lands on, never raises a duplicate-key + error, and never invalidates its transaction. Its metadata still follows + the 2026-10-05 rules: it clears unless it is an equal re-insert of a row + the source wrote. Core keeps duplicates and invalidation. +9. **Cached rows are seeds in core too (2026-10-26).** The prep-PR review + found that a later `DbClient` seed could not replace a persisted cache + row: the wrapper loaded cached rows into core as ordinary adapter work, so + core counted them as source rows. The maintainer confirmed that cached + rows are seeds there too ("yeah they're seeds too"). A cache load + happens at preload, so a `DbClient` seed arrives after it and wins, as a + later seed does. The same review confirmed the metadata law that a kept + value is not a write: an equal re-insert keeps metadata as an update + does, so the latest seed still governs it until the source writes it. +10. **A merged write can carry a later seed into an insert's kind + (2026-10-26).** Decision 7 said a seed accepted after an insert was + written cannot change the insert's kind. Decision "b" lets that seed + change a partial update's merge base. A random persisted campaign found + the two meeting: an outer equal re-insert, then a later seed, then an + inner partial update that merges over the seed and commits ahead. Core + and the wrapper re-judged the re-insert against the row the inner update + left, which differs, so core invalidated the outer transaction and the + wrapper cleared its metadata; the model kept the re-insert. The + maintainer chose production's reading ("2"): an insert's kind follows the + row it lands on. A later seed alone does not change it, and neither does + an open transaction's own write over the seed, but a transaction that + commits after the seed and merges over it does. +11. **A peer's write in this run is a source write (2026-10-26).** A + second high review found that rows a peer's source wrote in this run, + which reach this tab through storage, were treated as seeds or skipped + behind a `DbClient` seed. The maintainer adopted the recommendation that + follows D4: such a row and its metadata are source-written for this + session. It replaces a `DbClient` seed this tab holds, a later seed does + not supersede it, and an equal re-insert of it keeps metadata. The rule + of `origin/main` that "DbClient hydration seeds remain source-owned until + confirmed by the source" now yields to a peer's notice of a write in + this run; rows from a previous run stay seeds (decision 9). Eighteen + persisted-oracle cases that held the seed against a peer's targeted + notice were rewritten to this rule. Asked about them, the maintainer + answered "LWW is a good general heuristic here": among source writes, + the last one wins, and a seed is older than every source write (D4). +12. **A source transaction may stay open across a storage read + (2026-10-26).** The new read-hold lane found that a commit threw "a + persisted sync transaction cannot cross a hydration cycle" when an + on-demand storage read started while the transaction was open. The + maintainer chose to support it: a transaction binds to a storage read + when it commits, not when it first writes, and writes during a read + follow the same metadata rules as other writes: the last write for a key + wins, so an insert without metadata clears an explicit write before it + and a cached row's metadata. The wrapper's earlier rules, that such an + insert kept an explicit write before it (on every path) and kept the + metadata of rows the read supplied, and that a commit crossing a read + threw, are retired; ten persisted-oracle cases were rewritten. +13. **Unknown authorship keeps what this tab holds (2026-10-26).** After a + full-reload notice, a missed notice, or a truncate, this tab cannot tell + which stored rows a source wrote in this run. The third high review + asked which way that should cut. The maintainer first chose to count + every such row as written, then, shown that a full reload would then + delete or roll back every held `DbClient` seed that storage lacks or + holds older (the persisted oracle's `keeps a DbClient hydration seed + across … peer notice` cases), chose the rule the code already followed: + a seed this tab holds stays, and a stored row this tab did not hold + counts as a source write, so a later seed cannot replace it. A peer's + write to a key this tab holds as a seed stays hidden until a notice + names the key or the source writes it. Decision 15 extends the rule to + every stored row. +14. **A commit fixes its inserts' kinds as it read them (2026-10-26).** The + fourth high review asked whether an inner transaction's insert, judged + against an enclosing open transaction's write it read, should be judged + again against the row it lands on when it commits ahead. The maintainer + kept the model's rule: a commit fixes its inserts' kinds in the order as + the transaction read it, so an inner equal re-insert of the enclosing + row keeps metadata and replaces the row it lands on. `30175be48` already + let an inner insert over an enclosing open truncate replace the row it + lands on. Decision 10's "an insert's kind follows the row it lands on" + concerns the inserts of transactions still open. +15. **Under unknown writes, only a changed row is a source write + (2026-10-26).** Decision 13 left the code's two paths disagreeing about + stored rows when this tab cannot tell what a source wrote: a reload + counted every stored row and its truncate as source writes, so later + seeds were ignored for the rest of the run, while a validation read left + unchanged rows alone. The maintainer chose the second: a stored row that + differs from the one this tab holds was written; an unchanged seed stays + one; a dropped row is a source's delete; and a reload's truncate is not a + source truncate. A reset still is. +16. **Row metadata of a missing row stays until a source deletes the row + (2026-10-27).** The peer lane found `begin rowSet(2) commit + peer(1,row)`: SQLite stores row metadata on its row, so metadata a source + writes for a row that does not exist is not durable, and a reload + deleted it. Whether a notice reloaded decided whether it survived. The + maintainer chose option C: this tab keeps such metadata for the rest of + the run unless a source deletes the row; a restart loses it, as it was + never durable. Three persisted-oracle cases that pinned the deletion + (`… orphan row metadata after a no-demand peer reload`, twice, and the + orphan check of `does not republish unchanged rows for a repeated + reconciliation notice`) were rewritten. +17. **A write over a `DbClient` seed stores the row this tab shows + (2026-10-27).** `seed(2) begin upsert(2) commit peer(1,row)`: seeds never + reach storage, so an update merged over a seed stored only its own + fields, and a reload or restart dropped the seed's fields. The + maintainer chose option B: the first update a transaction makes to a row + a `DbClient` seed holds stores the seed's row merged with the update. +18. **A reload after a named notice keeps a seed's metadata (2026-10-27).** + `seed(2,narrow) begin upsert(2) commit seed(2) peer(1,row)`: the update + kept the first seed's metadata, and a later seed replaced it in this tab + (decision 9), but storage cannot follow a seed, so a reload put the + first seed's metadata back. The maintainer chose option B: metadata no + source wrote in this run stays as this tab shows it across reloads, and + decision 17 stores only the row. Asked whether that holds after a + full-reload notice too, where two `main` cases expect stored metadata to + win, the maintainer chose the scoped form, B′: decision 18 governs + reloads after notices that name their keys; when this run's writes are + unknown, stored metadata that differs is a source write (decision 15). +19. **A reset's treatment of held seeds depends on the sync mode + (2026-10-27).** M4 found that an on-demand reset beside a held + `DbClient` seed skipped its truncate, so metadata of a missing row and + rows a hold kept from `base` survived it. Asked which rule governs held + seeds, the maintainer chose the mode-specific rule after a corrected + premise (eager resets already truncate first): eager, a reset is a plain + source truncate, seeds included; on demand, it keeps held seeds' rows and + metadata until the replayed demand replaces them, so one publication + replaces them without an empty view. +20. **An on-demand reset is a source truncate that keeps held seeds + (2026-10-27).** The new lane then found a seed of row 2 that arrived + after an on-demand reset beside a held seed of row 1: production + applied it, because the reset deleted only rows it saw, while with no + held seed the reset truncated and rejected the same seed. A reset that + skipped its truncate also skipped the demand replay that the truncate + drives. The maintainer chose a source truncate that exempts held seed + keys: they keep their row, metadata, seed status, and seed authority; + every other key is cleared and closed to later seeds; demand replays. + A first answer ("seen objects only") was withdrawn after a corrected + premise. + +21. **A persisted Collection accepts a source transaction when it hands it + to core (2026-10-28).** H2 needed a late abort: a commit whose signal + aborts while the wrapper still buffers it behind a storage read. + `SyncConfig.commit` says that aborting after acceptance has no effect. + The maintainer first chose that text, but it failed 16 cases of the + abort-graph law ("abort graphs preserve independent siblings and reject + dependent suffixes") and its witnesses, which the question had not + named. Asked again, the maintainer kept the abort-graph law: the + wrapper's acceptance is its handoff to core, so a signal aborted before + then abandons the transaction, its receipt rejects, and source reads + include it until then. +22. **A seed is judged when it arrives (2026-10-28).** The reads-and-seeds + lane found a seed that arrives after a buffered write to its row, which + is then abandoned. The glossary's "surviving source write" would revive + the seed; production ignored it for good. The maintainer chose judging at + arrival, for less code in a tiny window: the seed stays ignored for that + object. The glossary's hydration-seed entry says so. + +## Laws and sources + +| Law | Source | Transfer to a sync run | +|---|---|---| +| Read your writes | Terry et al. 1994, §3.1 | Every read includes every accepted write of the run and every write of its open transactions, ordered by commit: accepted transactions in commit order, then open transactions in begin order | +| Monotonic writes; one current value | Terry §3.4; Moss 1981, p. 61 | A transaction's writes take effect at its commit. The last write in commit order, issue order within a transaction, is the value, for reads and for what applies | +| Monotonic reads | Terry §3.2 | The writes a read includes only grow, except at an abort. When an inner transaction commits, an enclosing open transaction's write to the same object is ordered after it, so a read can change without a new write | +| Own reads | Adya 1999, §3.1.2 | A transaction reads its own writes; the innermost open transaction's writes come last in the order | +| Aborted reads | Adya §3.1.2, G1a | Not guaranteed across open transactions: an inner transaction reads an enclosing transaction's open writes and can commit before that transaction aborts (decision 6). No read after an abort includes the aborted writes | +| Invalid transactions | decision 7; core's invalidation comment | A transaction whose insert lands on a different source row, in commit order, is invalid: no read includes it, later writes are ignored, and its commit rejects. Where inserts replace (the persisted wrapper, decision 8), the insert replaces that row instead | +| Termination order | the sync API | `commit` and abort act on the innermost open transaction; the API has no transaction handle | +| Monotonic atomic view | Bailis et al. 2014 (HAT), Def. 27 | Once a consumer observes any effect of a transaction, it observes all of its effects thereafter, across both stores. The comparison is stronger: `collection.base` must equal the model at every checkpoint | +| Applied receipt | `SyncConfig.commit` docs; glossary "applied receipt" | A receipt settles when its transaction applies | +| Sticky availability | HAT §5.1.3 | The persisted wrapper and core form one session | +| Thomas's write rule | Thomas 1979; Bernstein, Hadzilacos, and Goodman 1987, §4.2 | A seed's snapshot is older than every source write, so it is ignored for any object a surviving source write has written, in either arrival order | + +Writes-follow-reads and PRAM follow from read-your-writes, monotonic reads, +and monotonic writes under commit-order apply; the oracle does not check them +separately. Two transfers are excluded. Session guarantees are per session, so consumers +(`collection.base`) keep the applied view. Adya allows version order to differ +from commit order; TanStack DB chooses commit order. + +The research survey read the primary texts locally and verified 13 +load-bearing quotes against the text or page images. The Thomas's-write-rule +row names the textbook rule; those texts were not read for this record. Moss is the only primary +source on nesting. Lynch, Merritt, Weihl, and Fekete's formal treatment was +not accessible. + +## Executable owner + +`packages/db/tests/sync-metadata-session-oracle.ts` holds the contract, an +event-log model, the history grammar, the driver contract, and the +comparison. Drivers: + +- `sync-metadata-session-oracle.property.test.ts`: a `DbClient` Collection; + lanes `flat`, `nested` (depth ≤ 3), `seeds`, `nested-seeds`, `seeds-full` + (`rowUpdateMode: 'full'`), and `nested-conflicts`, which favours row + writes to one row inside nested transactions so that inner writes land + ahead of enclosing ones and transactions become invalid. +- `db-sqlite-persistence-core/tests/sync-metadata-session-persisted.test.ts`: + `persistedCollectionOptions` over an in-memory adapter, with store and core + holds; lanes `persisted-flat`, `persisted-nested`, `persisted-seeds`, + `persisted-cached` (row 1 is in storage when the run starts; the model + holds it as a seed accepted before any action), `persisted-conflicts`, + `persisted-nested-seeds`, and `persisted-reads`, whose read hold keeps an + on-demand storage read in flight while the source writes and commits + (decision 12). The last crosses `DbClient` seeds with nesting + and the cached row, with the conflict lane's weighting; its + `keepsMetadata` flag favours writes that keep metadata over writes that + clear it, without excluding any action, so a later seed often meets + metadata that an accepted re-insert kept. Every persisted lane declares + the one model difference of decision 8: an insert over a different row + replaces it. + +The row-metadata composition oracle keeps its base nested lane, which +already judged independent transactions: a transaction begun inside T1 +applies first, and T1's inserts are reclassified against the rows it leaves. +This change adds a `held` variant of that lane (a hold cannot change commit +order) and applies D5 to its seed variant. Its held-window lane, added +earlier for this issue, stays. + +The grammar generates every nested history (decision 7). The key +independence property skips histories in which the model invalidates a +transaction: invalidation is per transaction, so a row 2 conflict also +removes row 1's writes. That is atomicity, not a cross-key leak. + +## RED evidence on `30175be48` + +| Witness | Law | Observation | +|---|---|---| +| `hold, begin, collectionSet(cursor), commit, begin` | Read your writes | cursor reads absent in the open transaction | +| `hold, begin, rowSet(1), collectionSet(gc:a), commit, begin` | Read your writes | row and prefix list miss the held writes | +| `hold, begin, rowSet(1), begin, rowSet(1), commit, commit, release` | Commit order | the held inner commit stays after the open outer transaction: the read returns the inner write, and the release applies the outer write first, so the inner write wins | +| `hold, begin, begin, rowSet(1), commit, rowSet(2)` | Termination order | the outer transaction's write throws `SyncTransactionAlreadyCommittedWriteError` | +| `begin, rowSet(1), commit, seed(1)` | Seeds are older | the seed's metadata replaces the source's metadata-only write | +| persisted: `hold(core), begin, upsert(1), commit, begin, commit, begin, collectionSet(cursor), commit` | Read your writes | row 1's metadata reads absent; the store-hold neighbour passes | +| persisted: `hold(core), begin, collectionSet(cursor), commit, begin, upsert(1), commit` | Read your writes | the cursor reads absent once the wrapper has handed both transactions to core; the store-hold neighbour passes | +| persisted: `begin, rowSet(2), upsert(2)` | Own reads | the insert's implicit clear is not visible in the open transaction | +| `begin, upsert(1), seed(1), commit` | A source insert replaces a seed (D5) | production turns the open insert into an update over the late seed, so the seed's metadata survives the insert's clear and its other field survives in the row | +| `seed(1), begin, reinsert(1)` | A source insert replaces a seed (D5) | core treats the insert as an equal re-insert and keeps the seed's metadata; the persisted wrapper clears it, as D5 requires | +| composition nested lane, `immediate` and `held` | D5 seed variant; commit order under a hold | both variants fail; the held variant also applies the open outer transaction before the inner one | +| retention: `keeps an accepted insert when it commits ahead of an open truncate (held: true)` | Commit order | the inner insert applies after the outer truncate, so row 1 survives | + +Witnesses written for closed nesting and then revised for decision 6 pass on +`30175be48`, which already applied independent transactions in commit order +when nothing was held: `outerAbortKeepsInnerCommit` and +`innerTruncateAppliesAtItsCommit`. The run against the base production files +(`sync.ts`, `state.ts`, `client.ts` from `30175be48`, with this branch's +tests) fails 51 of 186 tests in the session, composition, and retention +files. + +## Calibration + +- A temporary commit-order reader (walk every pending transaction newest + first, stop at a truncate for rows, merge `list()` oldest first) made the + core flat lane and both issue witnesses GREEN in three runs. The same edit + left the nested and seed lanes RED, as their laws require more than a reader + change. The edit was reverted. +- With that reader, a mutant that ignores the truncate stop fails the + composition held-window lane's truncated variant, and a `list()` that merges + only the newest transaction fails both held-window variants. +- A bounded exhaustive model check (outside the repository) found that + readers which omit ancestors or read only the newest pending transaction + violate the laws. It also judged today's independent nested apply against + closed nesting; decision 6 superseded that part. +- The campaign reach test requires each lane to reach its declared states; + "Production repair" lists the counts. + +- A mutant gap hunt against the commit-order reader baseline, in a separate + worktree: walking row reads oldest first, ignoring the row truncate stop, + and merging `list()` newest first fail at the comparison. Walking + collection reads oldest first fails both flat campaigns but survives the + named witnesses and the composition held-window lanes. Returning a delete + write's missing value is equivalent: a delete carries no value. +- Adding the receipt observation kept the core flat lane GREEN with the + commit-order reader in three runs. +- Seeds. Under the first K4 encoding (reading A), `begin, upsert(1), + seed(1), commit` passed on production, and a mutant that derived the + implicit metadata write from the original `insert` failed it. Under D5 + the expectation reverses, and that witness is RED. While encoding A, the + model briefly judged the insert's row presence after its own row write, + which turned the wrapper's implicit-clear witness falsely GREEN. Under D5 + the clear is an ordinary write again, and the witness is RED. +- Paired formulations. Key independence and seed order fail on `30175be48` + without consulting the model's reads. + - Key independence, seeds lane: `hold, seed(1), seed(2), begin, cancel`. + Row 1's held seed reads absent once row 2's seed follows; without + `seed(2)` it reads as seeded. This is the issue's newest-transaction read + in a cross-key form. + - Seed order: a seed that arrives after a source's metadata write replaces + it, while the same calls with the seed first keep the source's write. + Under reading A, a reviewer found that a reordered run could throw on an + equal re-insert, a false positive. Under D5 the insert replaces the seed + in both orders, so the re-insert stays equal, and a throw would be a D5 + violation. + - Calibration: with the commit-order reader, flat key independence passes. + A mutant reader that stops at the newest transaction touching any row's + metadata fails it on both flat campaigns. Nested key independence fails + on the known closed-nesting throw. No correct production exists yet to + calibrate seed order GREEN; with every seed already first, its two runs + perform identical calls. +- Range (ORC-004). Two edge cases from outside the design work were added. + - Collection keys `gc:` (equal to the prefix) and `gc` (a proper prefix of + it) needed only new parameter values. With the commit-order reader, the + core flat lanes and both issue witnesses stayed GREEN in three runs. The + probe exposed one oracle artifact: the model listed keys in declaration + order while the drivers sort them, which only matched for the old key set. + The model now sorts. + - A seed that carries a field the source never writes exposed a missing + rule: under `rowUpdateMode: 'partial'`, a source update merges over the + row before it, a seed included. The model now merges updates; an insert + replaces the row (D5). `updateOverSeed` checks the merge and + `updateOverSeedFullRows` checks replacement under a `seeds-full` lane; + both pass on production. + +## Production repair + +The repair keeps the existing structures and changes these rules. + +- **Reads.** `metadata.row.get`, `metadata.collection.get`, and `list()` + walk every pending transaction, newest first, then applied state; a + truncate hides older row metadata. The persisted wrapper reads every open + transaction, innermost first, then its staged transactions, then core. +- **Commit order.** `acceptSyncTransaction` places a committed transaction + after every accepted one and ahead of every open one; seeds use the same + method. An inner commit during a hold therefore no longer leaves the queue's + last transaction committed, so the outer transaction can still write and + commit, and a release applies in commit order. A replay keeps an accepted + transaction's operation kinds: an inner insert written over an outer open + truncate replaces the row it lands on, as `30175be48` already did when + nothing was held. An open transaction's inserts still follow the rows before + it and can make the transaction invalid. +- **Seeds (D4, D5).** Row data and row metadata are judged separately against + committed adapter work, including a wrapping sync's staged committed + transactions. A seed replaces earlier seeds whole. A source insert over a + row only a seed supplied is an insert; over a seeded row the source also + wrote, it stays the re-insert the source wrote, so a later seed beneath an + open update cannot make it a duplicate. An insert names its whole row even + as an equal re-insert. A held seed's receipt has a rejection handler. +- **Wrapper metadata.** An insert without metadata stages a clear unless it is + an equal re-insert of a row the source wrote; the last write for a key + wins. +- **Query.** `getRowMetadata` no longer falls back to applied metadata, which + read a staged clear as "no value". +- **Invalid transactions (decision 7).** Core's readers skip a transaction a + replay invalidated, and `write` and `truncate` ignore it; `truncate` no + longer clears the invalidation. Its metadata writes need no check: no read + includes it, and it never applies (a mutant that lets them through is + equivalent). +- **Seed authority across a hold.** The replay projection records which keys + accepted source transactions wrote. An accepted source write makes a row + the source's, as its application does, so a seed no longer exempts that + row while the transaction is held. Before, an inner commit during a hold + kept the outer transaction valid, and the same history without the hold + invalidated it. +- **Wrapper inserts (decisions 8 and 9).** A source insert replaces the row + it lands on. The wrapper judges its metadata when the source writes it and + hands it to core as an update that names the whole row + (`originalSyncType: 'insert'`, core's own form for an equal re-insert), so + core replaces the row and only the wrapper's metadata writes change its + metadata: an insert that clears staged a delete, and an equal re-insert + keeps the value without writing it. Before, the wrapper wrote the kept + value back as explicit metadata, which made the source its owner, and a + delete then the insert over a different row. In storage it is an `insert` + mutation, which the SQLite adapter upserts. + The wrapper records the keys its source transactions handed to core in this + run; any other row came from the cache or a seed, so an insert equal to it + still clears metadata. After an inner commit, the wrapper re-judges each + enclosing transaction's implicit metadata clears on the keys the inner + transaction wrote, as core's replay does, and an inner re-insert keeps its + metadata when it lands where the row is absent. The wrapper keeps, per key, + every insert without metadata since the last write that named the key's + metadata, and re-judges them all: if any no longer re-inserts the row it + lands on, the key's metadata clears; otherwise the write before them + stands. An update without metadata keeps that judgment open, because the + inserts before it still decide the row's metadata. +- **Duplicate inserts.** A seed exempts a source insert from the duplicate + check only over a row that only seeds supplied; over a row the source + wrote, it is judged like any insert, whatever holds or seeds there are. In + the replay, a seed beneath an open insert of a row the source also wrote + keeps that insert's kind. +- **Cached rows (decisions 9 and 11).** A storage read marks a row it + applies as a seed unless core holds it as a source row or a source wrote + it in this run, which the runtime records from this tab's handoffs and + peers' notices (a truncate or a full-reload notice makes every stored row + count as written). The read stays one transaction; core keeps seed status + only for its seed keys, and every other write of the read, a validation + delete included, is adapter work. A `DbClient` seed holds against stored + rows only until a source writes its key in this run. +- **Writes during a storage read (decision 12).** They follow the ordinary + metadata rules, and a transaction binds to a read when it commits. + +### Evidence block + +- **Law:** read your writes, monotonic writes in commit order, own reads, + monotonic atomic view, applied receipts, invalid transactions, and D4/D5 + seed authority, as stated above, with the wrapper difference of decision + 8. Authority: decisions 1 and 3–8. +- **Reach:** declared with `createOracleReach` in each fixed campaign + (ORC-015); a campaign fails below its minimum. Core lanes: 11, or 5 for + states that need a seed and a source write on one row, or a write after an + invalidation. Persisted lanes: 5. The prep-PR round added the states + `duplicate source insert` (and its hold and seeded forms) and `seed under + a kept re-insert`, and raised the seeds and nested-conflicts campaigns to + 150 and 450 histories; every fixed campaign meets its minimums. Counts in + the fixed campaigns before that round: + + | State | flat | nested | seeds | nested-seeds | seeds-full | nested-conflicts | + |---|---|---|---|---|---|---| + | read with two held transactions | 18 | 37 | 16 | 20 | 12 | 65 | + | open transaction over a held transaction | 51 | 61 | 36 | 47 | 30 | 136 | + | truncate during a hold | 30 | 31 | 22 | 24 | 15 | 69 | + | nested commit | | 59 | | 52 | | 158 | + | nested commit during a hold | | 43 | | 36 | | 107 | + | abort keeps an inner commit | | 11 | | 9 | | 24 | + | inner write to an enclosing row | | 7 | | 10 | | 92 | + | transaction invalidated | | 2 | | | | 28 | + | commit of an invalid transaction | | 2 | | | | 15 | + | write to an invalid transaction | | 1 | | | | 16 | + | seed under a source insert | | | 37 | 27 | 24 | | + | seed under an open update | | | 10 | 5 | 7 | | + + `nested-conflicts` runs 300 fixed histories; the others run 100. Persisted + lanes: `persisted-conflicts` (150 histories) reaches an insert that + replaces a different row 18 times and an inner write to an enclosing row + 50 times; `persisted-cached` (80) reaches a source insert over the cached + row 25 times and a source update over it 14 times. +- **RED:** + - `30175be48` production with this branch's tests, as listed under "RED + evidence": 51 of 186 tests fail in the session, composition, and + retention files. + - Decision 7, on the production before its repair: 7 failures in the + session oracle (reads include an invalid transaction's staged metadata + and a collection write made after its invalidation; a hold changes + whether a seed exempts a row). Two more were model and grammar faults, + fixed before the production change: key independence compared histories + that invalidate a transaction, and the model kept a transaction's row + writes from before its own truncate. + - Decision 8, on the wrapper before its repair: the `persisted-cached` lane + fails because a source insert merges over the cached row and keeps its + `seeded` field; the persisted lanes fail on nested conflicts. +- **GREEN:** `packages/db`: 286 files, 13,917 tests pass; the session oracle + file passes 78 of 78 twice. `packages/db-sqlite-persistence-core`: 33 + files, 1,732 tests pass (2 skipped, 2 todo); the persisted session file + passes three runs in a row. Query DB, node, browser, and Electric suites + pass with their receiving expectations updated for decision 8. +- **Mutant:** each fails the oracle. + - Commit in place (no commit-order move): 17 failures, including + `commitOrderDuringHold`, `outerContinuesAfterInnerDuringHold`, nested hold + invariance, and the nested campaigns. + - Replay reclassifies accepted transactions and throws on a duplicate, as + `30175be48` did: both variants of the retention witness. A variant that + marks the accepted transaction invalid without throwing changes only the + internal projection and survives; no public checkpoint observes it. + - Seed exemption only for seed-only rows: `seedUnderOpenReinsert`. The fixed + campaigns do not catch it; random campaigns did, twice, before the witness + existed. + - An equal re-insert that merges under partial row updates instead of + replacing its row: `seedUnderOpenReinsert`. + - Readers that include an invalid transaction: the nested and conflict + campaigns. A truncate that revives an invalid transaction: the nested and + conflict campaigns and the conflict lane's reach. + - Wrapper, earlier rules: reading only the innermost open transaction; + keeping an earlier explicit write over an insert's clear; clearing on + every insert; hiding staged transactions from seed authority. Each fails + at least one persisted lane. + - Wrapper, decision 8: an insert that merges over a held row (12 persisted + hydration cases and the hydration FIFO test); storage that receives + inserts as updates (the sync-config and hydration cases); a cached row + counted as source-written (`persisted-cached`); enclosing implicit clears + left unjudged (`persisted-conflicts`). An inner re-insert that loses its + kept metadata survives every campaign; the pinned witness `keeps an inner + re-insert's metadata when it commits ahead of the row it re-inserted` + kills it. + - Decision 9 (prep-PR round): the original handoff on `743a6ce14` fails + all four new witnesses and `persisted-nested-seeds`; writing the kept + value only when core would not keep it fails the inner re-insert + witness; loading cached rows as source rows fails the cached-row witness + and `persisted-nested-seeds`; a core that ignores a wrapper's + `originalSyncType` merges over seeded rows and fails `persisted-seeds` + and `persisted-cached`; dropping the insert's judgment at a later update + fails `rejudges an enclosing re-insert that a later update kept`; re-judging + only a key's last insert fails the two chain witnesses. +- **Limits:** under partial row updates the seed-order property drops + `reinsert` from both runs, because decision "b" lets a moved seed change a + re-insert's planned row. Key independence skips histories that invalidate + a transaction. The `persisted-reads` and `persisted-peers` lanes' adapter + keeps durable rows, so storage reads and reloads within a run are + exercised there; the other persisted lanes' cached row is readable only + until the run's first durable commit. Since the eighth round, a peer is a + grammar dimension of `persisted-peers`, played by the driver through the + coordinator (see "Eighth round"). The error an invalid commit rejects + with, and restart and resume, stay outside this oracle. + +### Code weight + +Final head `d1166fe6f`, against the merge-base `4c39add29`: `app` grows +232 bytes gzip, `@tanstack/db` 523, `db-sqlite-persistence-core` 1,331, and +`query-db-collection` shrinks 21. Each fix commit since the eighth round, +measured against its parent: + +| Commit | `app` | `db` | persistence | +|---|---|---|---| +| `d3847dbb6` notice-carried reload | 0 | 0 | −6 | +| `e4fb7a71d` decisions 19 and 20 (reset truncate, kept seeds) | +76 | +134 | +76 | +| `3b1973276` per-key unknown writes | 0 | 0 | +151 | +| `18f7df562` drop read knowledge from unknown writes | 0 | 0 | −30 | +| `4b6081698` H1, H2, H5, H6, decisions 21 and 22 | +28 | −15 | −31 | + +The rest of the change from the eighth round's figures comes from the merges +of `origin/main`. The `app` growth since the eighth round is the source +truncate that a reset opens (`sourceTruncate`, `truncatesSourceKey`) and +the H1 reader check that uses it. The H5 and H6 dead-code removals paid for +the H2 move of `noteRunWrites` after the commit. + +Eighth round, against `7e46f81ad`: `app` grows 125 bytes, 20 fewer than +before, because `_syncedRowMetadataKeysOutsideBase` left the Collection +class; the staging check for preserved keys and the metadata reader's check +for a status-keeping truncate cost less than it saved. Persistence reaches ++1,206 (204 more): the stored-row status rule, whole-row reload writes, the +seed bases that decision 17 stores, and the held-row checks. + +`pnpm size:compare` against `427a7c31e` (`origin/main` after the second +merge): the typical `app` scenario grows 126 bytes gzip. The second high +review's repairs cost `app` 9 bytes: the internal whole-row marker and +readers that skip invalidated transactions in place. The marker is a bare +`Symbol()`, because its `Symbol.for` description string alone cost 31 +bytes and the wrapper imports the symbol from `@tanstack/db`. Seed status +became a key set on every seed transaction (a `DbClient` chunk passes its +keys), so per-key storage reads cost no extra check. `@tanstack/db` as a +whole package grows 375, `db-sqlite-persistence-core` 849, and +`query-db-collection` shrinks 14. In persistence, the second high review +went from 876 to 699 because removing the operation walk and the +hydration-deferred metadata path paid for its rules; the third added 150 +for the per-object record of writes in this run, judging open inserts +again after internal writes, and the `begin` guard. `app` did not change +in the third. + +The seventh round: against `6c1ca8f8f`, `app` grew 124 bytes, two fewer +than before, because the projection's accepted-source key set became a local +of its rebuild, and persistence 933 (84 more: the internal apply's written +keys and the first reload repair). Against `b8d8ee07c`, after two merges, +the pushed head measures `app` +135 and decision 15 adds 10 more (+145): +core's truncate keeps authority and seed status for a transaction that +marks seed keys. Persistence reaches +1002 (69 for decision 15: the shared +stored-seed rule and the dropped rows' deletes). A first repair that +validated reloaded rows in place, with no core change, failed the three +exact-shape witnesses, because only a truncate republishes rows that change +events consider equal. + +Earlier rounds: the private-member mangle cache now +covers the branch's new private methods. Removing Query's owner supplement +saved 9 bytes, and tracking peer writes and judging enclosing inserts again +after an abandoned transaction added 91 to persistence. + +The prep-PR round changed these numbers. Decision 9 costs core 12 minified +bytes (a wrapper's `originalSyncType` passes through `write`), paid for by +the state rebuild that `reset()` now reuses; `app` stayed at +111. The +duplicate-insert repair (B, C) adds 14 bytes to `app`. H first added 19 +bytes as a delete-and-return pair of set updates; merging the two +`DbClient` key sets into one map cut that to 6. In persistence, decision 9 +removed the kept-value lookup and the delete-then-insert handoff and added +the cached seed marking and the per-key chain of implicit inserts (+83); +the per-transaction staged rows that make a write-time read linear (D) add +the rest. Each addition has a witness or mutant that fails without it. + +- Core, paid by every app: the readers' loops over every pending + transaction and their skip of invalid transactions, commit-order + acceptance, the source-key sets that tell a seed-only row from a + source-written one, and the insert-replaces-its-row rule. +- `DbClient` only: per-object seed authority, including a wrapping sync's + staged transactions. It lives in `client.ts`, so an app without `DbClient` + does not ship it. +- Persistence only: the staged-row fold, the re-insert judgment, the record + of implicit metadata clears and their re-judgment after an inner commit, + the source-written key set, and the insert handoff to core and storage. + Each has a mutant that fails a persisted lane or witness. + +Refactors made to pay for it: the seed authority lookups moved out of the +state class into `client.ts` (`app` +240 to +111); the seed acceptance method +became the single acceptance method for commits and seeds; the two seed +authority lookups became one loop; the base invariant throw for an accepted +duplicate is gone; guards on an invalid transaction's metadata writes were +removed once a mutant showed them unobservable; decision 8 removed the +wrapper's invalidation and duplicate-key paths. Two refactors were tried and +reverted because they grew gzip: one shared reader for `row.get` and +`collection.get` (gzip compresses the two loops better than the helper), and +moving the wrapper's deferred-key predicate into core (it moved bytes from +persistence into every app). + +## Review ledger + +Three loss-audit scanners and three oracle reviewers examined this change. + +| Finding | Disposition | +|---|---| +| G1a was cited as permitting child reads | Fixed: the laws table cites relative commit and delayed commit | +| Monotonic atomic view dropped "thereafter" | Fixed: stated, with the stronger per-checkpoint comparison | +| Writes-follow-reads and PRAM uncited | Fixed: stated as consequences, not checked separately | +| Receipt checkpoint for a nested child undefined | Fixed: receipts are an observation; the law follows from the existing receipt contract | +| Adjacent form F2 (nested truncate during a hold) not pinned | Fixed: witness added | +| Adjacent form F3 (consumer sees a seed, then the source) not pinned | Fixed by D5: the order is legal, and `seedUnderOpenInsert` checks it | +| Source and consumer seed layers differ without explanation | Fixed: model prose explains the two layers | +| Reach test does not ablate generator dimensions | Fixed: see "Reach ablation" below | +| Derived checks (hold invisibility, unrelated-action invariance) and the bounded model check live outside the repository | Fixed: three production-against-production properties (hold invariance, key independence, seed order); the bounded model check stays outside | +| Mutant "collection reads oldest first survives" | Refuted on re-run: both flat campaigns kill it | +| The issue's held-cursor history passed through the wrapper | Checkpoint artifact: actions ran back to back, so transactions stayed in the wrapper's staging. With continuations drained between actions (the receipt `flush`), the core-hold variant fails | +| `list()` presence of a key set to `undefined` | Fixed: core lanes store `undefined`; `get` reads it as absent and `list()` keeps it | +| Equal re-inserts not generated | Fixed: `reinsert` action | +| Reach ablation | Fixed: removing each axis removes its regime | +| No second formulation in the repository | Fixed: hold invariance compares production with and without holds | +| Nesting and seeds through the wrapper not driven | Fixed: `persisted-nested` and `persisted-seeds` lanes | +| Stale runs and writes without a transaction | Fixed: boundary witnesses | +| Restart and resume reads | Owned elsewhere: SQLite resume-snapshot and Electric resume owners | +| Truncate across several keys | Covered: the session model has two row keys; truncate clears both | + +## Second review round + +Three fresh reviewers examined the changes made after the first round: an +adversarial review of the seed laws, a mutant gap hunt in a separate +worktree, and a tension scan of the Query witnesses. + +| Finding | Disposition | +|---|---| +| The K4 encoding extended the maintainer's answer to committed inserts, broke the monotonic-reads transfer, and adopted the partial merge that R9 called speculative | Fixed by decision D5 (reading B), asked of and chosen by the maintainer | +| `runSeedOrderInvariance` could throw on an equal re-insert after reordering | Fixed by D5: an insert replaces a seed in both orders, so the re-insert stays equal | +| Seed-order invariance cannot choose between arrival-independent readings | Accepted: the witnesses and D5 carry that choice; the property checks arrival independence only | +| The hydration-authority owner runs only full row updates and no metadata | Fixed: it crosses full and partial modes, adapter inserts and updates with and without metadata, and seeds carrying metadata and an extra field, `initialData` rows with an extra field, and a metadata-only source write; it reads metadata through `client.dehydrate()`. After the third review round, 19 of its 52 grid cells and one pinned production case fail on `30175be48`: an insert keeps a seed's metadata or merges its field (including over `initialData`); a late seed's metadata is dropped after an update that wrote none; a later seed overwrites a metadata-only source write; a second seed merges over the first; a metadata-less later seed keeps the earlier seed's metadata | +| Seed-to-seed merge versus replacement is not distinguished | Fixed: narrow seeds omit the extra field; `laterSeedReplacesSeed` fails on `30175be48`, where a second seed merges over the first | +| Reader mutants for a missing truncate stop, `list()` ignoring a pending delete, and prefix matching that is too loose or too strict died only in generated campaigns | Fixed: the named witnesses `openTruncateHidesHeldRow`, `openDeleteHidesHeldListEntry`, and `prefixBoundary` each kill their mutant and pass with the commit-order reader | +| A full-mode update merged only in the accepted projection survived: no checkpoint observes the accepted row | Fixed: an upsert that names metadata carries a field `m`, so row shapes differ; `fullUpdateThenEqualInsertDuringHold` kills the mutant with a duplicate-key throw at the equal re-insert, and production passes | +| A merge keyed on the original sync type, at the projection only, died only in one random run | Covered by the same witness's observation (the projection's row); not separately re-run | +| The Query retention witness did not separate "drop the fallback" from a full repair, and its prose blamed the fallback | Fixed: prose corrected; a peer-owner witness keeps the row, so the claim is an update and the stale owner comes from the claim's read of applied owners | +| A held removal of a hydrated row, then a claim, should clear the seed's metadata | Fixed: witness added; it fails on production and passes with the fallback dropped | +| Query witnesses read `_state.syncedMetadata` | Fixed where a `DbClient` exists: they read `client.dehydrate()`; the others say why they keep the internal read | +| Mutant: Query owners ignore accepted in-memory edges (`rowToQueries` supplement in `getOwnersFromMetadata`) | On production the new witness `publishes a held owner beside a later claim of its row` fails before the mutant matters. With the commit-order reader and the `??` fallback dropped, the mutant passes all 402 ownership tests. Probably equivalent there: each application that adds an edge also stages the owner set, and the reader then finds it. Open: a history with an in-memory edge but no staged owner write would separate them; none was found. If none exists, the repair may delete the supplement | +| Mutant: Query retires owner tracking before staging the cleanup | Equivalent by source inspection: no read occurs between the early retirement and the commit | +| Mutant: Query never retires owner tracking at cleanup | Killed by a sibling test (`keeps a released row and fails loudly when its release commit fails`), not by the retention witness | + +## Third review round + +Three fresh reviewers examined the D5 encoding, the Query and hydration +tests, and a mutant campaign in a separate worktree. + +| Finding | Disposition | +|---|---| +| D5 implied two answers for a seed that arrives after a partial source update | Fixed by the maintainer's choice "b" (decision 5); the session model places seeds by arrival, and seed order compares rows only under full updates | +| A later seed without metadata keeps the earlier seed's metadata; seeds always carried metadata | Fixed: bare seeds; `bareLaterSeedClearsMetadata` fails on `30175be48` | +| No fresh source insert over a seed-only row | Fixed: `insertOverSeed` action; `insertOverSeedReplaces` fails on `30175be48` | +| Seeds were never crossed with closed nesting | Fixed: `nested-seeds` lane | +| A late seed after a partial update was unwitnessed | Fixed: `partialUpdateThenLateSeed` fails on `30175be48` (the late seed's metadata is dropped) | +| A late seed erases Query's owner edges on a hydrated row Query claimed by a metadata-only write | Fixed: `ignores a late hydration seed for a row whose metadata a claim has published` fails on `30175be48` | +| An adapter insert over an `initialData` row merges its field; the hydration grid could not see it | Fixed: `initialData` rows carry a field and stay present in insert-first histories | +| The hydration grid had no metadata-only source write, and its reference cleared metadata on an equal re-insert of a source row | Fixed: the action is added and the clear is scoped to seed-only rows | +| Query tests matched the opaque owner-key encoding | Fixed: owner counts plus public-behaviour identity siblings (release and hydrated-release checks) | +| Stale counts, missing "hydration seed" glossary term, SSR omissions | Fixed | +| Mutants against a scratch D5 seed implementation: equal insert over a seed stays an update; replace only applied seeds; replace only pending seeds; metadata authority from row writes only; seed replacement in full mode only | Killed (by `equalInsertOverSeed`; seed-lane properties; hydration insert cells and `seedUnderOpenInsert`; `seedUnderMetadataOnlyWrite`; `laterSeedReplacesSeed` and partial hydration cells) | +| Mutant: Query owner supplement deleted | Survives with or without the fallback; probably equivalent, as recorded above | +| Mutant: Query fallback removed | Repairs exactly the fallback witnesses with no new failures | +| The persisted equal-insert witness is insensitive to core seed mutants | Accepted: it checks the wrapper's own clearing; core seeds are judged by the core lanes | +| A scratch D5 seed implementation still leaves seed lanes RED on histories with deletes, releases, and truncates between seeds | Open: diagnose during the production fix | + +## Fourth review round (prep-PR) + +A code review, a simplification pass, and 10× random campaigns examined the +branch before its pull request. Items B to H were then taken through one +plan of oracle changes before any repair: one grammar action for B and C, a +late-abort probe for E, a scaling experiment for D, and a churn count for H. + +| Finding | Disposition | +|---|---| +| A: a seed's metadata returns through the wrapper after an accepted equal re-insert (`persisted-seeds`, random seed -387838471) | Fixed: the wrapper hands inserts to core as whole-row updates (decision 9). Witness `lets a later seed replace metadata that an accepted re-insert kept` fails on `743a6ce14` | +| A sibling: an inner re-insert of an enclosing row commits ahead onto a seeded row; core classifies it `insert` and the kept value again claims the metadata | Fixed by the same change. Witness `lets a later seed replace metadata that an inner re-insert kept`; it alone kills the narrow fix that writes the kept value only when core would not keep it | +| A gap: no persisted lane crossed seeds with nesting or with the cached row | Fixed: `persisted-nested-seeds`, with reach `seed under a kept re-insert` (minimum 5) | +| Cached row versus a later `DbClient` seed: the seed could not replace the cached row or its metadata | Fixed (decision 9). Witness `lets a later seed replace a cached row's metadata` | +| An update without metadata after an enclosing insert dropped the insert's re-judgment, so an inner commit could not make it clear metadata (found by the 10× campaign) | Fixed. Witness `rejudges an enclosing re-insert that a later update kept` | +| The wrapper re-judged only a key's last insert without metadata against the value saved before it, so an earlier insert whose kind an inner commit changed was missed, in either direction (found by a second 10× campaign) | Fixed: each key keeps its chain of such inserts. Witnesses `rejudges every enclosing re-insert of a key, not only the last` and `lets an enclosing clear become an equal re-insert after an inner commit` | +| B: a hold hides a duplicate source insert (`acceptSyncTransaction` refreshes the projection only when a transaction moves) | Fixed. The grammar never wrote a different insert over a row the source wrote; the `conflictingInsert` action does, and the model expects core's duplicate-key error (decision 8 keeps duplicates in core). On `743a6ce14` plus decision 9, 26 core tests fail, every seeded lane among them, including hold invariance. Core's classifier now exempts only a row that only seeds supplied; a seed beneath an open insert keeps that insert's kind in the replay instead | +| C: a seed hides a duplicate insert in the same or an enclosing transaction | Fixed by the same change and caught by the same action (`seed(1) begin upsert(1) conflictingInsert(1)` was accepted) | +| D: the wrapper's staged-row fold is O(N²) per transaction | Fixed. 4000 inserts in one transaction took 48 ms against 0.8 ms on `origin/main`. Each staged transaction now keeps its row per key, so a write-time read folds one entry per transaction; 16000 inserts take 6 ms. A mutant that drops partial updates' merge base fails three witnesses and four persisted campaigns. No oracle measures work; the scaling run is the evidence | +| E: an aborted staged transaction leaves enclosing implicit metadata judged against it | Refuted for a store-held transaction: `commit` says that aborting after acceptance has no effect, and its inner transaction still applied. Confirmed and fixed for a transaction buffered behind a storage read, which an abort abandons before acceptance: when a deferred transaction leaves without reaching the Collection, the wrapper judges the enclosing inserts again. Witness `judges enclosing inserts again when a buffered inner transaction is abandoned` fails without it. A durable failure ends the run with a terminal error, outside this law | +| A storage read marked every row the Collection did not hold as a seed, including a row a peer's source wrote in this run and that this tab loads for a new demand | Fixed: the runtime records the keys that peer notices name as written in this run, and stops marking seeds after a notice or catch-up that needs a full reload, because it names no keys. Witnesses `keeps a row another tab wrote this run when a later seed arrives` (named keys, full reload) fail on `e4b562448`; a mutant that ignores a full reload fails the second | +| Query owner supplement (`rowToQueries` in `getOwnersFromMetadata`) | Removed. Each edge Query adds stages its owner set in the same sync transaction, and Query rolls edges back when that transaction fails, so the supplement only covered owner metadata that held transactions hid from reads. The commit-order reader makes it redundant; all 1,486 Query tests pass without it | +| F: Query's owner read lost its applied-metadata fallback for a sync without `metadata` | No action: core always supplies `metadata`, and no wrapper in the repository drops it | +| G: state internals are public for `client.ts` | Accepted design: the seed authority lookups moved to `client.ts` so that an app without `DbClient` ships 130 fewer gzip bytes | +| A later seed merged into an outer re-insert's row by an inner update that commits ahead (random seed 50521469; also fails in core on `743a6ce14`) | Decision 10: the model now counts such a seed in the insert's kind. Witnesses `lateSeedUnderMergedUpdate` and `heldLateSeedUnderMergedUpdate` (core) and `lets an inner update merged over a later seed change an enclosing re-insert` (persisted). The held witness kills a core that keeps the insert's kind after the accepted inner write; without a hold the inner write applies and clears the seed mark, so that mutant is invisible there | +| H: `appliedAdapterMetadataKeys` grows with every deleted key | Fixed: the deleted-key and metadata-key sets became one map, `appliedAdapterKeys`, from each key whose metadata an applied adapter write fixed to whether that write deleted the row. After 5000 keys were inserted and deleted it holds 5000 entries instead of 10000 across two sets; the deleted keys remain, as D4 requires. The hydration authority grid gains `delete,update` cells, which a mutant that loses the returning key's authority fails. `sourceWrittenKeys` in the wrapper is unchanged | +## Fifth review round (second high review) + +A high review of `ad966ec12` raised ten items. The plan of oracle changes +came first: per-key storage-read witnesses (peer metadata, a peer delete, a +held seed, an equal re-insert), a read hold in the persisted grammar with +the `persisted-reads` lane, a public-message witness, and scaling runs. + +| Finding | Disposition | +|---|---| +| R1: a storage read marked as seeds for its whole transaction lets a later seed replace a peer's metadata | Fixed. The read stays one transaction; core keeps seed status only for the keys it lists (`preserveHydrationSeedKeys` may be a key set). Witness `keeps a row another tab wrote this run…` now checks metadata; it failed on `749079331` and passes with the `origin/main` wrapper | +| R1b: a DbClient seed held against a peer's write in this run | Fixed by decision 11; witness `treats a row a peer wrote this run as a source row (replaces a held seed)` | +| R2: validation deletes inside a seed transaction lost delete authority | Fixed by the same per-key rule; witness `keeps a row a peer deleted absent when a later seed arrives` | +| R3: a committed enclosing transaction stays judged against an abandoned buffered write | Refuted on the same path: the outer commit threw the hydration-cycle error, and after decision 12 the inner transaction either flushes or is dropped before the outer one commits | +| R4: writes during a storage read used older metadata rules | Fixed by decision 12: one write path. Witness `clears an explicit metadata write with a later insert during a storage read` and the `persisted-reads` lane (reach `write during a storage read`) fail on `749079331` and on `origin/main` | +| R5: `prependHydrationRows` did not shift partial-update indexes read by the staged-row walk | Resolved by removing the walk: each insert keeps its transaction's row from just before it | +| R6: core honoured `originalSyncType` from any public write | Fixed: the wrapper marks a whole-row update with the internal `syncWriteNamesWholeRow` symbol. Witness `merges a public partial update whatever fields the message carries` | +| R7: seed keys entered `sourceKeys` while staged | Refuted as unreachable: no caller classifies a seed-only row then | +| R8: two sets of keys written in this run | Fixed: the runtime keeps one record, fed by this tab's handoffs and peers' notices. Witness `treats a row a peer wrote this run as a source row (keeps metadata on an equal re-insert)` | +| R9: re-judgment was quadratic | Fixed by the same change as R5: 8000 implicit inserts re-judge in 2.5 ms (2000 took 18 ms). A mutant that judges against the transaction's whole row fails two witnesses and three campaigns | +| R10: readers copied the pending queue on every read | Fixed: they skip invalidated transactions in place | +| S1 (sibling): a commit threw when an on-demand read started during its transaction | Fixed by decision 12; the `persisted-reads` lane found it | + +## Sixth review round (third high review) + +A high review of `ff80a31c3` raised nine items. The oracle changes came +first: a peer-route witness grid (metadata-only notice, a missed notice, a +peer row during an open insert) and stored rows for the `persisted-reads` +lane's read hold, with reach `storage read of a stored row`. + +| Finding | Disposition | +|---|---| +| H1: a peer's metadata-only notice did not count as a source write | Fixed: the runtime records row keys and metadata keys apart (D5 judges them separately). Witness `treats a peer's write in this run as a source write (metadata-only notice over a held seed)` failed on `ff80a31c3` | +| H2: the gap-recovery fallback and term-fence reloads left the run's writes known | Fixed: both mark them unknown. Witness `… (write whose notice was missed)` failed on `ff80a31c3` | +| H3: rows a peer's notice or a storage read landed ahead of an open wrapper insert did not judge it again | Fixed: every internal apply judges the open transactions' inserts again. Witness `… (peer row during an open insert)` failed on `ff80a31c3` | +| H4: a held seed ignores the unknown-writes state that seed marking honours | Accepted design (decision 13): under unknown authorship, what this tab holds wins. The persisted oracle's seed-across-peer-notice cases with absent, older, and equal durable data witness it; a trial that let unknown rows replace held seeds failed eight of them | +| H5: the read lane's storage read returned no rows | Fixed in the oracle: the holding adapter keeps durable rows; the lane reaches `storage read of a stored row` and passes | +| H6: the storage read assumed `begin` opened the transaction it marks | Fixed: it marks seeds only when `begin` opened one | +| H7: the staged-row fold copied the queues on every read | Fixed: it iterates them in place | +| H8: re-judgment scanned every implicit insert on each inner commit | Fixed: it looks up the inner transaction's keys | +| H9: `syncWriteNamesWholeRow` was exported without `@internal` | Fixed | + +## Seventh review round (fourth high review) + +A high review of `e4b81558b` raised ten items. The oracle change came first: +a reload-route grid in the persisted oracle, `keeps previous-run rows seeds +across a reload a peer's notice causes`, which crosses the three reloads a +named-key notice can cause (no `DbClient` seed held, so the reload +truncates core; a held seed, so it rewrites rows in place; more keys than a +targeted update allows) with a full-reload neighbour, and observes a later +seed of a previous run's row, of the row the notice named, and of a key +storage never held. + +| Finding | Disposition | +|---|---| +| F1: a follower merges a targeted insert's row over its own and keeps a removed field | Refuted on the same path: a follower that shows the key reloads it from storage, and `follows durable same-key order for targeted insert replacement` (active and retired demand) passes | +| F2: an inner insert is judged against the enclosing open transaction's row, though it commits ahead onto another | Accepted design (decision 14). Production and the model agree (`nested`, `nested-conflicts`); a probe of the reported trace keeps metadata with no error. The mutant that re-judges accepted inserts, as `30175be48`'s replay did, fails both retention witnesses (fourth round) | +| F3: reloads a peer's notice causes rewrote previous-run rows as source writes, and the reload's truncate counted as a source truncate | Fixed (decisions 9, 11, and 15). A reload marks its transaction's seed keys: rows no source wrote, and under unknown writes rows storage left unchanged. Core keeps authority and seed status across that transaction's truncate, and the reload deletes each dropped row as a source write. The truncate stays, because it publishes each durable row's exact shape (three exact-shape witnesses need it). The grid failed three of four variants on `5013e5aba`; a later seed of a never-stored key was ignored after a truncating reload. Decision 15 added a full-reload variant followed by a new demand's storage read and a row the peer deleted; both full-reload variants failed on `44ef0c2d0`. Mutants: core treating the truncate as a source truncate (four variants fail), no unchanged-seed exemption under unknown writes (both full-reload variants), and no per-key delete of a dropped row (four variants). An earlier repair that validated rows in place lost the exact-shape publication | +| F4: every internal apply re-judged every open implicit insert | Fixed: the runtime passes the keys the internal apply's transaction wrote (every key after a truncate). Ten storage reads beside 4000 open inserts re-judged 40000 times, now none. A mutant that re-judges no keys fails `… (peer row during an open insert)`; no generated lane catches it | +| F5: staging adds a storage read's seed keys to the source-key set that a rebuild excludes | Refuted as unobservable, as R7: a mutant that always rebuilds at acceptance changes no persisted session lane, persisted-oracle case, or core session lane; the wrapper's only classifier call skips keys no source wrote in this run | +| F6: metadata reads scan every pending transaction | Accepted cost of read-your-writes: 10000 reads take 0.7 ms with no held transaction and 4.2 ms with 100 | +| F7: the run's written-key sets grow, and the internal-apply listener outlives cleanup | No action: the sets keep deleted keys because D4 and decision 11 need them, as H; cleanup empties the arrays the listener reads, and the next run replaces it | +| F8: a redundant deleted-key check, and a stored `acceptedSourceKeys` only the rebuild reads | Fixed | +| F9: state internals are public for `client.ts` | Duplicate of round 4's G (accepted design); `any` follows `client.ts`'s own `AnyCollection` convention | +| F10: seed keys are marked before the read's commit | Refuted: the read's begin, writes, and commit run synchronously, its commit takes no signal, a retired run's `begin` opens no transaction to mark (H6), and an accepted transaction always applies | + +## Eighth round (the peer dimension) + +After the fourth high review, the maintainer asked for the next oracle +improvement before another review: a peer in the persisted session grammar. +Every real finding since the fifth round had sat on a peer or reload route +that only pinned grids covered. + +- **Law.** A peer's write in this run is a source write, accepted when this + tab applies its notice, after every accepted transaction and beneath the + open ones (decision 11). A full-reload notice keeps a held `DbClient` seed + (decision 13) and makes stored rows and metadata that differ source + writes (decisions 15 and 18). Reloads keep metadata of a missing row + (decision 16); a write over a seed stores the row this tab shows + (decision 17). +- **Grammar and model.** The `peer` action sends one of four notices: a + named row with its metadata, metadata alone, a delete, or a full reload. + It is legal only when no store hold or read hold is active, so every + accepted transaction of this tab is durable and the notice is the next in + the stream. The model tells cached rows from `DbClient` seeds and keeps + what a reload restores per row: storage's row and metadata, which each of + this tab's commits sets to what this tab shows (decisions 16 to 18). +- **Driver.** A `SingleProcessCoordinator` that delivers the `tx:committed` + shapes the Browser and Electron coordinators send; the holding adapter + keeps rows and collection metadata. +- **Lane and reach.** `persisted-peers` (seeds, the cached row, nesting to + depth 2, store holds, 150 fixed histories). A 150-history sample with the + fixed seed reaches each named notice kind 44 to 69 times, a named write + over a held seed 42, a full reload over a held seed 19, a peer write during + a hold 46, and during an open insert 17; the declared minimum is 5. A + declared state for a full reload that reveals a hidden peer write was + dropped: a write of the row by this tab stores its row over the peer's + (decision 17), so only a named notice reveals it, which `named peer write + over a held seed` covers. + +| Finding | Disposition | +|---|---| +| P1: a reload's held-seed set came from `base`, so it truncated over a seed accepted during a hold (`hold seed(2) peer(1,row)`) | Fixed: held seeds come from core's hydrated keys | +| P2: a named notice for a row only held transactions wrote merged the peer's whole row over this tab's held row (`hold begin upsert(2) commit peer(2,row)`) | Fixed: a row in `base` counts as ambiguous, and so does a held row that the notice would change, so the notice reloads. The first fix checked held rows only and regressed `begin remove(1) hold commit peer(1,row)`; the lane found it at once. The second counted every held row and broke the real-host reload-class oracle's law (e), "a commit the tab already knows never makes it reread rows", on its Browser and Electron drivers: the notices it rerouted carried exactly the held row. A third version reloaded only when the notice's merge changed the held row; a peer's whole row that kept the held `v` and dropped its other fields then kept them. The grammar's row notice now has a variant that keeps this tab's `v` (`peer(…,row,keeps)`), and the replay `a notice whose row keeps a held row's value still replaces it` failed on that version. The final check reloads unless the notice carries exactly the held row, and the reload-class oracle passes on both drivers. Generated histories reach that premise too rarely to declare it (none in 300); the replay holds it | +| An in-place reload merged a stored row over a held seed (`hold seed(2) seed(1,narrow) peer(2,row)`) | Fixed: reloads write each stored row whole | +| A reload deleted again a row a held delete removed, dropping metadata written after it | Fixed: a dropped row is deleted only while core holds it, judged before the reload's truncate | +| F5 (seventh round, refuted as unobservable): staging counted a reload's preserved keys as source writes | Reachable after all: `seed(2,bare) begin reinsert(1) hold peer(2,reload)` judged the open re-insert equal. Fixed in core: staging skips preserved keys, as the rebuild does | +| L1, L2, and the later-seed metadata case | Decisions 16, 17, and 18 (B′) | +| A reload counted unchanged rows' writes as source writes, so metadata a seed governs lost that status | Fixed: a reload's write of an unchanged row keeps its status. A truncate in a storage read that keeps statuses keeps row metadata in core, and `metadata.row.get` reads through it; `_syncedRowMetadataKeysOutsideBase` left core | +| A named row notice did not mark its metadata as written in this run, so decision 18 kept a held seed's metadata over the peer's | Fixed: a peer's row comes with its metadata (decision 11) | +| Driver: the holding adapter dropped collection metadata, and cleared it on truncate | Fixed in the test adapter | + +Evidence: + +- **RED.** Each shrunk history above failed on the production before its + fix; the survey of 2,000 generated histories before any fix found P1 in + 22 and P2 in 9. +- **GREEN.** Four surveys of 1,000 generated histories (seeds 77, 91, 105, + 133) find no mismatch; the fixed and random `persisted-peers` campaigns + and the shrunk histories, kept as replays through the same model, pass. +- **Mutants.** P1 reverted fails both peer campaigns; P2 reverted fails the + fixed one; B′ collapsed to B fails seven cases, two `main` full-reload + cases among them; decision 11's metadata keys removed fails `treats a row + a peer wrote this run as a source row (replaces a held seed)`. Three + mutants survive every campaign and fail only their replay: no seed base + (decision 17), storage metadata always winning (decision 18), and a + dropped-row delete without the held check. The campaigns reach their + premises too rarely; the replays keep them. In core, staging that counts + preserved keys (F5) fails its replay; a status-keeping truncate that + clears row metadata fails nine cases, both peer campaigns among them; and + a metadata reader that stops at such a truncate fails two `replaces a + hydrated seed and metadata with a peer's targeted …` cases. +- **Limits.** The peer is the driver; no second Collection or real + coordinator delivers its notices, so the claim stops at the controlled + coordinator (ORC-014). The next receiving witness is a second production + driver of this oracle with two coordinators over a real channel + (issue #2272). Missed + notices and notices over the targeted key limit are not generated; the + persisted oracle's grids cover them. The adapter is in memory, and restart + stays outside this oracle. + +## Ninth round (medium review) + +A medium review of `2156c9c57` raised ten items. Planned together, they +asked for three oracle changes before any production edit: a peer lane +under full row updates (M1), a reset notice (M4), and a Query storage-scan +witness (M10). + +- **Law.** A durable write stores the row this tab shows, so storage after + settlement equals the model's storage, which a restart reads. Under full + row updates an update replaces its row there too. A reset is a source + truncate; eager it takes held seeds, on demand it keeps them with their + objects' authority (decisions 19 and 20). A notice in a reset's term may + predate the reset, so this tab reloads storage for it (the wrapper's + reset fence). +- **Grammar and model.** The peer notice gains `reset`, which empties + storage. The model's truncate may keep a set of keys, and a reset on + demand keeps the `DbClient` seeds it holds. After a reset every notice + reloads storage. New lanes: `persisted-peers-full` and + `persisted-peers-on-demand`, the second with a demand for every row. +- **Observation.** The peer lanes now compare the holding adapter's stored + rows with the model's after settlement. Before, storage was seen only + when a reload read it back. +- **Reach.** The full-row lane declares `accepted update drops a field`; + the on-demand lane declares `reset over a held seed` with the peer + states. A reset over metadata of a missing row reached 1 in 150 + histories, so a replay holds it instead of a declared state. + +| Finding | Disposition | +|---|---| +| M1: decision 17's seed merge ignored `rowUpdateMode: 'full'` | Confirmed and wider: the wrapper stored every sync update as a merge, so under full rows a reload or restart brought back fields an update dropped, with or without a seed (`main` too). Fixed: only a partial update merges in storage; every other write replaces its row | +| M2: seed marks added at a read's begin | Refuted, as F10 | +| M3: seed metadata applies over a rejected row | Accepted: row and metadata are separate objects (D5) | +| M4: a reset beside a held seed kept orphan metadata | Confirmed and wider: the reset also kept rows a hold kept from `base` and skipped the demand replay its truncate drives; a reload deleted only `base` rows that storage lacked. Fixed: a reset is a source truncate that keeps held seeds (decisions 19 and 20), and a reload deletes every accepted row that storage lacks | +| M5: a status-keeping truncate keeps flags of dropped keys | Refuted: a dropped key is outside the read's preserved keys, so its delete is a source write and clears its flags (the grid's `gone` row) | +| M6: an accepted inner equal insert keeps its kind | Accepted (decision 14) | +| M7, M8 | Duplicates of F9 and F7, accepted earlier | +| M9: the commit wrapper reads the newest pending transaction on every commit | Harmless: `withInternalApply` resets the key set before each use | +| M10: Query's storage-scan owners omit accepted edges | Open; see "Open" | +| Lane: a reset kept a held seed but cleared the authority a peer's metadata write had fixed over it, so a later seed's metadata replaced the peer's | Fixed: the reset's truncate keeps the authority entries of kept keys | +| Survey: a source row write after an on-demand reset closed the kept seed's metadata to a later seed | Fixed: a kept key stays exempt for its metadata; a row write fixes row authority through the applied row | +| Survey: after a full-reload notice, a named notice that the wrapper turns into a reload erased a seed's metadata of another row (`peer(1,reload) seed(2) begin upsert(2) commit peer(1,row,keeps)`, also on `2156c9c57`) | Fixed: a notice that names no keys makes writes unknown per key, not for the rest of the run. A key is known again once this tab's source writes it, and a storage read records each row it finds changed as this run's source write. This tab's own truncate still makes every later write unknown. Ending the unknown state with the reload instead failed two `persisted-oracle` witnesses: an on-demand reload with no demand reads no rows, so a row it never read must still beat a later seed. Marking keys known before the read wrote them failed both oracles widely. Mutants: not recording changed rows fails the fixed campaigns of all three peer lanes; no knowledge from this tab's writes fails the family's replay; knowledge kept across a later full-reload notice fails 16 cases. Knowledge from a read's rows too survived every case and was removed | + +Evidence: + +- **RED.** On `2156c9c57`'s production, ten cases fail: the five new + replays and the fixed and random campaigns of both new lanes. A + replay of the kept-authority case failed on the first M4 repair. +- **GREEN.** Every persisted session lane and replay passes at the + configured runs. Two surveys at four times the runs found two families, + one on the reset path and one that `2156c9c57` already had; both are + fixed and pinned, and two more surveys at four times the runs pass. +- **Mutants.** Killed: an update over a seed merging under full rows; no + seed base for partial updates (now by both partial campaigns, through the + storage check); every update replacing; a reset that keeps no seeds; no + rewrite of a kept seed's row; reload deletes from `base` only; + authority that ignores kept keys; a truncate that clears kept metadata. + For the scoped unknown writes, the committed sticky flag fails the + family's replay, and not recording changed rows fails the fixed + campaigns of all three peer lanes. Survived: a session reader that stops + at the reset's truncate for kept keys. A truncating transaction applies at once, so no read sees it + pending; the reader change was removed. +- **Limits.** The reset's term fence is exercised only through the + driver's single-term peer. The full-row and on-demand lanes cover the + peer grammar, not the other lanes' holds and reads. + +## Tenth round (high review) + +A high review of `18f7df562` raised ten items (H1 to H10). + +- **Law.** A reset's own reload reads metadata through its open truncate + only for kept seeds. What storage holds after a reset is this run's + source writes. A transaction the wrapper abandons before handoff wrote + nothing, so a seed beneath it holds (decision 21); a seed that arrives + after it stays judged against it (decision 22). +- **Grammar and model.** A reset may rewrite its rows: the resetting tab + writes storage's rows back, and the reload applies them after its + truncate. A new `abandon` action aborts the signal of the newest + outermost transaction committed during a read hold; only the read's end + may follow, which abandons it. A transaction begun over a buffered one + reads it and becomes a dependent successor, which the abort-graph oracle + owns, so `abandon` needs none. New lane `persisted-reads-seeds` crosses + read holds with seeds and the cached row, loaded by a demand for every + row. +- **Reach.** `reset that rewrites its rows` and `abort after commit` are + declared. Abandoning the writer of a held seed's row stays below five in + 150 histories; a replay pins it. + +| Finding | Disposition | +|---|---| +| H1: the session reader read through a reset's open truncate, so the reset skipped writing stored metadata equal to what it was about to delete | Confirmed. RED: the rewriting reset's replay and the on-demand campaigns. Fixed: the reader stops at a source truncate for keys it does not keep. Round 9 had removed this as unreachable; the wrapper reads inside its own open reset | +| Sibling: an equal re-insert of a row a reset wrote back cleared its metadata | Fixed: a reset records the rows it restores as this run's source writes | +| Sibling (model): the model kept decision 16's metadata of a missing row through a rewriting reset | Fixed in the model: only rows are durable | +| H2: run writes were recorded before the handoff and kept when it abandoned the transaction | Confirmed. RED: the abandon replay. Fixed: only a transaction handed to core with a live signal is recorded | +| H3: `truncate` ignores an invalid open transaction | Accepted design (decision 7); a mutant that revives it fails the nested and conflict campaigns | +| H4: a read's seed marks and run-written rows stay if its transaction is discarded | First half refuted earlier (F10). Second half true but harmless: under unknown writes a changed stored row is this run's source write whether or not the read applies (decision 15) | +| H5: the targeted-invalidation seed checks are always false | Confirmed by source: the notice's keys are recorded first. Removed | +| H6: the hydration binding's cross-cycle throw and its assert are unreachable | Confirmed by source. Removed with their `try`; the assert at the handoff stays | +| H7 | Duplicate of G (accepted design) | +| H8 | Duplicate of F7; the newer known-key set is cleared at each full-reload notice | +| H9: `appliedAdapterKeys` keeps an entry for nearly every synced key while a `DbClient` tracks authority | Accepted: per-object seed authority needs it (D4, D5); the map exists only under `DbClient` tracking | +| H10: the session reader walks every pending transaction | Observation, no performance law: pending transactions accumulate only while an optimistic transaction holds sync | +| Survey: a seed after a buffered write that is then abandoned | Decision 22; the model records each seed's losses before abandonment | + +Evidence: + +- **RED.** On `18f7df562`: H1's replay and the on-demand campaigns; the + re-insert sibling's replay; H2's replay. +- **GREEN.** Every persisted session lane and replay; three searches of + `persisted-reads-seeds` at four times the runs. +- **Limits.** The abandon action covers one buffered transaction with no + successor; dependent suffixes belong to the abort-graph oracle. +- **Files.** Under `oracle/no-unit-test` (#2278), the session model module + declares its check, model, and grammar. Its fixed boundary tests moved to + `packages/db/tests/sync-metadata-session.test.ts`, and the persisted fixed + witnesses to + `packages/db-sqlite-persistence-core/tests/persisted-sync-metadata-session.test.ts`. + +## Open + +- **Held-cleanup retention witnesses race on CI.** `does not publish a + retention-cleaned owner through a claim made during the cleanup hold` and + `hydrated clients drop a held-cleanup row once its peer and claimant + release it` (Query ownership lifecycle) sometimes see no expiry + transaction staged within 5 seconds of the unload while an optimistic + blocker holds sync. They failed on CI at `1c7624710` and `9ec84d81c` and + passed on the rerun of `9ec84d81c`, at `c065ab5ec`, and at `bc8f5f3d2`; + locally they pass five times in five at load average 43. The witnesses + without a hold pass in under 120 ms. Refuted: a Node timer firing before + the entry's wall-clock deadline (a timer set after a 50 ms busy task fired + 12 ms later), and a wait on a held receipt (no step of the expiry awaits + one). The witnesses now trace each timer Query DB sets during the test + (where it was set, whether it fired, what cleared it) and each Query + fetch, and a failed retention wait prints that trace. With it, the + witnesses passed on CI at `543d29066` and in four local runs of CI's + collections command. Deferred by the maintainer on 2026-10-27: the pull + request merges with the race open ("it'll eventually fail at which point + we can fix it"). Resume when a failure reports its trace; the trace + names the step to fix, in production if a valid expiry was cancelled, or + in the witness if only its timing raced. + +- **Query storage-scan owners (M10).** `getOwnersFromMetadata` no longer + adds this tab's accepted edges, which matters only where it reads a + storage scan: `loadPersistedBaselineForQuery` for a retained Query whose + known rows are not all in the Collection. Three witnesses (a held scan + beside another Query's claim; a retained Query with a stored row, and with + a row moved out of its predicate) each took the in-memory baseline, which + includes the claim. The scan and every storage hydration hold the apply + lock, so a claim during a held scan waits. Not excluded: a claim + committed during a storage read, which the wrapper buffers (decision 12), + with a retained Query's scan before the buffer flushes. That history is + the missing witness; no production change without its RED. + +Nothing else in this bug class is known to be open. The limits in the +evidence block still apply. + +## Oracle guide requirements + +| Requirement | Outcome | +|---|---| +| ORC-001 | Met: decisions and sources above; limits in the module's opening prose. | +| ORC-002 | Met: the model folds an event log; production scans queued write sets. | +| ORC-003 | Met: contract, model, grammar, driver contract, and comparison are separate sections with prose. | +| ORC-004 | Met: witnesses are reconstructed by the grammar; the ablation test shows each axis is needed for its regime; range probes added prefix-boundary keys (new values only) and a seed field (a missing merge rule, now modelled); exclusions (`remove` or `reinsert` of an absent row) are stated. | +| ORC-005 | Met: real sync API, `DbClient.hydrate`, and the persisted wrapper; observations at each checkpoint. | +| ORC-006 | Met: production on `30175be48` and the temporary reader mutants. | +| ORC-007 | Met: fixed and random campaigns per lane with replay properties registered in `oracle-config.ts`. | +| ORC-008 | Met: frames, accepted queue, seeds, and hold each change an observation (see the witnesses). | +| ORC-009 | Met: frames are sync transactions, each independent (decision 6); the model's serialization order is the order a read and an application use. | +| ORC-010 | Met: the runner preserves the mismatch and aggregates a cleanup failure. | +| ORC-011 | Met: three properties compare production with itself, without the model's reads: hold invariance (holds removed), key independence (row 2's actions removed), and seed order (seeds performed first, decisions D4 and D5). A shared fault in the model's reads cannot pass them. | +| ORC-012 | This record. | +| ORC-013 | Met for hold kind (store-hold neighbour passes, core hold fails on `30175be48`) and nesting (inner commits during a hold fail on `30175be48`; outer aborts and unheld inner truncates pass there). | +| ORC-014 | Met: the persisted lanes receive the flat, nested, and seed laws through the wrapper. Restart and resume belong to other owners. | diff --git a/docs/contributing/oracle-reviews/2026-10-23-convex-pagination-and-codecs.md b/docs/contributing/oracle-reviews/2026-10-23-convex-pagination-and-codecs.md new file mode 100644 index 0000000000..147e2efcd4 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-23-convex-pagination-and-codecs.md @@ -0,0 +1,105 @@ +# Convex pagination and mutation codec review + +Reviewed source and test head: `6511ee4ed0940cd54ee58ceeba7202ca859344b3`. +This record reviews these owners against [ORC-001 through ORC-014](../oracle-rubric.md): + +- `packages/convex-db-collection/tests/pagination-oracle.property.test.ts` +- `packages/convex-db-collection/tests/values-oracle.property.test.ts` +- The opt-in codec property in `packages/offline-transactions/tests/transaction-serializer-oracle.property.test.ts` + +The pagination review is independent of its implementation author. The reviewer +also inspected the value models and codec dispatch independently, then repaired +replay routing and cleanup mechanics. That participation is not an independent +review of those harness repairs. This is not a new audit of every historical +serializer law. Source acceptance and snapshot ownership have separate reviews. + +No known product counterexample remains in the bounded domains below. Passing +these histories does not establish an on-demand API, a lifetime resource bound, +or arbitrary storage and process behavior. + +## Executed evidence + +At the reviewed head, the reviewer ran the pagination and Convex values owners, +including their normal fixed and random campaigns. The pagination file has ten +checks, including cleanup calibration. The Convex values property runs 100 +histories per campaign. The shared serializer file passes all 51 checks with +`OFFLINE_CODEC_ORACLE_SEED=20261022` and +`OFFLINE_CODEC_ORACLE_PATH=0:0:0:0`. This full-file command directly replays the +codec property without applying its path to unrelated properties. + +Before the selector repair, the same path under the shared `OFFLINE_ORACLE` +prefix made four unrelated properties fail with `Unable to replay, got wrong +path`. That is a replay-routing failure, not a product mismatch or checker kill. +The dedicated prefix repairs that failure. Other legacy serializer properties +keep their existing selectors. + +The pagination author recorded these assertion failures at their intended +observations, with repaired replays passing: + +| Wrong behavior | Seed and path | Observation | +| --- | --- | --- | +| Reverse concatenated pages | `20261022`, `0:1:0:2:3:1:1:1` | Ordered rows received by `select` | +| Publish a prefix while another range is missing | `20261022`, `0:1:3:1:2:5:7:3:3:0:1:1` | Previous public snapshot while acquisition is pending | +| Copy incomplete prefixes during initial acquisition | `20261022`, `0:0:0:0` | Provider row reads at first exhaustion | +| Omit the accepted-row cap | `20261023`, `0:0` | Error and retained public rows after growth beyond the cap | + +The prior codec calibration is recorded in the coverage map: a decoder that +leaves Convex scalar wire markers as JSON fails the typed-value observation at +`CONVEX_VALUES_ORACLE_SEED=20261022`, path `0:0`. This review inspected the +unchanged Convex encoding and observation boundary, and reran the repaired owner. +It does not claim to have rerun that production mutant after the head was frozen. + +## Requirement outcomes + +| Requirement | Outcome and evidence | +| --- | --- | +| ORC-001, authority and limits | Satisfied for the stated domains. Pagination follows the documented complete eager mirror, `maxRows`, and Convex's fixed `endCursor` and split semantics. The row-read check is a deliberately narrow acquisition cost guard, not a total complexity promise. Codec meaning follows the opt-in `MutationValueCodec` and version-4 wire contract. Metadata retains the existing version-3 contract. The Convex receiver uses its public scalar encoding. Limits are stated in the files and below. | +| ORC-002, independent judgment | Satisfied. Pagination expects authored rows in mathematical rank order, without using production cursor bookkeeping. The numeric interval provider answers requested intervals independently. Convex values are compared with their authored native trees through a type and byte observation. The shared codec fixture uses Node V8 serialization, while expected native values come directly from generated leaves. Neither codec model uses production dispatch to compute its expected result. | +| ORC-003, literate responsibilities | Satisfied within this scope. Pagination explains the complete snapshot law, range fixture, histories, public and select observations, pending cut, and work/cap comparisons beside the code. The Convex values opening distinguishes authored tree, storage driver, fresh reader, and typed comparison. The shared codec section separates its opt-in law, V8 fixture, native grammar, and storage/error comparisons from the older serializer model. | +| ORC-004, grammar controls | Satisfied within the declared bounds. Pagination varies 0-8 distinct rows, 1-10 snapshots, page sizes 1-4, both callback directions, and no/required/recommended splits. Work varies 3-40 pages and 1-8 rows per page. Cap histories vary limits 1-20, page sizes 1-6, and excess 1-6. Empty results distinguish readiness from nonempty data; row removal and movement distinguish complete replacement from append-only accumulation; split modes distinguish replacement from continuation; callback direction removes delivery-order dependence. Split cursors are asserted strictly inside the returned range, excluding the formerly illegal page-size-1 split. Convex values use depth-two trees, arrays up to three, bytes up to eight, bounded strings, signed int64s, special floats, undefined fields, and six marker-like field names. Removing a native leaf would remove evidence for that codec branch. Shared codec histories use 1-5 native leaves, bytes up to twelve, and each mutation kind, plus missing/wrong/throwing readers and a non-string encoder. Cycles, custom native objects, and arbitrary malformed server protocols are not legal histories in these grammars. | +| ORC-005, production path and observation | Satisfied. Pagination uses a real Collection and adapter with controlled provider cache callbacks. It compares public keyed rows and ordered `select` input after exhaustion, and both while a new range lacks a result. The pending pump drains scheduled flushes before checking, so it reaches incomplete acquisition rather than filling new ranges first. The work recorder counts numeric provider element reads before explicit cache refresh. Codec drivers use actual OutboxManager writes and fresh readers; all three mutation payloads are compared. The shared owner also checks registry identity, wire version/codec ID, metadata, mixed legacy records, and byte-for-byte retained storage after failed reads. No concrete missing-input hypothesis remains for these named cuts under the stated provider premises. | +| ORC-006, calibration | Satisfied with the recorded controls above. Pagination's wrong page order, premature prefix, extra row reads, and absent cap each fail the relevant assertion. The typed-value receiver rejects scalar-as-JSON normalization. The original default serializer's BigInt admission failure and repaired codec replay are recorded in the coverage map. Replay-routing failures are reported separately and are not counted as product calibration. | +| ORC-007, campaigns and replay | Satisfied. Each generated property normally runs matching fixed and random budgets. Pagination has separate `CONVEX_PAGINATION_ROWS`, `CONVEX_PAGINATION_WORK`, and `CONVEX_PAGINATION_BOUND` seed/path selectors, each with 60 runs. Values uses `CONVEX_VALUES_ORACLE`, with 100. New shared codec replay uses `OFFLINE_CODEC_ORACLE_SEED/PATH/RUNS`, defaulting to 60. Their direct replay branches bypass the normal two campaigns for the selected property. No reviewed property uses `fc.commands`, so command `replayPath` handling is not applicable. | +| ORC-008, state minimality | Satisfied for pagination. Its last accepted input array is necessary: a missing replacement range must preserve that array, and a later completed acquisition replaces it. Two different accepted snapshots are distinguished by the next pending-range observation. Provider slot/cache state drives production and is not the reference state machine. Not applicable to the value models, which compare one authored tree with one restored tree rather than reducing a stateful history. | +| ORC-009, vocabulary | Satisfied. Collection rows, public snapshot, source result, acceptance, and cleanup retain the glossary's meanings. Numeric rank intervals are an explicit fixture abstraction of opaque provider cursors, not production cursor parsing. The work recorder measures element reads, not network work or subscription count. Codec wire version and codec ID are distinct from the application's transaction version and named mutation function. No hidden production-state equivalence is introduced. | +| ORC-010, failure fidelity | Satisfied for the reviewed generated properties. Pagination uses the shared test cleanup instrument, whose double-fault calibration preserves the original comparison as AggregateError cause, keeps secondary failures, and attempts a later release after an earlier release throws. Values and new shared codec catch and rethrow the primary failure, then use the existing cleanupOfflineOracle instrument to attempt both Collection releases. That instrument logs secondary cleanup failures without replacing an existing primary. The reviewer inspected this control flow; the reviewed value properties do not inject cleanup failures themselves. | +| ORC-011, second formulation | Satisfied where a concrete shared-fault hypothesis applies. A numeric controlled provider could implement the same wrong cursor semantics as production, so the actual backend independently evaluates an unpaginated query and compares it with real cursor-range results, including actual split responses. A JSON-normalizing recorder could hide native value loss, so typed scalar/byte observation distinguishes that hypothesis, with native values also received by process/backend tests. No further specific shared semantic fault was identified that calls for another model. This is not an exhaustive absence claim. | +| ORC-012, review evidence | Satisfied by this versioned record tied to the exact reviewed source/test commit. It lists each requirement, concrete checks, controls, and remaining limits. The record reviews the pagination bug class at complete exhaustion, pending acquisition, ordered selection, initial element-read, and cap/error cuts. It reviews codecs at durable admission and fresh read, with all payloads and unreadable-record retention. Known limits below are not presented as closed classes. | +| ORC-013, distinguishing boundary witnesses | Satisfied within the promised boundaries. Generated cap histories first accept exactly `maxRows` rows, then reject excess, retain the previous snapshot, and recover after shrink. A separate pinned default test distinguishes 10,000 from 10,001, and invalid 0, negative, fractional, infinite, and NaN caps reject before subscription. Work's three-row/page-size-1 failure distinguishes copying an incomplete prefix from the one complete materialization. Codec histories distinguish valid binding from absent/wrong/throwing binding while requiring unchanged durable bytes. These are not claims about arbitrary resource thresholds. | +| ORC-014, controlled-premise handoff | Satisfied with explicit limits. The controlled pager assumes Convex fixed ranges and legal split responses; real local backend tests exercise both actual SDK clients, fixed ranges, and server-generated splits against an unpaginated query. The native tree/storage driver assumes preservation across serialization and fresh readers; backend crash tests receive native payloads on disk and across process death, now on insert and update. These receiving drivers use provider-specific controls and separate expected server queries/application state rather than literally reusing the numeric fixture grammar. Their shared law is documented, but arbitrary cloud scheduling and every generated native tree are not thereby received. | + +## Remaining limits + +The eager cap bounds accepted rows, not network bytes, peak memory, or lifetime +cursor ranges. A reactive range can itself return more than the cap. Old ranges +remain subscribed, so long-running insert/delete histories can accumulate ranges +even while the current row count remains small. Initial acquisition still scans +cached ranges repeatedly. Only row materialization, not total work, is bounded +by the work property. On-demand filtering, ordering, demand release, and a bounded +subscription policy need a separate API and receiving witnesses. + +The numeric interval grammar assumes coherent source revisions and finite +acquisition. It does not prove continuous growth, cross-Collection publication, +or arbitrary contradictory cursor responses. The backend is a real local Convex +backend, not evidence for cloud availability or network latency. + +The direct Convex value receiver uses one update-shaped mutation, checking +modified, original, and changes. Mutation kinds are crossed by the shared owner, +and CRUD is received by the backend process tests. This does not prove every +native tree through every operation and storage implementation. The reviewed +values are bounded acyclic trees; identity sharing, cycles, cross-realm natives, +custom objects, native Temporal, browser storage atomicity, and power-loss +atomicity are outside this evidence. These limits remain with the codec and +backend owners in the coverage map. + +## CI type-check follow-up + +CI exposed an omitted type-level selector entry in the shared config reader: +`OFFLINE_CODEC_ORACLE` worked at runtime but was absent from the accepted prefix +union. The offline package type check reproduced TS2820 before the repair. +Adding that exact prefix to the union makes the type check pass, and the same +full-file codec replay at seed 20261022/path 0:0:0:0 still passes all 51 checks. +This follow-up changes only a test configuration type, no production code, +model judgment, generated history, runtime selector routing, or comparison. +The exact-head semantic review above continues to refer to its stated source +revision rather than claiming a fresh independent audit of this type edit. diff --git a/docs/contributing/oracle-reviews/2026-10-23-convex-source-and-backend.md b/docs/contributing/oracle-reviews/2026-10-23-convex-source-and-backend.md new file mode 100644 index 0000000000..484b6af507 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-23-convex-source-and-backend.md @@ -0,0 +1,130 @@ +# Independent review of Convex snapshot, ownership, bridge, and backend oracles + +Reviewed implementation head: `6511ee4ed0940cd54ee58ceeba7202ca859344b3`. + +The reviewer did not author these semantic models. The reviewer changed their +cleanup harness after finding that cleanup could replace a primary mismatch. +Pagination is excluded from this independent verdict because the reviewer +implemented its changes; another reviewer covers that owner. + +## Scope and observations + +- Snapshot owner: `packages/convex-db-collection/tests/snapshot-oracle.property.test.ts`. + Last valid keyed source array, including absent fields and duplicate rejection, + through raw arrays and a successful uppercase text projection. Observations are + public keyed rows and error presence after synchronous delivery. +- Ownership owner: `packages/convex-db-collection/tests/ownership-oracle.property.test.ts`. + Concrete option identity admission and independent fresh/descriptor owners, + across idle, ready, and cleanup checkpoints. Observations are construction + rejection and public rows after cached mutation confirmation. +- Bridge owner: `packages/convex-db-collection/tests/offline-oracle.property.test.ts`. + Ordered stages and retry classification, plus cold real-Collection source + acceptance and rejected durable acceptance through cleanup/restart. Observations are stage traces, settlement, provider subscription + admission, and public rows before/after the first source delivery. +- Backend receiving owner: `packages/convex-db-collection/e2e/backend-oracle.test.ts`. + Real range pagination and durable insert/update/delete replay through process + death. The replay observations are durable receipt key, server application + counter, complete server fields, and complete replayed Collection fields. +- SDK receiving support: `packages/convex-db-collection/tests/sdk.test.ts`. + Both SDK clients receive subscriptions, mutation timestamp transitions, + reconnects, and sibling listener cleanup. These focused tests do not own + another independent state model. + +Fresh independent execution before the final freeze passed 28 checks across +snapshot, ownership, bridge, SDK, and pagination support. The author then ran +the new durable-acceptance rejection property with 60 fixed and 60 random +histories and direct replay after the independent finding described below. +The final source tree is pinned above; this record adds review evidence after +that implementation commit without changing its production or test sources. + +The backend author reports all nine final backend tests green after the adapter +freeze, including 36 generated process histories. Insert replay at seed 20261022, +path 0:0:0:1, and update replay at seed 20261022, path 0:0:0:0:0, pass with their +operation-specific selectors. The source acceptance, snapshot, ownership and +backend evidence below distinguishes independent source inspection and fresh +review execution from the author's hostile-control calibrations. + +## Requirement outcomes + +| Requirement | Snapshot | Ownership | Bridge, including cold source | Real backend receiving owner | +| --- | --- | --- | --- | --- | +| ORC-001, authority and limits | Complete keyed snapshots and select follow the public adapter guide. Transport and mutation timing are separate boundaries. | The concrete-options claim follows the established LocalStorage policy and documented adapter ownership. Arbitrary Collection lifecycle is excluded. | The guide promises ordered mutation then source acceptance, with terminal application errors and retryable post-confirmation sync failure. Storage/leadership stay with offline-engine owners. | Convex transactions and the application's atomic receipt contract authorize one application per durable key. Finite pagination must equal an independent unpaginated query. Cloud availability, multi-tab leadership, and power-loss writes are excluded. | +| ORC-002, independent judgment | Last valid input array and pairwise duplicate-key judgment do not compute adapter sync operations. The selected reference applies the explicit input transformation. | Identity and materialization form determine admission; no production claim flag or sync state is imported. | A dependency prefix determines stage traces. Cold source expectations depend on whether a valid snapshot was delivered, not adapter state. | Expected task state comes from the operation and draft. An unpaginated server query judges range results, and a counter distinguishes equal final rows after duplicate writes. | +| ORC-003, literate responsibilities | Opening prose explains contract, validity model, grammar, production path, comparison, and exclusions. | Opening prose maps identity forms and checkpoints to admission and mutation confirmation. | Separate prose explains stage-prefix judgment and cold source acceptance, their histories, cuts, and limits. | Opening prose separates pagination and process-replay judgments, SIGKILL cuts, server and Collection observations, and excluded boundaries. | +| ORC-004, grammar controls | Keys a/b/c, text up to 12, optional text up to 6, 0-5 rows and 1-30 snapshots. Empty, changed, absent, duplicate, reordered and reintroduced keys are reconstructible. Raw/projected variants distinguish missing projection. Duplicate snapshots are rejected atomically rather than admitted as valid rows. | Four identity forms cross idle/ready/cleaned-up and text up to 16. Direct/shallow forms must reject, fresh/descriptor forms must admit. Removing reuse or sibling cleanup loses distinct ownership witnesses. | Stage failure chooses none/args/mutation/sync, with keys/text up to 15. Cold histories choose idle/pending/startup-error and empty/rows/error/cleanup delivery. Rejected durable acceptance crosses empty/nonempty snapshots, 1-4 unchanged/changed callback retries, and a cleanup/restart cut. Delivery is irrelevant after failed startup, which is explicitly terminal at that cut. Empty success and rejected startup distinguish readiness from nonempty rows. | Insert/update/delete cross acknowledgement known/uncertain, text up to 12, rank -50..50, native signed int64/special float/0-12 bytes on insert/update. Delete has no payload. One pending transaction excludes batch and shared-key leadership histories. Child checkpoints enforce admission before offline SIGKILL and server acceptance before uncertain SIGKILL. | +| ORC-005, path and checkpoint | Real Collection plus adapter receive controlled callbacks; keyed public rows and error presence are checked after each delivery. | Real constructors, DbClient materialization, adapters, optimistic transactions and public rows run. Actual SDK sibling tests receive the provider multi-listener premise separately. | The stage driver runs the public bridge with explicit collaborators. The cold driver runs the public bridge with actual Collections and proves pending settlement and one subscription before delivering source data. The acceptance-rejection driver defers actual core commit behind durability, matching persistence's withAcceptedReceipt boundary, and checks rejection before and after retries, then successful fresh-run rows. | Both actual SDK clients run against the isolated local backend. Process replay uses fresh child processes and disk storage. Parent reads the durable key independently; child checks public fields only at replay completion. | +| ORC-006, calibration | Omitting projection is rejected at public row comparison, seed 20261022/path 0:1:1:0:3:4:3:7:7:7:7:7:6. Existing deletion and full-row mutants separately calibrate omitted keys/fields. | Original reuse failed at admission, seed 20261022/path 2:0:0; the two-owner confirmation regression additionally exposed stale first-owner rows. | Original argument failure escaped terminal classification, seed 20261022/path 5:1:1. Original cold acceptance resolved before delivery, seed 20261023/path 0:0:0. Removing terminal durable-failure guards fails at rejection observation, seed 20261023/path 0:0. | Duplicate effects with one receipt fail at applicationCount, insert seed 20261022/path 0:0:0:1. A substituted durable key fails at receipt-key equality, update path 0:0:0:0:0. Dropped replay-only payload fails child public-row deepStrictEqual at the same update path. | +| ORC-007, parity and replay | Identical property runs 150 fixed and 150 random histories; CONVEX_SNAPSHOT_ORACLE_SEED/PATH directly selects replay. | Identical property runs 60 fixed and 60 random histories; CONVEX_OWNERSHIP_ORACLE_SEED/PATH directly selects replay. | Stage, cold source, and rejected-acceptance properties each run 60 fixed and 60 random histories. CONVEX_OFFLINE_ORACLE, CONVEX_OFFLINE_SOURCE_ORACLE, and CONVEX_OFFLINE_ACCEPTANCE_ORACLE keep their seed/path bindings separate. | Each operation runs six fixed and six random histories against the same driver. CONVEX_BACKEND_INSERT_ORACLE, CONVEX_BACKEND_UPDATE_ORACLE, and CONVEX_BACKEND_DELETE_ORACLE each own their seed/path, preventing another operation's grammar from interpreting a recorded path. | +| ORC-008, model state | Retained last-valid rows are needed: a duplicate-key rejection followed by observation distinguishes replacement from preserving prior rows. No queue or transport state enters the model. | No stateful reference machine is introduced; admission is a direct identity-form judgment. Lifecycle checkpoints are production driver conditions. | The prefix computation is stateless. The cold source judgment distinguishes no valid source from a delivered valid source; these states allow different successful settlements. Rejected acceptance has failed-run/restarted-run reference states: cached or changed callbacks do not restore authority, while cleanup/restart does. | No stateful reference machine is introduced. The expected operation result and counter increment are recomputed independently from one logical write. | +| ORC-009, vocabulary | Source input and public Collection rows are named separately; retained expected rows mean the latest valid source snapshot. | Concrete owner identity, sync-run cleanup and DbClient materialization are explicitly distinct. | Source acceptance is distinct from visibility held by an optimistic transaction. Idle/pending/startup-error are declared driver starts, not a replacement Collection status enum. | Offline replay is distinguished from pagination replacement. Admission, server acceptance, replay completion, receipt key and application counter name concrete process/server observations. | +| ORC-010, failure fidelity | Shared withTestCleanup retains the primary failure and secondary release errors. | The helper enumerates admitted Collections at cleanup time and attempts every release. | The real-Collection driver's cleanup uses the same preservation helper; the stage driver owns no disposable resource. | The shared preservation helper protects backend bodies and attempts both client close and temporary storage removal. Child timeout/runtime failures are setup/process failures, not successful mutant kills. | +| ORC-011, second formulation | No concrete shared semantic-fault hypothesis remains after pairwise validity and direct source-array comparison; another copy is not needed. | No named shared semantic-fault hypothesis calls for another identity formulation. Real SDK support is a receiving witness, not a second ownership model. | No named shared semantic-fault hypothesis remains for the dependency prefix; the real-Collection receiving path repairs the prior stub assumption at a different boundary. | Equal final task rows could hide duplicate applications. The non-idempotent counter is the independent distinguishing formulation, while durable receipt-key equality also rejects key substitution. | + +## Bug-class boundary and calibration + +The original cold-replay defect violated source acceptance at +`bridge × idle/pending/failed startup × real Collection adapter × replay settlement`. +The repaired source driver rejects premature fulfillment before the first valid +snapshot, accepts empty snapshots, and distinguishes provider-error same-run +recovery from cleanup invalidation. Independent review found an adjacent hole: +a rejected durable proposal could leave the identity fast path clearing its +error on the same cached rows. The receiving fixture now defers core commit +behind durability rather than artificially rejecting after core acceptance. +Empty/nonempty proposals, one to four unchanged/changed retries, and a fresh +cleanup/restart distinguish terminal failed-base authority from provider recovery. +The nonterminal-guard mutant fails at the rejection checkpoint and repaired +fixed/random campaigns and direct replay pass. No known reachable counterexample +remains in these declared cells; no universal concurrency proof is claimed. + +Backend calibration rejected all three hostile controls at intended observations: + +- Applying a write twice while retaining one receipt fails the non-idempotent + application counter, insert seed 20261022/path 0:0:0:1. +- Substituting `wrong-durable-key` fails equality with the admission record's key, + update seed 20261022/path 0:0:0:0:0. +- Omitting payload only from the replay child's select projection fails its + complete public-row comparison, with server payload still correct, using that + same update replay path. + +These were assertion failures after the legal target checkpoint, rather than +setup failures or timeouts. The author restored every mutant in finally and +reran the repaired operation-specific direct replays. The parent independently +reads durable bytes and server effects; the replay child observes its Collection. + +## Remaining limits and verdict + +The cold source driver establishes real Collection acceptance with a controlled +provider, not a cloud or local-backend cold replay. The backend replay fixture +preloads its Collection. SDK listeners receive the actual SDK cache and listener +ownership premise. None of these observations claims cross-Collection atomic +publication, continuous unbounded pagination, native browser storage power-loss +atomicity, or arbitrary coordination histories. + +No confirmed contract defect remains within the reviewed boundaries. This is a +bounded verdict, not a proof of every legal provider or scheduler history. + +## Additional numbered requirements + +| Requirement | Snapshot | Ownership | Bridge, including cold source and rejected acceptance | Real backend receiving owner | +| --- | --- | --- | --- | --- | +| ORC-012, exact-head review evidence | This versioned record names the reviewed head and every applicable outcome above. Missing projection was challenged by a hostile control, not only by a new happy-path example. | The record separates concrete admission from SDK receiving evidence and bounds the claim to the generated identity forms and checkpoints. | The bug-class boundary, original early success, adjacent rejected-durable-base counterexample, wrong-design rejection and remaining provider limits are explicit. No unresolved reachable in-scope trace is known. | The record names the real host, legal SIGKILL cuts, independent effects, exact calibration observations, replay bindings and excluded coordination/power-loss boundaries. | +| ORC-013, distinguishing reusable boundaries | One valid key is admitted; two equal keys reject the complete snapshot and preserve the prior valid array. An absent optional field removes it after a prior present field, rejecting partial-row retention. Nonidentity projection rejects an identity-selector implementation. | Same concrete owner rejects before and after cleanup; fresh options and separate DbClients admit at those same checkpoints. The cleaned-up reuse case rejects the wrong rule that cleanup retires the options owner. | Pending first source cannot fulfill, while valid empty source does. Argument/mutation errors are terminal while post-confirmation sync errors remain retryable. Provider error can recover on a later valid result; rejected durable acceptance stays failed across unchanged/changed retries until a fresh run. Removing the terminal guards is rejected. | Two server attempts with one durable key produce one logical application. One receipt alone is not sufficient: the duplicate-effect mutant still leaves one receipt yet fails the counter. A same-count wrong-key receipt fails exact key comparison, and a server-correct/public-stale payload fails the child's public-row comparison. | +| ORC-014, controlled-premise handoff | Controlled arrays and successful projection exercise the adapter semantic boundary. SDK tests receive actual Convex decoding and listener delivery; backend select projection receives real documents. They are narrower receiving examples, not copies of this full generated source grammar. The source-array owner does not claim provider scheduling coverage. | The controlled fixture supplies cached data before the scheduled callback. Both actual SDK clients receive that cache window in sdk.test.ts. New sibling tests clean up either listener, then supply the next protocol transition and check the survivor, supplying the exact multi-listener premise the controlled fixture cannot. | Stub stage traces claim collaborator ordering/classification only. Cold and rejected-acceptance drivers use real Collections with controlled callback and receipt boundaries. The receipt fixture delays actual core commit behind durability, matching persisted.visibleAfterDurable. These drivers do not claim native SQLite failure or backend cold-replay coverage. SDK tests separately receive query/mutation timestamp ordering. | This owner receives actual local Convex query and mutation behavior and actual process death/storage reads. Its online and single-leader collaborators deliberately limit the coordination premise; native multi-tab election and OS online detection are not claimed. It does not reuse the bridge's stage-prefix model because durable restart, counter effects and server receipts are a different law and observation boundary. | + +The SDK receiving examples use fixed protocol histories to observe the SDK's +cache, mutation timestamp and listener ownership premises. They do not share the +snapshot generator because their purpose is to receive those timing/ownership +conditions rather than repeat the complete snapshot law. Their fixed expected +selected text is derived from the explicitly supplied server text and projection. +The drift risk is that these examples do not receive every generated snapshot +shape; the generated owner remains the authority for those shapes at its stated +adapter boundary. Real-backend pagination and durable rows add provider-boundary +coverage without claiming the SDK fixture proves cloud availability. + +The manually deferred receipt is limited to the documented acceptance interface. +A Convex Collection wrapped by a real SQLite persistence adapter failing its disk +write has not been exercised by this integration. That native combination belongs +to the persistence receiving boundary, not a claim made by this controlled driver. +The coverage map retains the persistence and offline-engine owners for their +storage, leadership and settlement laws. + diff --git a/docs/contributing/oracle-reviews/2026-10-23-ssr-review.md b/docs/contributing/oracle-reviews/2026-10-23-ssr-review.md new file mode 100644 index 0000000000..b6bb1b49ab --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-23-ssr-review.md @@ -0,0 +1,585 @@ +# Second high review of SSR preparation — 2026-10-23 + +Reviewed production: `f374bdbb12c626a393c920a111ab892e905646f9`, PR #2229. +Repair production and tests: `44ee3f002f4db8b4c6b3987d8c821047762c4a1a`. +This record reviews that repair without changing its production or test blobs. +The raw review contains ten findings: seven correctness, one test-integrity, +one efficiency, and one simplification. The reporting user did not verify them. +No proposed fixes accompanied the findings. This evaluation verified each one. + +## Laws, experiments, and inference + +The server render and the first browser view must agree in projected rows and +observable readiness. The SSR guide supplies that promise. The maintainer chose +programming-error rejection for server reads without reproducible transport. +Browser writes, source-loading rows, and a preload before browser hydration are +valid operations. They cannot become SSR programming errors. Runtime source +errors keep their ordinary error surface. Active-cleanup rejection retains the +separate approved contract. + +Prepared ready data must also let non-streaming Suspense finish. The old model +forced source-prepared queries to report loading. That protected one transport +mechanism but contradicted prepared Suspense completion. Before production edits, +the independent model changed its prediction for a prepared ready source: + +| Case | Old prediction | Joint contract prediction | +| --- | --- | --- | +| Prepared ready source, no query capture | Server and browser loading | Render captures ready query rows/status. Server and browser ready. | +| Prepared loading source | Loading rows | Loading rows, captured at render. A later explicit preload still waits. | +| Normalized rows in a browser without query capture | Loading | Loading. Source rows alone do not prove query readiness. | + +The repaired model does not infer browser Collection readiness from normalized +rows. It requires a query-result capture of the actual server view. React and +Svelte both rejected the previous implementation under this model before any +production repair. This is a repair of the joint contract, not an assertion +rewrite to accept new production output. + +| Law and findings | Previous evidence gap | Decisive experiment | Repair and neighboring guarantee | +| --- | --- | --- | --- | +| Initial-view equality, SR03/SR08/SR09 | Missing server-write/browser-write dimensions and a false Svelte receiving path. | Real React hydration rejected browser activity. Real Svelte hydration returned later source rows. Browser-guard-only mutants still failed both hosts. | Capture query rows/status at render in existing DbClient query records. Preserve first hydration view across a same-query local preload. Commit can publish later live rows. | +| Prepared Suspense and portable identity, SR01/SR02 | Forced-loading model and a warm-result identity bypass. | Source-prepared non-streaming Suspense stayed suspended. Warm opaque unkeyed streaming rendered without transport identity. | Preserve captured readiness. Streaming admission runs even for warm rows. Empty/nonempty, query/source preparation, cold/warm and keyed/unkeyed controls remain. | +| Selected query/window identity, SR06/SR07 | Callback Collection selection and explicit-key windows were absent. | Replacement displayed old rows. Bounded and unbounded hashes aliased. | Recompute selected Collection hash and include limit/offset with explicit keys. Matching builder and supplied Collection windows share identity. | +| Ownership retirement, SR04/SR10 | No retry after a throwing release. Active-resource accounting hid duplicate calls. | Observer retry acquired zero channels. Pooled status release ran twice. | Retire observer attachment before invoking release handles. Remove redundant pooled release. All acquired channels still release and errors propagate. | +| Unchanged getter work, SR05 | Identity tests did not observe traversal or lazy Map work. | Eleven admission walks for eleven unchanged reads. | First capture supplies later reads without another admission walk or keyed Map build. This is a deterministic measured improvement. | + +### Primary owners and checkpoints + +`packages/db/tests/ssr-snapshot-admission-oracle.ts` owns the preparation judgment +and history grammar. It uses authored rows and caller preparation actions. It +imports no production admission or identity classifier to compute expectations. +React's descriptor owner drives ordinary and infinite hooks separately. It uses +actual `renderToString` and `hydrateRoot`, and compares server markup, initial +browser rows/status flags, recoverable errors, and committed browser output. + +The new Svelte handoff driver loads the real fixture with Vite SSR compilation. +Only JSON-serialized HTML and dehydration data cross into browser-compiled +`hydrate()`. The component records its first public hook rows/status before +effects. The driver derives expectations from the same shared judgment. The old +Svelte owner now states its narrower server-reconstruction scope honestly. +A normal warm-browser control remains separate from server admission. + +The shared grammar covers no/source/query/pending-query preparation, idle/loading/ +ready sources, zero to three rows, and writes on both sides of transport. A +same-query browser preload may finish before hydration. Idle sources contain no +rows. Completed query preparation requires ready sources. Pending preparation +requires loading sources. Those exclusions describe legal starting histories. +Ablating either write axis loses its distinct timing violation. Removing the +same-query preload axis loses replacement of the initial loading view. Removing +ready-empty cases loses readiness without row evidence. Named witnesses rebuild +the reported traces. Empty and three-row bounds remain in the random grammar. + +The React stream-cut owner separately varies zero to three rows and zero to three +microtask publications between startup and preload settlement. It compares the +transported result with the view actually rendered, including empty and unchanged +controls. It does not infer this ordering from a delayed return promise. + +The observer release owner varies granular/wholesale mode, Error/undefined +failures and one to three release/retry cycles. Its controlled wrapper retires a +real Collection subscription and then throws synchronously. It compares thrown +identity, zero channels after release, a fresh channel on explicit retry, and a +later public update. Real Collection event listeners report failures +asynchronously and do not supply this premise. No first-party callback-failure +claim receives credit from this controlled witness. + +The pooled owner counts every release invocation, preserving duplicate evidence. +The work probe counts admission calls and lazy keyed-state accesses. These +observations do not establish an elapsed-time budget or universal complexity law. + +## Pre-edit RED and calibration + +All required receiving paths ran on unchanged `f374bdbb1` before initial +production edits. Logs remained under `/tmp/tanstack-ssr-review2` during the task. +The durable results below preserve their useful evidence without depending on +those temporary files remaining available. + +| Experiment | Observed result | +| --- | --- | +| React receiving gate, revised ready law | 11 assertion failures, 13 passes. Source readiness was loading, late source rows changed markup, and browser activity threw the SSR-only error. | +| Actual Svelte handoff gate | Six assertion failures, six passes. First browser hook rows/status disagreed with the render-time model. | +| React Suspense/identity gate | Eight assertion failures, eight passes. Prepared Suspense did not finish, warm opaque identity bypassed admission, callback replacement retained old rows, and bounded/unbounded hashes matched. | +| Corrected observer release gate | Two generated-property failures. Explicit retry acquired zero channels instead of one. | +| Pooled release gate | Two generated-property failures. One status handle received two release invocations. | +| Work and explicit-preload neighbor | One failure, one pass. Eleven unchanged server reads repeated admission eleven times. A separate unchanged-production work probe also recorded twelve lazy state-getter accesses. Explicit preload control passed. | +| Narrow mutant: browser guard without render capture | React: one intended mismatch and one neighbor pass. Svelte: first-browser rows contained a late server row and failed. | +| Browser-only Svelte corruption | Old server-only test passed. Actual browser comparison failed at first hook rows/status. This directly demonstrates SR09's false green. | + +Earlier Svelte setup failures receive no RED credit. An initial observer premise +used a `subscribers:change` callback that reports errors asynchronously. Its +failure to throw was a fixture error, not production evidence. The corrected +controlled-release gate above supplies SR04 evidence. The first narrow-mutant +build also failed setup. Only the corrected build and value-assertion failures +receive calibration credit. All temporary mutants were restored. + +### Sibling defects found during the repair + +These are additional witnesses, not replacements for any original finding. +Each received a failing test before its subsequent production repair. + +| ID | Repair-attempt counterexample | RED and final enforcement | +| --- | --- | --- | +| N01 | A ready streaming component rendered before queued source updates, but its preload later transported the updated rows. | Four failures, one unchanged pass. Stream-cut property captures the rendered view. | +| N02 | A loading capture caused a second preload to replace an established pending stream. | Promise identity assertion failed. Pending records now remain reusable while carrying an initial snapshot. | +| N03 | A provisional result admitted a conflicting same-hash source before acquisition. | Rejection assertion failed. Source claims now check before replacement/startup. | +| N04 | A non-streaming loading capture carried a Promise into JSON transport. | Non-streaming promise-presence assertion failed, streaming neighbor passed. Default non-streaming transport retains its snapshot only. | +| N05 | A browser preload completed before hydration and replaced the original loading view. | React: six failures, 25 passes. Svelte: three failures, ten passes. Initial snapshot survives local preload replacement. | +| N06 | Matching explicit-key builder preload and supplied window disagreed. | Both direct and callback supplied Collection inputs failed. Window metadata now participates across these input forms. | +| N07 | Window inspection invoked an unrelated source utility named getWindow. | Two focused cases failed: an invented window changed identity and a utility threw. Hashing now reads registered live-query builder metadata. The finite invocation boundary has no independent generated-history owner. | +| N08 | The retained-snapshot helper erased its row type through generic ReturnType. | Parent CI and a direct full-package compiler run reported the same four errors. The helper retains its inferred observer type. Both the direct owner check and the final full-package compiler then passed. | + +The callback-returned Collection path in SR06/N06 is supported by current runtime +resolution, but has no TypeScript overload. Tests invoke this JavaScript path with +`Reflect.apply`. This repair adds no new typed public input form. + +## Lossless source-order findings + +Each quoted claim below is the original review text. File/line references name +the reviewed commit. All ten dispositions are `fixed-now`; SR04's confirmed +premise has the controlled-boundary limit stated above. + +### SR01 — correctness, `packages/react-db/src/useLiveQuery.ts:1058` + +> With SSR streaming off, useLiveSuspenseQuery now reads getServerSnapshot. That method forces status `loading` for every result that has no hydration seed, so a query whose sources were explicitly preloaded can never reach ready on the server. + +Confirmed. Forced-loading model confused normalized source readiness with captured query readiness. + +Repair evidence: react-remaining-red.log: prepared non-streaming output suspended; react-final-red.log: source-ready status loading rather than ready. Original implementation rejects the intended readiness comparison. + +Owner and durable value: shared SSR judgment and React descriptor receiving owner. Histories: source/query preparation; 0 or 2 rows; streaming on/off. +Checkpoint: completed server markup before browser work. + +### SR02 — correctness, `packages/react-db/src/useLiveQuery.ts:1054` + +> The streaming-Suspense branch returns observer.getSnapshot() and skips SSR admission even when the query has no queryHash, so warm rows render on the server with nothing that transfers them to the browser. + +Confirmed. Warm Suspense bypassed discovery and the missing-identity branch ran only while loading. + +Repair evidence: react-remaining-red.log: warm unkeyed opaque result rendered without an identity error. Original warm bypass fails; cold and explicit-key neighbors remain legal controls. + +Owner and durable value: React descriptor receiving owner. Histories: warm/cold opaque logic; explicit key present/absent. +Checkpoint: server markup and error callbacks; dehydrated query presence. + +### SR03 — correctness, `packages/db/src/live-query-observer.ts:352` + +> The admission check also runs in the browser, because React calls getServerSnapshot during hydrateRoot. Rows produced by browser-side activity therefore throw the 'SSR cannot render server-only rows' error during hydration. + +Confirmed. Hydration driver cleared window during its browser pass and never received browser-local writes. + +Repair evidence: react-final-red.log: browser activity threw SSR programming error. narrow-mutant-react.log: browser-only guard still fails render-time equality. + +Owner and durable value: shared SSR judgment, React ordinary/infinite and actual Svelte handoff drivers. Histories: browser writes and same-query preload before hydration. +Checkpoint: first public browser rows/status, React recoverable errors, committed browser output. + +### SR04 — correctness, `packages/db/src/live-query-observer.ts:682` + +> Re-attachment is now decided by `!this.attached` instead of `subscriptions.size === 1`. detach() resets `attached` only after collectionUnsub() returns, and that call now rethrows via runAllCallbacks. A throwing release therefore leaves the observer marked attached but with no channels. + +Confirmed. Final channel counts missed a failed release followed by fresh explicit ownership. + +Repair evidence: observer-release-red.log: retry subscriber count 0 instead of 1. Original detach ordering fails retry assertion. Controlled synchronous release only; first-party event callbacks report asynchronously. + +Owner and durable value: observer history owner. Histories: granular/wholesale; Error/undefined; 1–3 release/retry cycles. +Checkpoint: release error identity, zero channels after release, fresh channel and later publication. + +### SR05 — efficiency, `packages/db/src/live-query-observer.ts:350` + +> getServerSnapshot repeats the full source walk (getSourceInfo plus recursive _hasSsrCollectionSnapshot with builder lookups) on every call, and calls `snapshot.state?.size`, which forces the lazy Map build just to test whether there are any rows. + +Confirmed. Snapshot identity checks did not count admission walks or lazy Map getter accesses. + +Repair evidence: work-and-preload-red.log: 11 admission walks where one suffices. Original implementation fails work counter; capture removes repeated admission and lazy Map materialization. + +Owner and durable value: observer snapshot owner. Histories: prepared unchanged revision; repeated server getters; explicit preload neighbor. +Checkpoint: deterministic admission and lazy state-access counters. + +### SR06 — correctness, `packages/react-db/src/useLiveInfiniteQuery.ts:357` + +> When the query function returns a Collection (input.kind === 'collection'), queryHash keeps the stale committed?.queryHash from the previous query and is never recomputed, because inputIsCollection is false on that path. + +Confirmed. Callback-returned Collection input was absent from replacement coverage. + +Repair evidence: react-remaining-red.log: replacement showed old rows instead of new. Original stale-hash branch fails replacement assertion. Callback-returned Collections are JavaScript runtime coverage, not a new TypeScript overload. + +Owner and durable value: React descriptor receiving owner. Histories: builder to callback-returned Collection; derived/explicit identity. +Checkpoint: replacement rendered rows and current query hash. + +### SR07 — correctness, `packages/react-db/src/useLiveInfiniteQuery.ts:377` + +> With a queryKey, the windowed query's transport hash is just hash(queryKey) and ignores the limit/offset window. An infinite query therefore shares a hydration record with any non-windowed preload that uses the same key. + +Confirmed. Explicit keys erased limit/offset; supplied Collection windows were absent from input-form coverage. + +Repair evidence: react-remaining-red.log: bounded/unbounded hashes equal; supplied-window-red.log: matching supplied forms rejected. Unbounded alias and matching-window rejection provide opposite boundary controls. + +Owner and durable value: React descriptor receiving owner. Histories: matching window versus unbounded preload; builder/direct Collection/callback input. +Checkpoint: distinct or matching transport hash and server window rows. + +### SR08 — correctness, `packages/db/src/client.ts:707` + +> _hasSsrCollectionSnapshot admits a normalized source that is still loading. Rows it receives between server render and dehydrate() change the transported rows, so the first browser snapshot no longer matches the markup. + +Confirmed. Transport sampled mutable source rows at dehydrate instead of retaining rendered query view. + +Repair evidence: react-final-red.log and svelte-final-red.log: late source rows changed initial browser output. narrow-mutant-react.log and narrow-mutant-svelte.log: guard-only repair admits late rows and fails. + +Owner and durable value: shared SSR judgment; React ordinary/infinite and actual Svelte handoff. Histories: loading/ready source; server writes after render; browser writes before hydration. +Checkpoint: rendered rows/status versus first browser hook result. + +### SR09 — test-coverage, `packages/svelte-db/tests/ssr-snapshot-admission-oracle.svelte.test.ts:106` + +> False green: the 'initial browser' render uses svelte/server render() under @vitest-environment node. typeof window is undefined there, so it goes through getServerSnapshot, and the real browser branch (getSnapshot, useLiveQuery.svelte.ts:455) is never compared with the server markup. + +Confirmed. Node server render was incorrectly credited as an initial browser witness. + +Repair evidence: svelte-browser-mutant.log: old server-only test green while real browser comparison red. Browser-only corruption survives old test and fails new receiving assertion. + +Owner and durable value: shared SSR judgment and actual Svelte handoff driver. Histories: real SSR compilation followed by browser compilation/hydrate; same timing grammar. +Checkpoint: first public hook values before effects. + +### SR10 — simplification, `packages/db/src/query/pooled-live-query.ts:160` + +> The new early `this.stopStatusEvents = stopStatus` overlaps the terminated branch below it. In the re-entrant cleanup case, terminate() already calls stopStatus via release(), and then `if (this.terminated) { stopStatus(); ... }` calls it again. + +Confirmed. Resource balance tracked active handles but normalized duplicate release calls away. + +Repair evidence: ownership-red.log: two pooled status releases for one handle. Original redundant call fails invocation counter. The observer failures in this initial log used an invalid event-listener premise and receive no credit. + +Owner and durable value: pooled live-query GC owner. Histories: synchronous cleanup registration; callback return/throw and undefined cause. +Checkpoint: every status release invocation, not only remaining resources. + +## Final GREEN and declared reach + +The final React package run passed 517 tests in 23 files. The Svelte package run +passed 163 tests in 11 files. Both reported no type errors. The final scoped core run +passed 346 tests in ten files with no type errors. It selected DbClient source +identity/hydration, observer history/snapshots/API, pooled GC, and window control. +This includes the error-message owner. Manifest/reporting checks have separate +results below. Earlier assertion-only framework runs reported type-check errors in test +drivers. Those runs receive no clean-GREEN credit. The corrected runs do. + +The full DB test compiler also passed on `44ee3f002`: +`pnpm exec tsc -p packages/db/tsconfig.test.json --noEmit --incremental false`. +It exited zero with no diagnostics. + +Normal campaigns use seed 2229 and an unsupplied seed with identical budgets. +Counts below are histories reaching each declared state, not total events. + +| Path, fixed/random budget | State counts, fixed / random | +| --- | --- | +| React ordinary, 120/120 | cold-loading 19/25; unbacked-ready-empty 5/6; unbacked-rows 43/34; query-result 18/15; normalized-ready 12/18; normalized-partial 23/22; pending-query 19/17; server-write 29/30; browser-write 27/34; same-query-preload 14/17 | +| React infinite, 120/120 | cold-loading 19/26; unbacked-ready-empty 5/3; unbacked-rows 43/36; query-result 18/18; normalized-ready 12/19; normalized-partial 23/18; pending-query 19/20; server-write 29/30; browser-write 27/41; same-query-preload 14/19 | +| Actual Svelte handoff, 120/120 | cold 19/15; query 18/16; source-ready 12/26; source-loading 23/16; rejected 48/47; server-write 29/34; browser-write 27/36; same-query-preload 14/13 | +| React stream cut, 40/40 | empty 11/14; nonempty 29/26; later-publication 31/29; unchanged 9/11 | +| Observer release, 40/40 | granular 22/22; wholesale 18/18; release-error 40/40; retry 40/40; undefined-cause 18/21 | +| Pooled late cleanup, 40/40 | return 22/18; throw 18/22; undefined-cause 18/15 | + +Each owner enforces declared minima. Fixed examples add replayable witnesses but +do not replace these campaigns. Six guarded final replays use seed 2229 and captured failure paths. Each +reported one completed execution with nonzero runs and `failed:false`: + +| Property | Package | Path | Runs | +| --- | --- | --- | --- | +| react.ssr-ordinary | react-db | 4 | 120 | +| react.ssr-infinite | react-db | 4 | 120 | +| svelte.ssr-handoff | svelte-db | 4 | 120 | +| react.ssr-stream-cut | react-db | 0:0:1 | 1 | +| live-query-observer.release-failure | db | 0:0 | 1 | +| pooled-live-query.late-cleanup | db | 0:0 | 1 | + +For example, from `packages/react-db`: + +```sh +TANSTACK_DB_ORACLE_SEED=2229 \ +TANSTACK_DB_ORACLE_PATH=4 \ +TANSTACK_DB_ORACLE_PROPERTY=react.ssr-ordinary \ +pnpm exec node --import tsx ../db/tests/oracle-replay.ts \ + tests/descriptor-query-binding-oracle.test.tsx \ + --coverage.enabled=false --maxWorkers=2 +``` + +The manifest selects the named random lane and excludes fixed campaigns. +Direct `tsx` launch failed at a sandbox IPC socket before test execution. +`node --import tsx` ran the intended properties successfully. A broad core plus +replay-protocol run stopped reporting progress and was terminated. It receives +no GREEN credit. The separate scoped core run, six direct guarded replays and +six manifest/reporting tests passed. The error-message owner passed 19 tests. No replay-protocol implementation changed. + +The production build, 419-name mangle check, scoped ESLint, formatting, +error-doc generation and whitespace checks passed. ESLint retained one existing +require-await warning in the pooled GC file, with zero errors. The minified public consumer +check passed 107 error names, index metadata, query rows and live updates. +Production error checks erased 248 literals, including 20 shared literals, in +ESM and CommonJS while retaining development text. + +## Shipped weight and alternatives + +Compared with merged main `65d9ada508114f199464f753d50eb3447eb0eaf9`, +`pnpm size:compare --packages db,react-db --files` measured: + +| Output | Gzip change | Brotli change | +| --- | ---: | ---: | +| db package | +660 bytes | +646 bytes | +| react-db package | +107 bytes | +119 bytes | +| Typical app | +396 bytes | +410 bytes | +| Other app scenarios | +363 to +392 bytes | +250 to +476 bytes | + +These are whole-branch changes, not only this follow-up. Production source in +this follow-up grows by 70 net lines. Tests and fixtures grow by 1,326 net lines before this review record. +Documentation and metadata grow separately and do not enter shipped-byte figures. + +The repair removed the separate observer WeakMap and forced-loading snapshot +copies. It reused DbClient query-result records instead of adding a second +snapshot cache or lifecycle. A browser-only admission guard was tried as a narrow +mutant. It fixed browser rejection but still failed late-server-write equality +in both hosts. Counting a loading capture as a completed preload failed the +explicit-demand law. Replacing a pending record failed stream-promise identity. +Omitting the retained initial snapshot failed same-query preloads in both hosts. +Omitting window metadata from supplied Collection hashing failed matching +preload/hook identity. Reading arbitrary utils was also rejected: registered +builder metadata is both narrower and five production lines smaller. These experiments justify the remaining transport fields +and branches. No retries, generations, or separate recovery state machine were +added for a programming error. + +## Oracle conformance self-review + +This is a self-review. No independent agent or new external review ran. + +| Requirement | Outcome | +| --- | --- | +| ORC-001 authority/limits | SSR guide, prepared Suspense API, ownership contracts and maintainer policy supply authority. Finite limits appear beside owners and below. | +| ORC-002 independence | Shared judgment uses authored rows and preparation actions. Ownership uses token/channel counts. Production classifiers do not compute expected values. | +| ORC-003 literate responsibilities | Shared owner states the law, grammar and judgment. Directly named host owners explain drivers, recordings and checkpoints. | +| ORC-004 grammar controls | Named reconstructions, axis ablations, zero/three row bounds and idle/pending exclusions appear above. Window identity has matching/unbounded and selected-input controls. | +| ORC-005 path/observations | Actual React hydrateRoot and browser-compiled Svelte hydrate receive serialized state. First hook values, status flags and errors are observed before commit repair. Render captures provide information the browser otherwise lacks. | +| ORC-006 calibration | Original assertion failures, browser-guard-only mutants and browser-only Svelte corruption reach their intended comparisons. Setup failures receive no credit. | +| ORC-007 campaigns/replay | Equal fixed/random budgets and six completed guarded replays, including exact seed/path witnesses. No command-model replayPath is applicable. | +| ORC-008 model minimality | Preparation and source state remain distinct: ready-empty and source-loading predict different status. Later server/browser writes cannot change the modeled initial view. No new reference lifecycle state machine was added. | +| ORC-009 vocabulary | Model sourceState combines startup/readiness for the stated synchronous eager grammar. Preparation describes caller actions. Canonical Collection, cleanup, readiness and publication terms retain their glossary meaning. | +| ORC-010 failure fidelity | Observer driver retains thrown undefined, primary assertion and secondary cleanup diagnostics through withOracleCleanup. Host drivers release clients, mounts and compiler resources. The intended assertion failures survive shrinking. | +| ORC-011 second formulation | The named shared-fault concern was an incorrect readiness policy and a fake browser premise. Contract reconciliation and actual receiving drivers distinguish them. A second structurally similar model would not add independent evidence. No other concrete shared semantic fault was identified. | +| ORC-012 evidence/closure | This versioned record identifies reviewed and repaired commits, lossless findings, calibration, reach and finite limits. It does not claim universal proof. | +| ORC-013 nearby boundaries | Ready-empty versus cold-empty; prepared versus incidental rows; keyed versus opaque-unkeyed; matching versus unbounded windows; empty versus nonempty stream updates; and successful retry after throwing release distinguish plausible wrong rules. | +| ORC-014 handoff | React and Svelte real host APIs receive the shared law at the named cuts. jsdom supplies browser globals and DOM. This is no claim about a deployed Start/Electric host. SR04 remains explicitly controlled. | +| ORC-015 reach | Each generated path has minimum reach assertions. The table above reports actual normal-run counts separately for both hosts and both React hooks. | + +## Reviewer assessment, loss audit and readiness + +**Hire.** All ten claims supplied useful, reproducible evidence. The reviewer +connected hydration timing, Suspense readiness, identity and lifecycle ownership +instead of relying only on local code style. Prioritization was sound. The work +finding distinguishes unnecessary traversal from latency. The simplification +exposed duplicate release evidence that resource-balance checks had erased. +SSR row/readiness mismatches have high user impact. Callback replacement and +window identity have medium impact. Work and duplicate idempotent release are +low severity. SR04 needed a narrower synchronous-release premise than a real event-listener +failure. The review supplied counterexamples, not a complete transport design. +The repair still needed neighboring-law experiments to avoid new stream and +preload defects. + +Loss accounting: **10 raw = 10 fixed-now**. No original item is merged away, +refuted, deferred or left as an accepted-design exception. The additional +repair-attempt witnesses also pass. Accounting and the authorized repair for +this review are complete within the boundaries above. + +Known limits remain in the [coverage map](../oracle-coverage.md): synchronous eager +sources with zero to three authored rows, initial infinite windows, and the +named microtask schedules. Arbitrary asynchronous providers, persistence +readiness, later page-shape changes during hydration streams, mixed ordinary/ +Suspense streaming schedules, custom filters, and deployed Start/Electric hosts +remain unexercised paths. No known reachable counterexample remains in this +review's repaired scope. Finite green histories are not a proof for all histories. + +Parent CI had two failing unit shards from N08 and a canceled browser E2E job. +The aggregate test jobs reflected those results. Local assertion counts did not +erase those failures. N08 now has direct owner and full-package compiler evidence. + +Whole-PR readiness remains **unproven** until final pushed-head CI and the earlier +evidence gaps receive their own decisive evidence. The prior migration audit +retains H04 (legal recoverable pooled subscription throw), H08 (permanently +stranded demand after notification failure), and F07 (Date assignment and exact +dependency versions). This review neither closes those reports nor invents a +recovery state machine for them. CodeRabbit remains paused. No external review +request was sent. + + +## Follow-up: mixed streaming readers — 2026-10-23 + +Starting head: `a0991825ea326fcdbfadbeb61c2029ff130b1a88`. +Production repair: `10e0e6fed7f127d515144e1a14c4e69805425cc2`. +Final oracle instrumentation: `ce8f99d350556cf461cca059db41e4f490eea74d`. +This is a self-review. No independent agent or external review ran. +The earlier record remains historical. This section supersedes its exclusion +of the mixed-reader schedules exercised below. + +### Joint law and the missing experiment + +Each reader's first browser rows and readiness must reproduce that reader's +server-rendered view. Keeping an ordinary loading view cannot prevent explicit +Suspense preparation from completing. One committed reader cannot consume a +later boundary's server view. The SSR guide supplies initial-view equality and +explicit preload completion. React's hydration API supplies the receiving cut. +The maintainer's programming-error policy does not permit a crash for these +legal schedules. Active Collection cleanup retains its separately approved rule. + +The old stream-cut property compared one Suspense render with its transported +rows. It omitted an ordinary sibling and never hydrated this mixed tree. Its +narrower green result did not establish the joint promise. The expanded +metamorphic judgment retains each server reader's projected primitive values +and status, then compares that same reader's first browser result. It imports +no production classification to predict equality. + +The grammar varies zero to three authored rows and zero to three microtask +publications. An ordinary reader appears before Suspense, after Suspense, or +not at all. Browser startup may publish different rows under the same key. +An async React act cut and a synchronous act cut distinguish hydration together +from ordinary commit before Suspense hydration. Snapshots and resolved Promise +values cross through JSON. The real Router adapter supplies the stream under +controlled SSR callbacks. Actual renderToPipeableStream and hydrateRoot supply +the React receiving path. Suspense-only histories compare server markup and +transport. Only mixed histories receive browser-hydration credit. + +Removing placement loses the competing loading/ready views. Removing browser +variation permits local recomputation to conceal missing transport. Removing +the split cut permits an early commit to consume another boundary's view. +Empty rows distinguish readiness without row evidence. Microtask writes retain +the original rendered-view versus later-publication distinction. The grammar +reconstructs each known witness below. It permits one ordinary sibling, so the +mapping excludes both placement flags being true. Generated writes insert a +missing key and update an existing key, including the browser-only row. + +### Additional findings and pre-edit evidence + +These findings supplement N01–N08 and do not replace any raw SR item. + +| ID | Finding and test gap | Decisive evidence | Disposition | +| --- | --- | --- | --- | +| N09 | Completing a shared Suspense query replaced an ordinary reader's initial loading view. The previous owner lacked the second reader and its first-browser observation. | On unchanged a0991825e, three failures and three passes. Fixed seed 2229, path 0:0:0, and random seed -1274793130, path 0:0:0, reduce to zero rows and ready instead of loading. | fixed-now, within the mixed grammar above. | +| N10 | Router suppressed every later record with an already-streamed hash. Core snapshot repair alone could not deliver the replacement. | The actual Router receiving test ran before production edits. Its second read reported done:true instead of forwarding the completed preload. | fixed-now. The same Router adapter also supplies the generated receiving path. | +| N11 | A split hydration cut used local rows before the stream delivered its result, or lost a result after a sibling consumed it. Async-only hydration hid this schedule. | The expanded owner ran before the retention/wait repair: three failures, five passes. Seed 2229, path 3:0:0:1, reduces to empty rows with incorrect readiness. Random seed -45443823, path 0:0, reaches the same mismatch. A retained-record-only attempt still failed all three comparisons. | fixed-now, including retained transport and waiting for missing rows. | +| N12 | CI rejected a dynamically spread one-run replay override as oracle/no-disabled. Local ESLint does not include that installed CI rule. | Lint at a0991825e failed while all unit shards passed. Shared replay options replace the override. Normal fixed/random campaigns still run 75 histories each, and direct seed 2233/path 1:0 passes. | Local repair complete. Final CI must supply rule-specific confirmation. | + +N09 and N10 reached their receiving assertions on unchanged production before +its first edit. N11 reached its assertions on the first mixed-reader repair +before adding retained server records or the pending-result wait. These are +assertion failures at the promised first-view cuts, not timeout kills. The +retention-only attempt demonstrates why a narrower repair is insufficient. + +Fixture mistakes receive separate treatment. One browser-row fixture attempted +a duplicate insert and produced four uncaught exceptions. The corrected fixture +passes but those exceptions supply no calibration credit. Another run passed +519 assertions but reported a private-option TypeCheckError. It was not clean +GREEN. The fixture now uses its known client identity. Initial bundle checks +stopped at their stale-dist guard before execution. Later checks used a fresh +build. An incorrectly addressed diagnostic command changed no test source and +supplies no scheduling evidence. Temporary diagnostics are absent from the final +oracle. Development multiple-renderer warnings remain distinguishable from +uncaught errors and recoverable hydration mismatches. + +### Repair, alternatives, and cost + +The transport reuses the record's existing initial snapshot field. It sends a +second view only when that view differs from the completed result. Ordinary +readers retain their earlier view. Suspense reads the completed result. Router +replaces its hash Set with a version Map so replacement records can arrive. + +DbClient retains a reference to each transported query record until cleanup. +Only the server getter can recover that record after ordinary consumption. +Normal new browser observers continue to read live results. If hydration lacks +the server-rendered rows, its getter suspends on the stream Promise. Explicit +Suspense demand can still start local work. No retry timer, generation, queue, +or replacement lifecycle was added. + +Reusing only the active record was smaller but failed the split schedule. +Retention alone also failed while the Promise still lacked its snapshot. Reader +counts cannot include a boundary React has not instantiated. A separate root +hydration-completion lifecycle would require an architectural signal this API +does not expose. The chosen map keeps references rather than copying rows. +Its memory cost lasts for the DbClient lifetime and grows with distinct hydrated +query hashes. cleanup clears it. Those costs earn their place through the +receiving law and the rejected narrower designs. + +Compared with a0991825e, production grows by 45 net source lines. Tests grow by +225 net lines after correcting reach counters. Guide, coverage-map and changeset +text grow by 24 net lines, excluding this audit. The mangle cache adds two names +and drops none, for 421 names. Tests and documentation are separate from shipped +bytes. Against merged main 65d9ada508114f199464f753d50eb3447eb0eaf9, +size:compare over db, react-db and react-router-with-db reports: + +| Output | Gzip change | Brotli change | +| --- | ---: | ---: | +| db | +799 bytes | +891 bytes | +| react-db | +117 bytes | +129 bytes | +| react-router-with-db | +25 bytes | +27 bytes | +| Package total | +941 bytes | +1,047 bytes | +| Typical app | +479 bytes | +527 bytes | +| Other app scenarios | +437 to +488 bytes | +329 to +443 bytes | + +### Final evidence and self-review + +The full React package passes 520 tests in 23 files with coverage and type +checks. Svelte passes 163 tests in 11 files. Core checks cover 379 distinct tests +in 13 files, including observer lifecycle, DbClient transport and identity, +pooled queries, windows, and errors. Router passes ten tests in two files. +The full DB test compiler passes. The final reach-only adjustment runs the +same eight stream-cut tests again without broadening unrelated testing. + +Both normal stream campaigns retain a 40-history budget. Fixed seed 2229 and +the final unseeded run report these actual counts. Browser states count only +histories that reached hydrateRoot, not Suspense-only transport histories. + +| Declared state | Fixed | Random | +| --- | ---: | ---: | +| empty | 11 | 9 | +| nonempty | 29 | 31 | +| later-publication | 31 | 29 | +| unchanged | 9 | 11 | +| ordinary-first | 21 | 18 | +| suspense-first | 11 | 9 | +| suspense-only | 8 | 13 | +| browser-different | 12 | 18 | +| browser-same | 20 | 9 | +| split-hydration | 16 | 12 | +| together-hydration | 16 | 15 | + +Every normal campaign asserts minimum reach. The guarded stream replay at seed +2229/path 3:0:0:1 completes one execution with two runs and failed:false. The +older replay-demand property's wrapper stops at no-named-owner before execution. +Its direct replay passes but receives no guarded-replay credit. No manifest +entry was invented for this lint repair. + +Scoped ESLint has zero errors or warnings. Prettier, whitespace and the 421-name +mangle check pass. The minified consumer passes 107 error names, index metadata, +query rows and live updates. Production-error checks erase 248 literals, +including 20 shared literals, in production ESM and CommonJS while preserving +development text. + +| Requirement | Follow-up outcome | +| --- | --- | +| ORC-001 | SSR guide and hydration equality authorize the joint law. The finite source and host limits remain explicit. | +| ORC-002 | The relation compares each rendered primitive projection with that reader's first browser projection. Expected equality does not use the transport classifier. | +| ORC-003 | Local prose explains the law, placements, scheduling cuts, public observations and controlled Router premise beside the existing owner. | +| ORC-004 | The reconstruction, ablation, zero/three bounds and mutually exclusive placement mapping appear above. Duplicate inserts are invalid and no longer enter the fixture. | +| ORC-005 | Real React APIs receive actual Router adapter chunks. First hook rows/status and recoverable errors are compared. Suspense-only cases claim transport evidence only. Missing result data causes a wait rather than local substitution. | +| ORC-006 | Original mixed-reader and Router comparisons fail. The split comparison also rejects retention without waiting for the missing result. Setup/type failures receive no credit. | +| ORC-007 | The same grammar and comparison run 40 fixed and 40 unseeded histories. Named seed/path replay completes directly. No command-model replayPath applies. | +| ORC-008 | The relation needs a separate first view per reader: loading versus completed readiness predicts different hydration. It introduces no reference lifecycle state machine. | +| ORC-009 | Rows, Collection readiness, cleanup and publication retain glossary meanings. split-hydration names a host scheduling cut, not a new Collection state. | +| ORC-010 | Shrinking preserves the first-view mismatch. Diagnostics distinguish primary mismatches, fixture exceptions, compiler failures and cleanup. Roots, readers and clients are released. | +| ORC-011 | Browser-local variation challenges the concrete shared-fault hypothesis that both sides recompute from equal local rows. The relation needs no second copy of production semantics. | +| ORC-012 | This append-only section identifies production and final oracle commits, exact failures, reach, alternatives and finite limits. | +| ORC-013 | Empty readiness, both reader orders, async versus split hydration, and same/different browser rows distinguish nearby wrong consequences. | +| ORC-014 | Actual React supplies the split cut. Router callbacks remain controlled. This is not a deployed Start or real-provider claim. | +| ORC-015 | Each declared receiving state requires actual browser execution. The table records the final normal campaigns. | + +The ten raw SR findings remain accounted for as ten fixed-now. This follow-up +adds N09–N11 repairs and the local N12 CI repair without losing earlier evidence. +It makes no independent-reviewer quality claim. The earlier reviewer assessment +remains historical. + +Whole-PR readiness remains unproven. At a0991825e every unit shard passed, Lint +failed and browser E2E was canceled. Final pushed-head CI still needs inspection. +Earlier H04/H08/F07 evidence gaps remain in their migration audit. The coverage +map retains arbitrary asynchronous providers, persistence readiness, later +infinite-page changes, custom filters, concurrent-root schedules and deployed +Start/Electric receiving paths. No known counterexample remains in the exercised +mixed-reader scope. Passing these finite histories is not universal proof. +CodeRabbit remains paused. No new external review request was sent. diff --git a/docs/contributing/oracle-reviews/2026-10-24-convex-auth-dependency-update.md b/docs/contributing/oracle-reviews/2026-10-24-convex-auth-dependency-update.md new file mode 100644 index 0000000000..11f080a818 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-24-convex-auth-dependency-update.md @@ -0,0 +1,75 @@ +# Auth fixture dependency update + +Reviewed dependency commit: `bae01f788134aa421a9018278ec6d2867519dae3`. +This is an addendum to the [public application receiving review](2026-10-24-convex-public-apps.md). +That earlier record remains an accurate account of its historical source and +runtime versions. This update reviews the new dependency resolution and receiving +evidence, it does not repeat the full oracle audit or claim new oracle behavior. + +The private auth fixture now uses `@convex-dev/auth` 0.0.96 and `@auth/core` +0.41.3, replacing 0.0.90 and 0.37.4. Only its package manifest and corresponding +lockfile entries changed in the reviewed commit. The pinned upstream business +functions, provider substitution, models, assertions, and setup code are unchanged. +No dependency override, security exemption, or shipped production code change was +introduced. + +## Independent checks + +The reviewer inspected the complete dependency diff and queried published npm +metadata for `@convex-dev/auth` 0.0.96. Its declared peers are: + +| Peer | Declared range | Resolved fixture version | +| --- | --- | --- | +| `@auth/core` | `^0.41.1` | `0.41.3` | +| `convex` | `^1.17.0` | `1.47.0` | +| `react` | `^18.2.0 \|\| ^19.0.0-0` | `19.2.4` | + +All three resolutions satisfy the published peer ranges. The old Auth.js and +Convex Auth resolutions are absent from the lockfile. Auth.js now resolves its +own `jose` 6.2.12 dependency, while Convex Auth and the fixture's signing-key +setup retain their separate `jose` 5.10.0 resolution. These are ordinary package +resolutions, not a forced shared version. CI runs Node 22.13. + +The reviewer also queried both official GitHub advisory APIs: + +- [GHSA-7rqj-j65f-68wh](https://github.com/advisories/GHSA-7rqj-j65f-68wh) + identifies the Auth.js email-normalization issue and lists 0.41.3 as the first + patched `@auth/core` version. +- [GHSA-xmf8-cvqr-rfgj](https://github.com/advisories/GHSA-xmf8-cvqr-rfgj) + identifies malformed Bearer-header handling and likewise lists 0.41.3 as the + first patched version. + +The selected Auth.js version is the published patched version for both reported +advisories, and its companion Convex Auth version explicitly supports that peer +range. This establishes the dependency remediation for those alerts, not an +independent reproduction of either security exploit or an absence of other +vulnerabilities. + +## Receiving evidence and scope + +The root agent reports the complete 17-test backend suite passing after the +upgrade, including the final full auth permission matrix and equal-body message +multiplicity checks, with 34.02 seconds of test execution. Package typecheck also +passed. The reviewer inspected the dependency resolution independently, but did +not rerun this suite or the prior mutants. + +This fresh run receives the existing application laws with the updated auth +runtime: genuine Anonymous sign-in and JWT use, unauthenticated rejection, +invalid-token rejection, read/write/admin permission transitions, demotion, and +public Collection rows after source acceptance. The six public pagination +histories and existing nine backend tests also passed in that combined run. +The earlier record's mutant calibrations remain attributed to their original +runs, they are not presented as new controls executed against these versions. + +The upgrade does not change the models or their legal histories, so their +independence, bounded observations, and cleanup judgments remain as described in +the earlier record. The changed receiving premise is the installed auth runtime; +the new complete backend run supplies compatibility evidence for that premise. +This does not test email-provider normalization or every malformed Bearer-header +case, the patched-version claim comes from the official advisories. + +The fixture still adapts a pinned public app rather than installing its original +lockfile. The local Rust backend remains selected by the installed Convex CLI, +its binary version was not recorded. Cloud infrastructure, third-party OAuth, +email delivery, browser components, token refresh/revocation, authenticated +reconnect, and arbitrary network schedules remain outside these receiving tests. diff --git a/docs/contributing/oracle-reviews/2026-10-24-convex-backend-startup.md b/docs/contributing/oracle-reviews/2026-10-24-convex-backend-startup.md new file mode 100644 index 0000000000..c5c702e7ca --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-24-convex-backend-startup.md @@ -0,0 +1,87 @@ +# Local backend startup review + +Reviewed source and test commit: `4b37df090402469cafed0cf9f5e16b9528dae60f`. +This follows the [public-app receiving review](2026-10-24-convex-public-apps.md) +and [auth dependency update](2026-10-24-convex-auth-dependency-update.md). +It concerns test setup and workspace discovery, not an adapter production defect. +There is no shipped adapter production code change. + +The root agent reported that the browser CI job exited before test collection +with the official Convex CLI error `Requested port 43330 is not available`. +That establishes an unavailable requested port. It does not establish which +process occupied it or why. The fixture had imposed that machine-wide port +requirement itself. + +## Contract and repair + +Without a user port override, the fixture should start an isolated local backend +on available ports rather than require its former fixed numbers. The reviewer +inspected all three setup call sites and the installed Convex 1.47 CLI source. +Its anonymous deployment path calls `chooseLocalBackendPorts`, which delegates +to `detect-port`: explicit requested ports are mandatory, while defaults and +suggestions can select other available ports. The default search starts at +3210. The test does not calculate or promise the allocator's exact result. + +All three setups now omit mandatory port arguments by default. The existing +`CONVEX_TEST_PORT` override still supplies an explicit port to the main fixture. +The shared launcher retains strict loopback URL validation and verifies the +exact requested port when one was supplied. No custom free-port allocator, +retry loop, or silent fallback for an explicit override was added. + +A separate workspace discovery issue was also repaired. Auth setup creates an +ignored `auth-app/runtime/package.json`, which the broad `packages/**` workspace +glob could discover during concurrent pnpm checks. An exact exclusion now keeps +that generated runtime out of the workspace project set. The real auth fixture +remains a workspace package. This changes configuration, not dependency versions +or lockfile resolutions. + +## Witness and execution evidence + +`e2e/backend-startup-oracle.test.ts` supplies an independent resource judgment: +while the old public fixture port 43320 is occupied, setup must fulfill and +provide another loopback port serving a fresh empty upstream messages query. +It invokes the actual pinned public-app setup and queries the real backend. +Expected startup success and empty data do not come from copying CLI selection +logic. An explicit startup-fulfillment assertion precedes the URL and query +observations. + +The reviewer inspected that test, the setup changes, cleanup paths, workspace +exclusion, and installed CLI implementation. The following execution evidence +comes from the author and root agent, it is not presented as an independent +rerun by this reviewer: + +- Restoring the former fixed 43320 setup argument failed the startup-fulfillment + assertion in 292 ms with `Requested port 43320 is not available`. This is the + intended startup outcome comparison. The later query checkpoint was not + reached, so this is not a failure at a data comparison. +- Restoring default allocation passed in 1.05 seconds and reached the actual + upstream empty-page query, including `isDone: true`. +- The first combined 18-test run passed with normal allocation. A second run + held port 43330 from before pnpm and global setup began, matching the occupied + auth-port condition from CI, and all 18 tests passed in 33.69 seconds. The root + agent verified and terminated its own blocker afterward. Only this second + run establishes the specifically occupied auth-port condition. +- Final package typecheck and ESLint passed. With a synthetic generated runtime + package present, `pnpm list -r` succeeded and excluded it. The synthetic runtime + was removed afterward. + +The fixed startup witness runs in the normal backend suite. It releases its own +listener and backend independently through the existing failure-preserving +cleanup instrument. `EADDRINUSE` is accepted as an observation of an existing +external owner at bind time, that owner's lifetime is not leased by the test, +and the test never closes it. + +## Scope + +This closes the fixture's mandatory-default-port assumption for the reviewed +serial setup paths. The public fixture regression observes the shared startup +boundary directly; the complete suite also receives the auth setup under its +formerly required occupied port. The source inspection confirms that all three +call sites delegate their default choice to the same supported CLI path. + +This is a fixed native startup witness, not a generated port-allocation model +or a proof against every concurrent bind race. It does not promise startup when +no ports are available, override an occupied port explicitly requested by a user, +or identify the owner of the original CI port. Network access and backend +availability remain setup prerequisites. Existing public-app, auth, pagination, +and offline contracts and their recorded limits are unchanged. diff --git a/docs/contributing/oracle-reviews/2026-10-24-convex-public-apps.md b/docs/contributing/oracle-reviews/2026-10-24-convex-public-apps.md new file mode 100644 index 0000000000..615a36aaad --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-24-convex-public-apps.md @@ -0,0 +1,125 @@ +# Public Convex application receiving review + +Reviewed source and test commit: `4223c39a6d5c2b2e2fedb1a7eee3c0052b5e5013`. +This independent review covers the new public pagination and authenticated app +receiving tests, their pinned-source setup, `local-backend.ts`, and +`network-gate.ts`. It found no adapter defect. This commit changes test and +integration setup, dependencies, and documentation, with no shipped production +code change. + +The reviewer inspected the committed test and setup files and fetched the pinned +upstream message functions, schema, auth functions, and permission implementation +through GitHub's API. The execution results below are attributed to the authors +and root agent. The reviewer did not rerun the backend suites or the production +mutants for this review. + +## Sources and observed evidence + +The pagination source is [get-convex/convex-demos at +2fac21e360bfcceae6065096f1b81480bd8c6fd5](https://github.com/get-convex/convex-demos/tree/2fac21e360bfcceae6065096f1b81480bd8c6fd5/pagination). +The actual functions are deployed unchanged from `git archive` of that commit. +The optional local checkout must have that HEAD, and archive reads the pinned +object rather than dirty worktree files. The local app shell and SDK dependency +are supplied by the test. Upstream declares Convex 1.48, while this fixture +intentionally deploys with the package's installed Convex 1.47 SDK/CLI and the +CLI-selected local Rust backend release. The binary version was not recorded, +so this record does not assign it the SDK's version. + +The auth source is [get-convex/convex-auth-with-role-based-permissions at +45f08008e1257758560948db4280cc4e7d64e84a](https://github.com/get-convex/convex-auth-with-role-based-permissions/tree/45f08008e1257758560948db4280cc4e7d64e84a). +Setup verifies the fetched HEAD and copies the message functions, schema, auth +routes, and permission library into an ignored runtime. The one deliberate +application adaptation replaces GitHub/Resend providers with Convex Auth's +Anonymous provider. The replacement checks the expected pinned source text. +Server permission checks and role updates retain upstream behavior. The fixture +uses `@convex-dev/auth` 0.0.90 and `@auth/core` 0.37.4, rather than the upstream +app's older declared 0.0.71 and ^0.34.1. It does not install the upstream lockfile. +Fresh signing keys allow a genuine sign-in and JWT without third-party +credentials. + +Reported execution evidence: + +- All 17 combined backend tests passed before the final auth history extension. + That run included the existing nine backend tests and all eight new public-app + histories, six pagination and two authentication. +- After the last extension, both auth client histories passed with every + role/operation pair, two successful equal-body sends, and two deletions. This + is targeted evidence for the final auth changes, not a claim that the reviewer + reran the full combined suite at the reviewed commit. +- The root agent reported 62 adapter checks, package typecheck, and lint passing + for the final source. The author reported all six public pagination histories + passing after the continuation control was restored, using default fresh + GitHub fetch/archive setup rather than the local-source override. +- A stale runtime sentinel was removed by actual auth preparation. This checks + the fresh-runtime prerequisite rather than assuming a previous teardown ran. + +Calibration evidence is distinguished from successful execution: + +| Control | Reported result and checkpoint | +| --- | --- | +| Disable automatic continuation | With the proper selector `mirrors list through`, both actual SDK histories failed the immediate ordered `select` comparison after preload. JavaScript exposed three rows instead of the ledger's 17, React three instead of 34, because successful writes from the earlier selected history remained in the shared backend. These were assertion failures at the named checkpoint, not timeouts or setup errors. | +| Initial selector containing a literal `>` | It matched no tests. This was an unreached path and is not counted as a mutant kill. | +| Bypass upstream role checks in a temporary prototype | The author changed the pinned permission function's final numeric comparison to `return true`, preserving user/role existence checks, and let the real backend redeploy. The first read/send denial assertion received a resolved `null` instead of rejection, one failed/one skipped test, with no timeout or setup failure. The original source was restored and both SDK histories passed through their own pinned-source setup. This control used the earlier five-step history; the final expanded history retains that first read/send cell. It was not rerun as a mutant at the frozen commit. | + +## Requirement outcomes + +| Requirement | Outcome and evidence | +| --- | --- | +| ORC-001, authority and limits | Satisfied for these receiving claims. Upstream pagination functions establish newest-first order, indexed author membership, and uppercase body/author-initial transformation. The upstream permission function and message endpoints establish read/write/admin access. The adapter's existing source snapshot and complete eager pagination contracts establish the Collection observations. The tests do not authorize a new provider or adapter behavior. Pins and adaptations are explicit. | +| ORC-002, independent judgment | Satisfied. Pagination's expected rows come from an authored ledger of successful sends, reversed and optionally filtered/transformed according to the upstream query contract. Expected rows do not come from the adapter, a paged query, or SDK cache. Auth uses a set-valued role policy rather than copying upstream's numeric role comparison, and a body multiset rather than server-generated IDs. Actual IDs are used only to target deletion, not to compute the expected message count. | +| ORC-003, literate responsibilities | Satisfied. Both files explain the upstream authority, reference computation, authored histories, real SDK/Collection path, public observations, and limitations before mechanics. Comments identify the shared-backend ledger prerequisite, stable keys for transformed rows, role-update adaptation, denied operations, and reconnect cut. Setup and README describe which upstream code is deployed and which authentication provider is substituted. | +| ORC-004, generated grammar controls | Not applicable, these are fixed authored receiving histories rather than generated properties. Their finite scope is still explicit: both SDKs cross all three pagination query forms at page size three; the auth history crosses all six role/operation cells, demotion, two equal-body successful sends, and deletion of one occurrence at a time. Unique pagination bodies remain unique after uppercase conversion, excluding the transformed-key ambiguity that would make those histories invalid. Arbitrary operation sequences, page sizes, role values, token lifetimes, and malformed protocols are not generated or claimed. | +| ORC-005, production path and observation | Satisfied. Both drivers use actual SDK clients, actual deployed upstream functions, and real Collections. Pagination compares ordered input to `select` immediately after initial preload, then compares keyed public rows after source acceptance. Later checkpoints poll for the expected source revision before checking the public Collection. Auth waits for each real mutation outcome, then source acceptance, and compares marker multiplicity after both permitted and denied operations. Unauthenticated Collection preload and invalid-token HTTP query are distinct controls. No concrete missing-input hypothesis remains for the stated cuts. | +| ORC-006, calibration | Satisfied with the attributed controls above. Disabled continuation fails immediate source-order comparison on both clients. Bypassed server permission checks fail denial observations. The skipped initial selector is excluded from calibration. These controls target two specific wrong designs, they are not an exhaustive mutation campaign. | +| ORC-007, generated campaigns and replay | Not applicable, there is no generated property or `fc.commands` history in these new receiving files. Each named query/client history is directly selectable. Pinned source commits and fresh local deployments support reconstruction, but they are not seed/path replay or random campaigns. Existing primary pagination and offline owners retain those generated campaigns. | +| ORC-008, state minimality | Satisfied for the reference histories. Pagination retains successful messages in insertion order because later queries need their values, author membership, and reverse order, including writes from earlier histories against the shared backend. Counts alone cannot predict those observations. Auth's multiset must retain multiplicity because two identical sends create two rows and deleting one leaves one. Collapsing them to a set predicts the wrong intermediate public snapshot. Server IDs and timestamps are deliberately omitted from the model because the compared projection and legal deletion targets do not require predicting them. | +| ORC-009, vocabulary and abstraction | Satisfied. Collection rows, source acceptance, public snapshot, and cleanup retain the glossary meanings. The pagination ledger models successful application sends, not provider subscriptions or core sync transactions. Auth bodies abstract away server IDs while preserving the multiplicity that the observations need. TCP gate state is transport availability, not Collection readiness. The invalid-token HTTP control does not establish invalid-token WebSocket behavior. | +| ORC-010, failure fidelity and cleanup | Satisfied for the exercised harness. Registered Collection, SDK, TCP gate, backend, checkout, and runtime cleanup uses the existing calibrated withTestCleanup instrument. It preserves a primary comparison or setup error while reporting secondary release failures and attempts subsequent releases. The local backend startup catch now preserves the startup cause if stopping fails. Auth records its runtime path before preparation, clears stale contents before copying, and can clean a partial preparation. The runtime is scoped and ignored. This review inspected those paths; it does not claim fault injection at every construction instruction or process-kill cleanup. | +| ORC-011, independent second formulation | Satisfied for the concrete shared-fault concern motivating these witnesses. Adapter-authored backend functions could reproduce the same assumptions as the adapter and primary numeric range fixture. These tests instead deploy independently maintained public application functions at verified pins and compare them with an authored operation ledger or permission table. Auth's permission table differs structurally from upstream numeric hierarchy. No additional concrete shared semantic fault was identified that requires another model. This is not an exhaustive absence claim. | +| ORC-012, review evidence | Satisfied by this versioned record tied to the exact source/test commit. It distinguishes independent inspection from author/root execution, records the unreached control, reports the harness/provenance repairs, and retains scope limits. No adapter bug-class closure is claimed by these test-only changes. They receive existing laws at additional application and transport boundaries. | +| ORC-013, distinguishing boundary witnesses | Satisfied within the fixed cases. Authentication distinguishes no identity, an invalid token, and a genuine signed token accepted by server functions. The role history distinguishes read from write for send, write from admin for delete, and demotion after a successful write; every role/operation cell is exercised. Two equal-body sends followed by single deletions distinguish multiplicity from set membership. Pagination crosses one page into several with 17 initial appends at page size three, and includes another author to distinguish indexed membership from an unfiltered mirror. These fixed witnesses do not establish arbitrary thresholds or workloads. | +| ORC-014, controlled-premise handoff | Satisfied for the named receiving premises. Both actual SDK clients and a real local backend now supply cursor pagination, query transformation, successful/denied authenticated mutations, and subscribed source updates. A real auth action issues the tested JWT, neither setAdminAuth nor a fabricated identity supplies the tested user. The TCP gate forwards uninterpreted bytes, destroys live connections, and refuses new ones while closed. The test waits for the adapter client's public connection state to become disconnected before an independent direct writer commits. Old rows remain public; later ledger rows must arrive after reopening the gate. This receives real SDK reconnection, without protocol-message simulation. These app-specific ledgers and histories do not literally reuse the primary numeric fixture grammar, they require different expected application fields and auth policy. Their relationship is source snapshot refinement and complete finite acquisition; shared-law drift remains a reason to keep the primary owners and pinned-source review. | + +## Repairs and remaining limits + +Review identified setup and evidence gaps, with no adapter defect: + +- Auth runtime contents needed an ignore rule and a fresh-copy prerequisite. + Both are present at the reviewed commit, and the known runtime path is + registered before copying so partial setup can be removed. +- Local backend startup could lose its original error if stopping also failed. + It now uses the existing failure-preserving cleanup instrument. +- The first auth model was a set and the history admitted only one successful + send. The final history covers the full role/operation table and uses a + multiset, so repeated equal-body sends and one-at-a-time deletion are explicit. + +The upstream auth demo deliberately lets an authenticated user choose a role. +This is its documented demo behavior, not evidence of secure role administration. +The test does not establish tenant isolation, sign-out or token revocation, +refresh-token rotation, browser auth components, OAuth redirects, email delivery, +or production identity infrastructure. Token corruption is tested through HTTP; +unauthenticated preload and genuine token use are tested through both WebSocket +SDKs. Authenticated reconnect is not exercised. The reconnect witness uses the +separate unauthenticated public pagination app. + +The pagination observations project `author` and `body`; they deliberately omit +upstream `_id` and `_creationTime`, which the transformed query does not return. +Auth compares marked body multiplicity, not complete raw message records or +user/author fields. These are business-ledger observations, not full raw-row +fidelity. The existing backend owner checks IDs and application fields separately. + +Pagination observes complete finite results and live appends, not arbitrary +updates/deletes, continuous growth, transport work bounds, bounded lifetime +subscriptions, or browser rendering. Transformed rows have stable keys only +because the authored bodies are unique, including after case conversion. The +ledger assumes a fresh deployment and sequential execution, both supplied by +setup and test configuration. Auth compares test-marked messages within the +upstream query's latest-100 window, not every application row in an arbitrary +large database. + +The public applications are deployed locally with the package's installed SDK +and backend, with an independently supplied app shell. They are not unchanged +full frontend deployments or evidence for their originally declared dependency +matrix. GitHub availability, local backend download, and operating-system TCP +behavior remain test prerequisites. No production cloud service, third-party +OAuth system, power-loss atomicity, or arbitrary network scheduling is proved. diff --git a/docs/contributing/oracle-reviews/2026-10-26-convex-lazy-paging-source.json b/docs/contributing/oracle-reviews/2026-10-26-convex-lazy-paging-source.json new file mode 100644 index 0000000000..4eb777369c --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-26-convex-lazy-paging-source.json @@ -0,0 +1,29 @@ +{ + "starting_head": "8c24922026fb4a714cb54b992ac85faa2e876742", + "binding": "reviewed working tree; SHA-256 file contents; excludes local install override and this manifest/record", + "files_sha256": { + ".changeset/convex-collection.md": "c7ad56ad983fe653f0054f0a82fcd47cad46c1d59087d62d79a21ffb077eb324", + "docs/collections/convex-collection.md": "3901d5d040f8e101369f1a16a160d4e5d0df391ec48408f99d995191ad013105", + "docs/contributing/oracle-coverage.md": "ea22bb4760150ccc90bfc461c39aa6a435c5b74953a08b378052e6d0523569d7", + "packages/convex-db-collection/README.md": "53b984ef9156a52cea7cb92a81d34d6543508d2dbf048d8f2aeb361ae996d259", + "packages/convex-db-collection/e2e/lazy-paging-oracle.test.ts": "7a5c1390879e4e01757d020e22bfa24f5852a2cf5bb4e18faa4174ce47a02e48", + "packages/convex-db-collection/e2e/local-backend.ts": "e38be0c546a885f55b004f23bc894ab86d3c3b5880bfd9dde8208866a42aea5d", + "packages/convex-db-collection/package.json": "1ca78ea8f3805fc6839852025d63d852b9f756ce1dd35742a527f74ab6c71300", + "packages/convex-db-collection/src/index.ts": "4247e2bbd168a80791861f4553c10d0d878504a7cdc6a65d4bc648e15b1e51ba", + "packages/convex-db-collection/src/options.ts": "ea2c2efc4d574080c5e03da8e121b79406640c667a1e260ead9eff22f6253d33", + "packages/convex-db-collection/src/paginated-options.ts": "de0e92849d363f2545823d96829964084b1a115b890f0f521f9bdc0958ea2b76", + "packages/convex-db-collection/src/pagination-demand.ts": "d2be74c1f3610c153c2b7c3c9411aa6ea216db7e76c9b0a7f32d63ef2bde57ce", + "packages/convex-db-collection/src/pagination.ts": "0308668d07db701d1f7f891d001a4aca2798973ea29bf1d4b00931eaa0e05f1d", + "packages/convex-db-collection/src/subscription.ts": "5e9c72f271ff97721d25bb6a00b190633e79166254ca17173df7ffb5d4c19f57", + "packages/convex-db-collection/tests/lazy-paging-driver.ts": "2c0c8f928845daaaa54605273d01c86e2f9b576137466ed7867e262f39aaa7f7", + "packages/convex-db-collection/tests/lazy-paging-model-oracle.ts": "7410e80bd8ae376a7d7960db3bbc0dee7aecf9ece16038e2cdf90279266cb40d", + "packages/convex-db-collection/tests/lazy-paging-oracle.property.test.ts": "53e1c496514225ef153529e3d3ce361d67d4e01ec3a90ac5becc1c19ae5b8867", + "packages/convex-db-collection/tests/sdk-cache-oracle.test.ts": "0e011a20ce991195742bdfa0cd570d831105604ad8e3e2cc4b8fab4b62daad58", + "packages/convex-db-collection/tests/types.test-d.ts": "3ff1a42d6caf95d14e5270b632340524a9f796b978ccd031835bd1f2d0dab6dd", + "packages/convex-db-collection/tsconfig.json": "623bc70a72fd718e7d209c90a08df12cced0e9045a3565058623e34e946a5710", + "packages/convex-db-collection/vitest.lazy.e2e.config.ts": "e377ee3a835edd1215c48654db863d9ed27406ca67ae3b34c846e4ed253a030c", + "packages/react-db/tests/convex-lazy-paging-oracle.test.tsx": "0a33955d45816d766100b89e45dd63ce32125864cf3e2f0ea46533a582b0333b" + }, + "analytical_freeze_verified": true, + "local_install_files_unchanged": true +} diff --git a/docs/contributing/oracle-reviews/2026-10-26-convex-lazy-paging.md b/docs/contributing/oracle-reviews/2026-10-26-convex-lazy-paging.md new file mode 100644 index 0000000000..43faf31f19 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-26-convex-lazy-paging.md @@ -0,0 +1,255 @@ +# Convex lazy paging implementation evidence — 2026-10-26 + +This implements the maintainer-approved fixed-query lazy-paging design on PR +2230, starting at `8c24922026fb4a714cb54b992ac85faa2e876742`. The companion +[source manifest](2026-10-26-convex-lazy-paging-source.json) binds this record to +the reviewed working-tree files; it excludes the record itself and the preserved +local installation override. Earlier analytical design documents and their +63 TLA+ configurations remain frozen. Their original statements about work +not yet executed are historical; this record supplies implementation evidence. + +## Law and boundary + +`convexPaginatedCollectionOptions` supports `syncMode: 'on-demand'` using the +existing pager and subset protocol. One fixed server query declares its finite +numeric leading order. DB can order trailing terms locally because an acquired +window establishes every row tied at its leading boundary. Offset requests +establish the preceding prefix; whole-source requests may exhaust the query. +General query translation and arbitrary application filters/orders are outside +the initial public profile, as is array `select` in lazy mode. + +Authority is the approved October 26 design, now stated in the +[collection guide](../../collections/convex-collection.md), the public +`LoadSubsetOptions`/`LoadSubsetFn` and applied-receipt contracts in +`packages/db/src/types.ts`, and ordered window publication in +`packages/db/src/query/live/ARCHITECTURE.md`. + +A successful acquisition establishes its exact scope and waits for its actual +establishing writes to apply. Abort cannot withdraw accepted writes. No demand +or a zero limit starts no page. A new successor range needs delivered evidence +that an active scope remains incomplete. This is not a total call, memory, +network or scan budget. Cached completion, one batch and multiple actual batches +are legal. A live query retains its previous complete window while acquisition, +movement or repair is pending. Cleanup fences obsolete callbacks, including +after restart, and the old dependent graph stays terminal. + +The independent model orders authored rows for value/coverage checks. Its +separate work relation sees only delivered rows and whole-source exhaustion; +it cannot consult the hidden suffix. Opaque cursor intervals belong to the +transport driver. Production cursor parsing, expression evaluation, pager +state and source Collection rows do not compute expected results. + +## Grammar, observation and measured reach + +Five properties each run 60 fixed-seed histories (`20261026`) and 60 histories +without a seed, using the same arbitrary, recorder, comparison and budget. +Every declared state below was reached by **60 histories in each campaign**; +`createOracleReach` enforces a per-campaign minimum of one. These are 600 +property histories, not a universal proof. + +| Family / replay prefix | Declared states | +| --- | --- | +| `CONVEX_LAZY_VALUES_ORACLE` | offset-crosses-page; scoped-unlimited-tie; continuation-scope; whole-source-exhausted; authoritative-empty; required-split; recommended-split; short-nonterminal; unsupported-shape-rejected; rejected-unload-preserves-peer | +| `CONVEX_LAZY_WORK_ORACLE` | no-demand; zero-limit; finite-with-unread-tail; gap-free-growth-across-page; shallow-read-during-growth; peer-survives-release; same-object-pending-attempts | +| `CONVEX_LAZY_PUBLICATION_ORACLE` | pending-initial-window; pending-window-move; pending-boundary-repair; completed-boundary-refill; coherent-cache-before-callback; refetch-cache-before-callback | +| `CONVEX_LAZY_LIFECYCLE_ORACLE` | actual-establishing-set-checked; abort-before-acceptance; abort-after-acceptance; stale-after-restart; same-run-reconnect-recovery; cleanup-start-fences-callback; held-cleanup-fences-callback; old-dependent-stays-terminal | +| `CONVEX_LAZY_RECEIPT_ORACLE` | reentrant-second-held-receipt; cached-load-waits-for-latest | + +The arbitrary bounds source size to 16–32, provider page size to 1–5, base +offset/limit to 1–5, both directions and callback orders, 32 independently +authored texts of length 0–8, and a 0–8-step limit suffix with values 1–12. +Values use adjacent two-row ties, explicit scoped ties/continuations, a separate +empty source, and short/split scenes at page size four. The offset scene adds +the page size and observes successor work before crediting a crossed page. +Work uses unique ranks, page size four, top-three then top-six, a held deeper +tail and a healthy cached peer; its generated suffix moves finite limits. +Publication uses page size two and unique ranks to force held initial/moved +windows, deletion of all acquired rows and refill, then cache-only refetch. +Lifecycle uses generated page partitions and direction with two abort cuts, +reconnect, held cleanup and restart. The receipt family varies rows, text, +direction and page size, opening the second optimistic hold synchronously from +the first batch's actual public publication. + +Not every shared arbitrary field contributes to every family: offset/limit and +reverse delivery belong to values; reverse also belongs to publication; the +suffix belongs only to work. No semantic credit is claimed for ignored fields. +Removing ties loses coverage-vs-display discrimination; removing holds loses +early settlement/publication; removing growth loses gap-free continuation; +removing reentry loses the older-receipt overwrite; removing cleanup/restart +loses run authority. Constructive prefixes preserve those witnesses under +shrinking. Invalid directions, counts, filters and cursor predicates are nearby +negative controls. Invalid numeric caps and malformed order declarations have +separate bounded configuration checks, including every startup status event. + +The actual factory, Collection `_sync` normalization and ordered live-query +entry points run production. Every public live publication and checkpoint read +is retained. Completion checks reject a responsive-provider quiescent stall by +name. The fixture's nonquiescent turn guard is diagnostic, not a product cap. +`withTestCleanup` preserves primary failures and secondary cleanup diagnostics; +held gates and dependent resources are released on failing histories too. + +## RED, repairs and hostile controls + +The original eager runtime failed the new work oracle at the no-demand +comparison. A second calibration enabled only the proposed public type shape, +leaving runtime unchanged, and reproduced that semantic failure. This is RED +for a newly proposed capability, not a claim that the original public API +already advertised lazy mode. The log is `red-public-enabled.log`. + +Further extended scenes produced semantic RED before their repairs: + +- The split scene rejected an unbounded right split child as an unrelated new + successor (`split-witness1.log`). Children now stay within the parent's + returned interval. The existing eager oracle then caught missing continuation + after bounded split children (`green-campaigns2.log`); eager exhaustion was + restored without changing that oracle's expectations. +- Submitting the same immutable request object twice stranded its first waiter + at the named progress comparison (`duplicate-red.log`). Pending duplicate + attempts now share completion. +- A public callback opened a second optimistic hold and installed a newer + source revision before the older receipt continuation ran. A cached third + load incorrectly settled with the old row (`receipt-red.log`). The older + callback now clears `visible` only if it still identifies that receipt. +- Invalid lazy startup emitted loading → ready → error before validating its + cap (`invalid-cap-red4.log`). Subscription construction now precedes the + empty initial acceptance. The first acquired page still does not replay an + initial truncate. Earlier missing-import attempts were setup failures; + assuming `preload` must preserve the adapter's RangeError class was an + overly specific expectation. The final check preserves the adapter error and + checks the actual readiness law. +- Malformed runtime order declarations failed rejection before validation + (`order-runtime-red.log`). The type-level numeric-field check also first + failed as an unused `@ts-expect-error` (`types-order-red.log`). + +Seven temporary production mutants were each run, shrunk, replayed directly, +restored byte-for-byte, and checked with a repaired direct replay: + +| Mutant | Intended failure | +| --- | --- | +| eager startup | no row range without demand | +| unbounded right split child | unrelated successor at the work comparison | +| settle at boundary without its full tie | missing/current row at coverage | +| skip applied receipt | load settled before accepted rows applied | +| older receipt overwrites newer held receipt | cached load skipped newer establishing receipt | +| overwrite pending same-object attempt | quiescent responsive provider left load pending | +| stop after first sealed page | same named progress comparison | + +All seven were semantic comparison failures, not timeouts, setup failures or +unreached paths. All mutant direct replays failed and all repaired replays +passed. Each used seed `20261026` and this 34-component shrink path: + +```text +0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +``` + +For example, from `packages/convex-db-collection`: + +```sh +CONVEX_LAZY_RECEIPT_ORACLE_SEED=20261026 \ +CONVEX_LAZY_RECEIPT_ORACLE_PATH=0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \ +../../node_modules/.bin/vitest run tests/lazy-paging-oracle.property.test.ts \ + -t 'keeps the newer establishing' --coverage.enabled=false --typecheck.enabled=false +``` + +The audit's original findings and closure were checked independently by the +Query and lifecycle reviewers. Review also repaired timeout-only progress, +nonconstructive offset reach and a vacuous/final-only React publication check. +The current React recorder requires an actual committed ready target tuple. + +## Receiving witnesses and validation + +The SDK receiver feeds legal multi-query wire transitions through both real +SDKs and checks every callback against the shared publication relation. It +positively observes the callback comparison. This closes the coherent-cache +premise at the SDK boundary; it does not stand in for server cursor evidence. + +The backend receiver uses Convex 1.47.0 and cached local backend +`precompiled-2026-10-10-72ce13c`. Both clients passed no-demand, finite unread +prefix, offset growth, live delete/move, required/recommended splits, a native +four-row leading tie with DB's independent trailing client-ID order, and real +TCP reconnect. Stable-source work checking observed three permitted successor +starts per client. The strengthened run observed eight/ten required-split +responses and four native tie rows per client. Response counts can repeat; +they are not a call or split-count guarantee. + +React's actual `useLiveInfiniteQuery` receives the shared authored values for +provider page sizes 1–4, seven rows with ties and four displayed page counts. +Every committed ready data/page/parameter/lookahead tuple must be the complete +old tuple until the new one appears, then cannot revert; the target tuple must +actually occur. Settled fetching flags are also checked. + +Executed validation: + +- Complete adapter runtime: **10 files / 77 tests**, including all five + fixed/random lazy campaigns and existing eager, snapshot, ownership, offline + and native-value owners (`runtime-final1.log`). +- Full isolated backend suite: **5 files / 20 tests**, including 36 durable + crash/replay histories, pinned public apps and real auth + (`backend-full1.log`). The strengthened native-tie receiver subsequently + passed both clients (`backend-native-ties.log`). +- Core replay, transaction, ordered lifecycle and cleanup owners: + **5 files / 458 tests** (`core-owners.log`). This is delegated core evidence, + not native Convex replay coverage. +- React receiver and existing infinite-query tests: **17 tests** + (`react4.log`); the strengthened every-ready-tuple receiver passed its four + cases (`react-final.log`). +- Package TypeScript including public type assertions: + `tsc --noEmit --rootDir ../..` passed (`types-final.log`). Stock Vitest + typechecking had the same workspace rootDir errors on baseline; runtime + campaigns disable that runner and use this separate successful check. +- Changed-file ESLint, `pnpm lint:changed`, formatting and `git diff --check` + passed. The oracle lint script prints a harmless missing local `main` + diagnostic while successfully using `origin/main`. +- Both required minified size comparisons built successfully. Combined exports + are **4,096 gzip / 3,700 brotli bytes**, versus no package at the origin/main + merge base. Against the starting PR revision they grow **1,228 gzip / + 1,106 brotli bytes**. Existing app scenarios change by zero bytes. The exact + baseline uses archived source and the same installed dependency versions. + +The existing page chain and snapshot writer supply both modes; there is no +second pager, factory, mutation queue or recovery state machine. New bytes pay +for explicit capability parsing and coverage/waiter/receipt obligations. The +split, tie, progress and receipt mutants show why removing those distinctions +would violate the feature. Test/driver/documentation growth is outside the +shipped measurement. + +Local install state was preserved byte-for-byte: the pre-existing +`baseline-browser-mapping` override selects **2.11.30 instead of 2.11.32**. +Neither the override nor lockfile changes belong in this implementation. +Missing test/build dependencies were reused at their declared installed +versions through ignored local links; no new dependency downgrade was made. + +## ORC audit and limits + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Authority and excluded profile are beside the model and in the public guide. | +| ORC-002 | Authored array/window recomputation and delivered-only work knowledge import no pager semantics. | +| ORC-003 | Contract, model, grammar, driver and comparison prose remain in the directly named executable modules. | +| ORC-004 | Constructive witnesses, bounds, per-family axis use, ablations and invalid neighbors are documented above. | +| ORC-005 | Real Collection/live-query path, every publication/read, named pending cuts and positive receiving observations. Installed source facts are read before comparison; no concrete unresolved missing-input hypothesis remains within these cuts. | +| ORC-006 | Seven semantic mutants, direct replay and restored GREEN; wrong-result controls reject missing ties and transient windows. | +| ORC-007 | Same fixed/random property and budget; checked seed/path replay without a command replayPath because no fc.commands grammar is used. | +| ORC-008 | Value model is stateless recomputation. The work recorder retains delivered knowledge, exhaustion and exact active scopes because each changes next successor permission; public trace keeps whether the new window appeared because it changes allowed observations. | +| ORC-009 | Delivered evidence is provider-visible knowledge; applied receipts are actual core promises; transport cursor tables are authored intervals, not DB cursor state. Scope/coverage/publication remain separate. | +| ORC-010 | Shared cleanup helper preserves primary/secondary failures; gates, requests, dependents, sockets and backends release during shrinking. | +| ORC-011 | Shared hidden-suffix fault is challenged by paired suffix controls and delivered-only work judgment. Native cursors and local trailing ties use real server queries as receiving evidence. The frozen formal projections are distinct analytical formulations with their own limits, not a production equivalence claim. | +| ORC-012 | This versioned record, source manifest, findings/repairs, validation and explicit remaining limits bind the claim. | +| ORC-013 | Adjacent boundary ties, zero/nonzero, page-crossing growth, before/after acceptance, cached newer-held receipt and unchanged-source refetch distinguish the plausible wrong rules; mutants fail there. | +| ORC-014 | Real SDK cache scheduling, native splits/ties/reconnect and actual React ready tuples supply the named controlled-premise handoffs. Host replay/delayed cancellation remain delegated, not silently claimed. | +| ORC-015 | All 33 declared states reach 60 histories per fixed and random campaign, with enforced minima. | + +No combined unbounded proof follows from these finite tests or the TLA+ +projections. Continuous nonquiescent resealing has only a diagnostic guard. +Independent range loading, other framework receivers, general query +translation and broader provider/host timing remain outside this claim. + +An arbitrary external truncate wrapper is incompatible with the snapshot +writer's private previous/initialized state and is not a native Convex reset +mechanism. It earns no replay coverage here. The primary core replay owners +cover failed replay gates, authoritative recovery and delayed participation. +The adapter supplies real batches and prompt cancellation; it does not invent +multiple progressive commits for every acquisition. Source restart can cause +an errored dependent's retained subscription to request the whole source; +its graph remains terminal. The cleanup driver explicitly accounts for that +actual demand and makes no zero-post-error-acquisition promise. diff --git a/docs/contributing/oracle-reviews/2026-10-26-convex-oracle-reach.json b/docs/contributing/oracle-reviews/2026-10-26-convex-oracle-reach.json new file mode 100644 index 0000000000..364288b02a --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-26-convex-oracle-reach.json @@ -0,0 +1,495 @@ +{ + "reviewParent": "de7cf8de13f14dfa0d4ce6676452eb231ef215e2", + "minimumPerNormalLane": 1, + "reports": [ + { + "log": "adapter-final.log", + "property": "CONVEX_VALUES_ORACLE", + "campaign": "fixed", + "minimum": 1, + "counts": { + "insert": 35, + "update": 31, + "delete": 34, + "different-operands": 66, + "equal-payload-update": 21, + "changed-payload-update": 10, + "removed-field-update": 14, + "int64": 14, + "special-float": 19, + "binary": 19, + "nested-container": 70, + "undefined": 39 + } + }, + { + "log": "adapter-final.log", + "property": "CONVEX_OFFLINE_ORACLE", + "campaign": "fixed", + "minimum": 1, + "counts": { + "none": 60, + "args": 60, + "mutation": 60, + "sync": 60 + } + }, + { + "log": "adapter-final.log", + "property": "CONVEX_OFFLINE_ORACLE", + "campaign": "random", + "minimum": 1, + "counts": { + "none": 60, + "args": 60, + "mutation": 60, + "sync": 60 + } + }, + { + "log": "adapter-final.log", + "property": "CONVEX_VALUES_ORACLE", + "campaign": "random", + "minimum": 1, + "counts": { + "insert": 39, + "update": 26, + "delete": 35, + "different-operands": 65, + "equal-payload-update": 13, + "changed-payload-update": 12, + "removed-field-update": 15, + "int64": 22, + "special-float": 22, + "binary": 21, + "nested-container": 68, + "undefined": 29 + } + }, + { + "log": "adapter-final.log", + "property": "CONVEX_OFFLINE_SOURCE_ORACLE", + "campaign": "fixed", + "minimum": 1, + "counts": { + "idle": 60, + "pending": 60, + "startup-error": 60, + "empty": 60, + "rows": 60, + "error": 60, + "cleanup": 60, + "provider-recovery": 60 + } + }, + { + "log": "adapter-final.log", + "property": "CONVEX_PAGINATION_ROWS", + "campaign": "fixed", + "minimum": 1, + "counts": { + "complete-nonempty-snapshot": 60, + "complete-empty-snapshot": 60, + "required-split": 60, + "recommended-split": 60, + "pending-range-with-prior-snapshot": 60, + "public-snapshot-publication": 60 + } + }, + { + "log": "adapter-final.log", + "property": "CONVEX_OFFLINE_SOURCE_ORACLE", + "campaign": "random", + "minimum": 1, + "counts": { + "idle": 60, + "pending": 60, + "startup-error": 60, + "empty": 60, + "rows": 60, + "error": 60, + "cleanup": 60, + "provider-recovery": 60 + } + }, + { + "log": "adapter-final.log", + "property": "CONVEX_PAGINATION_ROWS", + "campaign": "random", + "minimum": 1, + "counts": { + "complete-nonempty-snapshot": 60, + "complete-empty-snapshot": 60, + "required-split": 60, + "recommended-split": 60, + "pending-range-with-prior-snapshot": 60, + "public-snapshot-publication": 60 + } + }, + { + "log": "adapter-final.log", + "property": "CONVEX_PAGINATION_WORK", + "campaign": "fixed", + "minimum": 1, + "counts": { + "initial-multi-range-exhaustion": 60 + } + }, + { + "log": "adapter-final.log", + "property": "CONVEX_PAGINATION_WORK", + "campaign": "random", + "minimum": 1, + "counts": { + "initial-multi-range-exhaustion": 60 + } + }, + { + "log": "adapter-final.log", + "property": "CONVEX_PAGINATION_BOUND", + "campaign": "fixed", + "minimum": 1, + "counts": { + "accepted-at-cap": 60, + "overflow-preserves-prior": 60, + "recovered-after-shrink": 60 + } + }, + { + "log": "adapter-final.log", + "property": "CONVEX_PAGINATION_BOUND", + "campaign": "random", + "minimum": 1, + "counts": { + "accepted-at-cap": 60, + "overflow-preserves-prior": 60, + "recovered-after-shrink": 60 + } + }, + { + "log": "adapter-final.log", + "property": "CONVEX_OFFLINE_ACCEPTANCE_ORACLE", + "campaign": "fixed", + "minimum": 1, + "counts": { + "first-empty": 60, + "first-rows": 60, + "accepted-empty-insert": 60, + "accepted-row-insert": 60, + "accepted-row-update": 60, + "accepted-row-delete": 60, + "accepted-row-replacement": 60, + "unchanged-cache-retry": 60, + "changed-cache-retry": 60, + "restart-recovery": 60 + } + }, + { + "log": "adapter-final.log", + "property": "CONVEX_OWNERSHIP_ORACLE", + "campaign": "fixed", + "minimum": 1, + "counts": { + "direct:idle": 60, + "direct:ready": 60, + "direct:cleaned-up": 60, + "shallow:idle": 60, + "shallow:ready": 60, + "shallow:cleaned-up": 60, + "fresh:idle": 60, + "fresh:ready": 60, + "fresh:cleaned-up": 60, + "descriptor:idle": 60, + "descriptor:ready": 60, + "descriptor:cleaned-up": 60, + "independent-confirmation": 60, + "rightful-after-rejected-reuse": 60, + "rightful-after-sibling-cleanup": 60 + } + }, + { + "log": "adapter-final.log", + "property": "CONVEX_OWNERSHIP_ORACLE", + "campaign": "random", + "minimum": 1, + "counts": { + "direct:idle": 60, + "direct:ready": 60, + "direct:cleaned-up": 60, + "shallow:idle": 60, + "shallow:ready": 60, + "shallow:cleaned-up": 60, + "fresh:idle": 60, + "fresh:ready": 60, + "fresh:cleaned-up": 60, + "descriptor:idle": 60, + "descriptor:ready": 60, + "descriptor:cleaned-up": 60, + "independent-confirmation": 60, + "rightful-after-rejected-reuse": 60, + "rightful-after-sibling-cleanup": 60 + } + }, + { + "log": "adapter-final.log", + "property": "CONVEX_OFFLINE_ACCEPTANCE_ORACLE", + "campaign": "random", + "minimum": 1, + "counts": { + "first-empty": 60, + "first-rows": 60, + "accepted-empty-insert": 60, + "accepted-row-insert": 60, + "accepted-row-update": 60, + "accepted-row-delete": 60, + "accepted-row-replacement": 60, + "unchanged-cache-retry": 60, + "changed-cache-retry": 60, + "restart-recovery": 60 + } + }, + { + "log": "adapter-final.log", + "property": "CONVEX_SNAPSHOT_ORACLE", + "campaign": "fixed", + "minimum": 1, + "counts": { + "string-keys": 150, + "numeric-keys": 150, + "mixed-key-types": 150, + "equal-looking-typed-keys": 150, + "selected-key-representation": 150, + "omitted-key": 150, + "omitted-field": 150, + "empty-result": 150, + "reintroduced-key": 150, + "duplicate-rejected": 150, + "valid-after-error": 150, + "initial-valid": 150, + "initial-duplicate-rejected": 150 + } + }, + { + "log": "adapter-final.log", + "property": "CONVEX_SNAPSHOT_ORACLE", + "campaign": "random", + "minimum": 1, + "counts": { + "string-keys": 150, + "numeric-keys": 150, + "mixed-key-types": 150, + "equal-looking-typed-keys": 150, + "selected-key-representation": 150, + "omitted-key": 150, + "omitted-field": 150, + "empty-result": 150, + "reintroduced-key": 150, + "duplicate-rejected": 150, + "valid-after-error": 150, + "initial-valid": 150, + "initial-duplicate-rejected": 150 + } + }, + { + "log": "offline-final.log", + "property": "OFFLINE_CODEC_ORACLE", + "campaign": "fixed", + "minimum": 1, + "counts": { + "insert": 19, + "update": 19, + "delete": 22, + "different-operands": 38, + "equal-payload-update": 6, + "changed-payload-update": 13, + "removed-field-update": 9, + "int64": 35, + "special-float": 46, + "binary": 41 + } + }, + { + "log": "offline-final.log", + "property": "OFFLINE_CODEC_ORACLE", + "campaign": "random", + "minimum": 1, + "counts": { + "insert": 17, + "update": 19, + "delete": 24, + "different-operands": 36, + "equal-payload-update": 9, + "changed-payload-update": 10, + "removed-field-update": 9, + "int64": 44, + "special-float": 35, + "binary": 45 + } + }, + { + "log": "backend-e2e-repaired.log", + "property": "CONVEX_BACKEND_INSERT_ORACLE", + "campaign": "fixed", + "minimum": 1, + "counts": { + "disk-admission": 6, + "replay": 6, + "known-acknowledgement": 3, + "uncertain-acknowledgement": 3, + "payload-int64": 2, + "payload-special-float": 2, + "payload-bytes": 2 + } + }, + { + "log": "backend-e2e-repaired.log", + "property": "CONVEX_BACKEND_INSERT_ORACLE", + "campaign": "random", + "minimum": 1, + "counts": { + "disk-admission": 6, + "replay": 6, + "known-acknowledgement": 3, + "uncertain-acknowledgement": 3, + "payload-int64": 2, + "payload-special-float": 2, + "payload-bytes": 2 + } + }, + { + "log": "backend-e2e-repaired.log", + "property": "CONVEX_BACKEND_UPDATE_ORACLE", + "campaign": "fixed", + "minimum": 1, + "counts": { + "disk-admission": 6, + "replay": 6, + "known-acknowledgement": 3, + "uncertain-acknowledgement": 3, + "payload-int64": 2, + "payload-special-float": 2, + "payload-bytes": 2 + } + }, + { + "log": "backend-e2e-repaired.log", + "property": "CONVEX_BACKEND_UPDATE_ORACLE", + "campaign": "random", + "minimum": 1, + "counts": { + "disk-admission": 6, + "replay": 6, + "known-acknowledgement": 3, + "uncertain-acknowledgement": 3, + "payload-int64": 2, + "payload-special-float": 2, + "payload-bytes": 2 + } + }, + { + "log": "backend-e2e-repaired.log", + "property": "CONVEX_BACKEND_DELETE_ORACLE", + "campaign": "fixed", + "minimum": 1, + "counts": { + "disk-admission": 6, + "replay": 6, + "known-acknowledgement": 3, + "uncertain-acknowledgement": 3 + } + }, + { + "log": "backend-e2e-repaired.log", + "property": "CONVEX_BACKEND_DELETE_ORACLE", + "campaign": "random", + "minimum": 1, + "counts": { + "disk-admission": 6, + "replay": 6, + "known-acknowledgement": 3, + "uncertain-acknowledgement": 3 + } + }, + { + "log": "values-followup-final.log", + "property": "CONVEX_VALUES_ORACLE", + "campaign": "fixed", + "minimum": 1, + "counts": { + "insert": 35, + "update": 31, + "delete": 34, + "different-operands": 66, + "equal-payload-update": 21, + "changed-payload-update": 10, + "removed-field-update": 14, + "int64": 14, + "special-float": 19, + "binary": 19, + "nested-container": 70, + "undefined": 39 + } + }, + { + "log": "values-followup-final.log", + "property": "CONVEX_VALUES_ORACLE", + "campaign": "random", + "minimum": 1, + "counts": { + "insert": 40, + "update": 41, + "delete": 19, + "different-operands": 81, + "equal-payload-update": 25, + "changed-payload-update": 15, + "removed-field-update": 22, + "int64": 19, + "special-float": 25, + "binary": 24, + "nested-container": 75, + "undefined": 39 + } + }, + { + "log": "serializer-followup-final.log", + "property": "OFFLINE_CODEC_ORACLE", + "campaign": "fixed", + "minimum": 1, + "counts": { + "insert": 19, + "update": 19, + "delete": 22, + "different-operands": 38, + "equal-payload-update": 6, + "changed-payload-update": 13, + "removed-field-update": 9, + "int64": 35, + "special-float": 46, + "binary": 41 + } + }, + { + "log": "serializer-followup-final.log", + "property": "OFFLINE_CODEC_ORACLE", + "campaign": "random", + "minimum": 1, + "counts": { + "insert": 22, + "update": 16, + "delete": 22, + "different-operands": 38, + "equal-payload-update": 9, + "changed-payload-update": 7, + "removed-field-update": 11, + "int64": 44, + "special-float": 39, + "binary": 40 + } + } + ], + "validationStages": { + "full-suites": "Before final expected-original capture; normal grammar and comparisons otherwise unchanged.", + "codec-followup": "Final captured-original sources, complete serializer file and Convex values owner." + }, + "reviewedImplementationRevision": "5aebb71f90919697b459b38673a0aa5fef00a0b2" +} diff --git a/docs/contributing/oracle-reviews/2026-10-26-convex-oracle-repairs.md b/docs/contributing/oracle-reviews/2026-10-26-convex-oracle-repairs.md new file mode 100644 index 0000000000..4446774534 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-26-convex-oracle-repairs.md @@ -0,0 +1,156 @@ +# Convex oracle repairs, October 26 + +PR: . +Review parent: `de7cf8de13f14dfa0d4ce6676452eb231ef215e2`. +Reviewed implementation revision: `5aebb71f90919697b459b38673a0aa5fef00a0b2`. +The [source manifest](2026-10-26-convex-oracle-source.json) identifies the exact tested files. Older +records and shrink paths remain evidence for their named revisions. + +These are oracle and test-runner repairs. Production sources are unchanged. +The parent implementation passes the stronger comparisons. RED below means a +hostile implementation rejected by the repaired owner, not a baseline product +failure. Private production mutants were restored after each experiment. + +## Law and repaired distinctions + +The authority is the public [Convex collection guide](../../collections/convex-collection.md), +the [offline transaction guide](../../guides/offline-transactions.md), and the +Collection lifecycle and optimistic-history contracts named in AGENTS.md. + +| Owner | Promised observation and missing distinction repaired | +| --- | --- | +| Snapshot | Latest valid selected rows replace the complete keyed public result. Numeric and string keys retain their types, including `1` versus `'1'`. Constructive prefixes require omission, field removal, empty/reintroduction, duplicate rejection and recovery. | +| Ownership | One concrete options object has one Collection owner. Direct/shallow reuse, fresh factory calls and descriptors are crossed with idle, ready and cleaned-up cuts; confirmation and sibling cleanup remain independent. | +| Primary pagination | Every ordered `select` array and keyed public publication is complete. During each coherent source revision the trace permits prior snapshots followed by current snapshots, including repeats, and forbids prefixes or stale rollback. Pending replacement ranges retain only the prior complete result. The omitted default accepts 10,000 and rejects 10,001 rows. | +| Offline bridge | Rejected durable source acceptance is terminal until cleanup/restart, including rejection after accepted empty or nonempty rows. Exact error identity, prior application rows, changed/unchanged cached retries and fresh-run recovery are checked. Ordered bridge stages and cold acceptance retain separate judgments. | +| Shared codec and Convex receiver | Fresh durable readers preserve each supplied original, modified and changes operand independently, including native values and removed fields. Real CRUD supplies independently authored before/after values. Expected observations are captured before the writer; an in-place mutation cannot rewrite the expected original. | +| Backend crashes | Durable admission precedes provider calls; restart keeps the key and produces one application after known or uncertain completion acknowledgement. Reach is credited only after actual disk/server/replay comparisons. | +| Public pagination receiver | Every observed select/publication must equal a complete authored ledger revision available at that moment, including live writes and TCP reconnect through both SDKs and three query forms. Future writes cannot excuse an earlier observation. | + +## RED and hostile controls + +The original review executed five false-green designs: numeric-key +stringification, prefix-before-full pagination, default cap 9,999, terminal +acceptance only over empty Collections, and v4 original-from-modified routing. +The repaired owners reject all five at their intended comparisons. An +independent cross-review also found and executed an in-place original-corruption +survivor; capturing the expected original before the writer closes that gap. + +| Isolated wrong design | Intended rejection and adjacent witness | +| --- | --- | +| Stringify snapshot map keys | Typed public entries differ. The adjacent stringified delete-key design also fails when a numeric row is omitted. Seed `20261022`, path `0:0:0:0:0`; restored replay passes. | +| Publish one-row prefix, then full rows | Retained select trace rejects the prefix. Primary seed `20261022`, path `0:0:0:0:0:0:0:0:0:0`; direct RED and restored GREEN. Adjacent current→empty→current fails monotonicity. The real public receiver rejects the same prefix in all six histories; publication-only diagnostic also fails both SDK histories. | +| Default cap 9,999 or 10,001 | Exact 10,000 acceptance rejects the former; 10,001 overflow rejects the latter. Both fail assertions rather than timing out. Configured-cap replay uses seed `20261023`, path `0:0`. | +| Latch acceptance failure only when Collection is empty | Accepted-nonempty prefix followed by rejected insert resolves a retry that must reject. Seed `20261023`, path `0:0`; intended promise assertion RED and restored direct GREEN. | +| Encode v4 original from modified | Both codec owners reject at `original operand`. The narrower update-only routing mutant also fails: shared seed `20261022`, path `2:1:1:1:1:1`; Convex path `2:1:1:1`. Direct restored replay passes. | +| Update-only `Object.assign(mutation.original, mutation.modified)` before encoding | Both previous repaired owners passed because expected original aliased the writer input. Capturing its observation before writer entry makes both fail at `original operand`, with the same update replay coordinates above; restored production passes. | +| Omit uncertain backend acknowledgement histories | Grammar/runner negative control fails `uncertain-acknowledgement (0)` for each operation. This calibrates reach enforcement, not a provider mutation kill. Normal real-backend campaigns reach both acknowledgement cuts. | + +These results are intended assertion failures. Setup failures, absent runtime +downloads, discovery warnings and timeouts are not counted as rejected mutants. +Fixed default-cap cases and six public receiving histories are deliberate fixed +witnesses, alongside the generated primary owners. + +## Reach and GREEN + +Normal generated properties run identical fixed and unseeded campaigns with +fresh ledgers and a minimum of one history for every declared state. Counts are +histories, not callbacks or events. Every normal report is retained in +[the reach record](2026-10-26-convex-oracle-reach.json). Explicit seed/path replay runs directly, +sets fast-check's run budget to one and bypasses campaign minima. Reconstructing +a shrink path can still evaluate its callback more than once. + +| Owner | Actual normal reach in each lane | +| --- | --- | +| Snapshot | All 13 declared states reach 150 histories: string/numeric/mixed/equal-looking typed keys, selected-key representation, omitted key/field, empty, reintroduction, duplicate rejection, valid recovery, valid start and invalid start. | +| Ownership | All 15 states reach 60: four construction/reuse forms × three lifecycle cuts, independent confirmation, rightful owner after rejected reuse and after sibling cleanup. | +| Pagination rows/work/bound | All ten states reach 60: complete nonempty/empty, required/recommended split, pending with prior snapshot, public publication, initial multi-range exhaustion, acceptance at cap, overflow retention and shrink recovery. | +| Offline bridge/source/acceptance | All 22 states reach 60: four stage outcomes; idle/pending/startup-error/empty/rows/error/cleanup/provider-recovery; first-empty/first-rows/accepted-empty-insert/accepted-row-insert/update/delete/replacement/unchanged-cache-retry/changed-cache-retry/restart-recovery. | +| Shared codec | 60 histories per lane; all ten CRUD, operand-separation, equal/changed/removed-field update and native-class states exceed the minimum. Exact fixed/random counts are in the JSON record. | +| Convex values | 100 histories per lane; all 12 CRUD, operand, native-class, nested-container and undefined states exceed the minimum. Exact counts are in the JSON record. | +| Backend insert/update | Six actual process histories per operation per lane: admission/replay 6, known/uncertain acknowledgement 3 each, int64/special-float/bytes 2 each. | +| Backend delete | Six actual process histories per lane: admission/replay 6, known/uncertain 3 each. Total backend crash histories: 36. | + +The complete validation run passed 64 adapter tests, 302 offline-engine tests, +seven campaign/reach helper tests, and all 18 real-backend tests. The final +two-file codec follow-up passes after capturing expected original observations +before writer entry. Offline typecheck and build pass with a test-only tsconfig +that permits the shared helper import; production build configuration is +unchanged. Configured ESLint, oracle lint and diff checks pass. + +Commands from the repository root (the environment setting suppresses pnpm's +automatic dependency revalidation for this already installed local workspace): + +```sh +pnpm_config_verify_deps_before_run='' pnpm --filter @tanstack/convex-db-collection test +pnpm_config_verify_deps_before_run='' pnpm --filter @tanstack/offline-transactions exec vitest run +pnpm_config_verify_deps_before_run='' pnpm --filter @tanstack/offline-transactions typecheck +pnpm_config_verify_deps_before_run='' pnpm --filter @tanstack/offline-transactions build +pnpm_config_verify_deps_before_run='' pnpm --filter @tanstack/db exec vitest run tests/oracle-campaign.test.ts tests/oracle-reach.test.ts +CI=1 pnpm_config_verify_deps_before_run='' pnpm --filter @tanstack/convex-db-collection test:e2e +pnpm_config_verify_deps_before_run='' pnpm lint:changed +``` + +Replay selectors remain those in the coverage map. For example, set +`CONVEX_SNAPSHOT_ORACLE_SEED=20261022` and +`CONVEX_SNAPSHOT_ORACLE_PATH=0:0:0:0:0` when selecting the snapshot owner. +Shared codec uses `OFFLINE_CODEC_ORACLE`; Convex codec uses +`CONVEX_VALUES_ORACLE`. No commands arbitrary is used, so there is no command +`replayPath` interface. Old backend paths identify the old grammar; the new +constructive bundles change those coordinates. + +## Grammar controls and review outcomes + +Reconstruction includes every original false-green witness, the adjacent +mutants above, and their direct restored replays. Constructive prefixes retain +arbitrary suffixes and generated values. Ablating numeric keys loses typed-key +discrimination; omitting earlier accepted rows loses later-rejection evidence; +merging codec operands loses update routing; retaining only the final callback +loses partial/stale trace evidence; omitting uncertain acknowledgement loses +the named crash cut and fails reach. + +Domains remain bounded: snapshot uses three key slots, 0–5 rows and 1–30 suffix +snapshots; numeric key parameters lie in -100..100. Pagination crosses page +sizes 1–2 and 3–4 with all split policies, a 3→8 prefix, generated replacements +and a final empty result. Shared codec uses arrays of 1–5 native leaves; +Convex trees have depth two and arrays up to three. Binary sizes are 0–12 and +0–8 respectively. Acceptance scenes vary values and retry order/counts. +Backend bundles vary payloads and acknowledgement order within six actual +histories per operation/lane. Duplicate-key proposals are invalid but remain +explicit rejection inputs; malformed cursor tokens, out-of-range native +integers, update/delete of absent fixture rows and simultaneous uncontrolled +source revisions are outside these legal grammars. + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Authority and bounded omissions are stated beside each owner and above. No product promise was changed to match output. | +| ORC-002 | Expected arrays/typed entries, dependency prefixes, authored operand observations and append ledger are independent of production transitions. The codec cross-review found and removed a mutable expected-input alias. Core's supplied mutation composition, native brand checks and test storage are explicit trusted boundaries. | +| ORC-003 | Each changed owner retains opening contract prose and distinguishes grammar, model, real entry point, recorder and comparison. Campaign/reach utilities supply execution support, not another product oracle. | +| ORC-004 | Reconstruction, ablation, bounded ranges and nearby invalid inputs are accounted for above; constructive prefixes add reach without removing suffix variation. | +| ORC-005 | Real Collections, OutboxManager/fresh readers and actual backend processes reach their named public comparisons. Retained callbacks represent intermediate violations. No concrete missing-information hypothesis remains for these controlled checkpoints; arbitrary Convex query translation is outside scope. | +| ORC-006 | Every repaired false-green distinction has an executed hostile comparison failure. Backend enforcement has a separately labeled omitted-state control. | +| ORC-007 | Fresh fixed/random lanes use one callback and budget; direct captured RED/restored GREEN replay is exercised. Seven helper tests include lane separation, seed zero/path forwarding, run budget and independence from unrelated DB replay selectors. | +| ORC-008 | Retained prior/current pagination phase distinguishes a later stale callback. Accepted rows plus terminal failure distinguish same-cache retry from restarted acceptance. Allowed receiving revisions are frozen at observation time so future ledger writes cannot rewrite prior judgments. No production cursor state is copied into the value model. | +| ORC-009 | Source acceptance versus visibility follows the Collection contract. Scene names and reach states map to concrete fixture cuts in owner prose. Backend counts distinguish bundles from actual process histories. | +| ORC-010 | Cleanup harnesses preserve original comparison failures; direct shrinking reproduces their named assertions. Assertions in the public receiver run outside provider callbacks so they cannot turn into projection errors. Broader infrastructure failure handling is not newly proved. | +| ORC-011 | No unresolved named shared semantic fault requires another model for these repairs. Independent reviewers challenged pagination, codec and acceptance/backend/helper changes; the expected-original alias challenge was executed and fixed. Arbitrary query semantics and native persistence-fault refinement remain outside scope. | +| ORC-012 | This exact-source record gives the applicable outcomes, original/adjacent witnesses and remaining limits. Closure is confined to the tested contract × grammar × path × observation cells. | +| ORC-013 | Exact 10,000/10,001 cases reject either adjacent default. Accepted empty/nonempty scenes distinguish the terminality boundary. Distinct and equal-payload real updates distinguish operand routing. Prior/current/prefix traces distinguish trace consequences. Other untested reusable boundaries retain their existing owners. | +| ORC-014 | Real public queries supply actual cursor continuation, ordered pages and reconnect through both SDKs. Their append ledger checks complete available revisions; it cannot reuse the synchronous revision-step checker because stream delivery can precede the writer acknowledgement and does not label revision boundaries. Drift risk is limited explicitly: within-phase monotonicity, arbitrary update/delete and split scheduling remain primary controlled-owner evidence. Backend native payloads receive disk/process/server transport for a bounded subset; native storage failures remain unresolved in their existing owners. | +| ORC-015 | Every changed important generated property declares states and enforces per-lane minimum one. Actual counts are versioned in the companion JSON. Fixed public histories/default-cap examples are not generated campaigns, so this trigger does not apply to those cases. | + +## Limits + +No on-demand functionality was added. The adapter remains eager and drains the +fixed server query. Lazy cursor acquisition could use the existing on-demand +protocol; a separate factory is not inherently required. Arbitrary DB filters +and ordering cannot automatically become arguments to an application-defined +Convex function. Every successful load would still owe its requested subset. + +The public receiving relation proves completeness and availability, not +within-phase monotonicity; the primary pagination owner checks that relation. +These repairs do not prove arbitrary concurrent source revisions, cloud +hosting, atomic cross-Collection publication, cold backend replay with native +disk failure, multi-tab/atomic batches, power-loss behavior, arbitrary custom +objects/cycles or stricter byte/peak-memory/lifetime-subscription bounds. +The coverage map retains the existing owners for those laws. diff --git a/docs/contributing/oracle-reviews/2026-10-26-convex-oracle-source.json b/docs/contributing/oracle-reviews/2026-10-26-convex-oracle-source.json new file mode 100644 index 0000000000..4073a8e401 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-26-convex-oracle-source.json @@ -0,0 +1,26 @@ +{ + "reviewParent": "de7cf8de13f14dfa0d4ce6676452eb231ef215e2", + "reviewedImplementationRevision": "5aebb71f90919697b459b38673a0aa5fef00a0b2", + "sha256": { + "docs/collections/convex-collection.md": "c432cd6a6b2cc926c382b75220407dae53ea76e4a914393e3a0b516ff6e85f70", + "docs/contributing/oracle-coverage.md": "3b040397b513301a247aa8bf202c5efd18f543a23745a8c889ccda84a64d1c80", + "packages/convex-db-collection/e2e/backend-oracle.test.ts": "e30455f6066988c6a6bf9594fce5fac834ce0a4e132da3c67db7c5b65986f363", + "packages/convex-db-collection/e2e/public-pagination-oracle.test.ts": "bc2315e2e493762b9b11785876d67de19d76a4f541663f9e22ce71897f337711", + "packages/convex-db-collection/tests/offline-oracle.property.test.ts": "02a86f41c6efc350442435156cd4e645652a538a0aaded47a0d9399a7e6ceeb1", + "packages/convex-db-collection/tests/ownership-oracle.property.test.ts": "65571b825260f5cba81910bd2c09d4c059381718a22092594732ea97bf39a4b1", + "packages/convex-db-collection/tests/pagination-oracle.property.test.ts": "edee7b6bdb287523e54bc4d2acbae9d9239376f67ec89b1e41ec4247e98ef1b8", + "packages/convex-db-collection/tests/snapshot-oracle.property.test.ts": "014848222ce1390918edd96eb87ac8f12639d805334e8ce1254942fbf90e06b7", + "packages/convex-db-collection/tests/values-oracle.property.test.ts": "656a180d80f1e5dede569487b7ebf4a64a2f463c4c9180fa7539479eba8c0130", + "packages/db/tests/oracle-campaign.test.ts": "9bd9847227c02ea294247d29fb263103e24f354b02c56762da53fb0b0bbf734a", + "packages/db/tests/oracle-campaign.ts": "eb62aba6b24c596be97a7fe607c4c39808f7bef12ae5e0e2add547a45f8849d5", + "packages/db/tests/oracle-reach.ts": "f32b74e0adfaf8d869edaa8aeb2d1d8ac0a413ddd61b91ff706bde1767959283", + "packages/offline-transactions/package.json": "2f729365bcdfefc77a948954a1b4bbccfb46683612fa3f46fab24942493a6c7a", + "packages/offline-transactions/tests/transaction-serializer-oracle.property.test.ts": "94d3d1aa21f52c3e88b17530653644258f2f44c8283ca70f7aae4ca6199b3278", + "packages/offline-transactions/tsconfig.test.json": "5168aca44188a9be3b0bee215bfc2f222010dedb8f4e1fc08029c6873dcb6cf1", + "packages/offline-transactions/vite.config.ts": "1314f58bd503c40e633342fb6ed05a28a4d03641280159d65b34e6913226bb63" + }, + "excludedLocalInstallationWorkaround": [ + "pnpm-workspace.yaml", + "pnpm-lock.yaml" + ] +} diff --git a/docs/contributing/oracle-reviews/2026-10-26-main-merge-size.md b/docs/contributing/oracle-reviews/2026-10-26-main-merge-size.md new file mode 100644 index 0000000000..38b73d0c3a --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-26-main-merge-size.md @@ -0,0 +1,102 @@ +# Main merge and size comparison, 2026-10-26 + +Measured merge: `ed22e033c335dd6a3e3b84290a0961885a7bfd9f`. Main: `5649a07d468117891193c2e4cde9c7706308b1cd`. +The merge's other parent is `b4955406a57e0032caffd7d494ca66799c45dd00`. + +## Integration contract and evidence + +The existing SSR law remains: the first browser view reproduces the rendered +server rows and readiness. An infinite query uses the identity of its initial +window for that transport. Authority: `docs/guides/ssr.md`, the existing +`ssr-snapshot-admission-oracle.ts`, and its React receiving driver in +`descriptor-query-binding-oracle.test.tsx`. + +Main moved infinite-query binding into core. The merge carries the original +window query's hash through that binding and passes its client and hash to the +window controller. It retains the server snapshot callback and main's reclaim, +identity, and render-rejection boundaries. + +RED: the first merge resolution computed the hash from a newly created +Collection instead of the query. The unchanged SSR oracle rejected it in seven +checks: prepared query admission, first browser rows/readiness for ready and +cold/loading sources, and the fixed and random campaigns. This attempted +simplification is the hostile wrong design: its generated Collection ID cannot +name the preloaded query window across server and browser. The repair retains +the initial query hash instead. The same receiving comparisons are GREEN. +No oracle row or readiness expectation was weakened. + +Main's shared React identity law rejects an unkeyed opaque query with +`UnhashableQueryIRError` during preparation. Its authority is the identity +contract in `packages/db/tests/conformance/contract.ts` from main. Two older +warm/cold streaming checks expected a later custom SSR error. Their assertion +now requires the earlier error class. Keyed controls still require successful +rendering and transported rows. This changes error precedence to the law +merged from main; it does not permit untransported rows. + +The existing infinite SSR grammar reached the following states in normal runs: + +| State | Fixed | Random | +| --- | ---: | ---: | +| Cold/loading | 19 | 22 | +| Unbacked ready-empty | 5 | 6 | +| Unbacked rows | 43 | 42 | +| Prepared query result | 18 | 17 | +| Normalized ready source | 12 | 18 | +| Normalized partial source | 23 | 15 | +| Pending query | 19 | 26 | +| Server write | 29 | 35 | +| Browser write | 27 | 31 | +| Same-query browser preload | 14 | 16 | + +Validation: 701 React runtime tests, 482 scoped core tests, 109 Svelte tests, +13 additional Svelte server-to-browser receiving tests, and 9 Router tests: +1,314 tests. Normal staged lint, oracle lint, production error erasure, +error-document consistency, private-name cache checks, and the minified public +API check passed. The size builds also completed. Vitest's optional in-test +typechecker reported cross-package fixture/rootDir errors; these runtime runs +use `--typecheck.enabled=false`. This is not a claim of a clean full typecheck. + +This bounded integration run does not establish all framework scheduling paths +or close the earlier whole-PR evidence gaps. Their existing owners and limits +remain in the coverage map. + +## Shipped bytes against main + +Vite packages and app scenarios use `pnpm size:compare --exact-base --files +--packages db,react-db,react-router-with-db`. Dependencies are external for +package bundles; scenarios inline their workspace dependencies and leave React +and TanStack Query external. All use production definitions, minification, +gzip level 9, and Brotli level 11. + +| Bundle | Main gzip | Merge gzip | Gzip change | Brotli change | +| --- | ---: | ---: | ---: | ---: | +| db | 103207 | 104108 | +901 (+0.9%) | +737 | +| react-db | 4921 | 4964 | +43 (+0.9%) | +44 | +| react-router-with-db | 943 | 968 | +25 (+2.7%) | +27 | +| app | 102142 | 102608 | +466 (+0.5%) | +350 | +| app-effect | 104582 | 105075 | +493 (+0.5%) | +450 | +| app-group-by | 102034 | 102482 | +448 (+0.4%) | +378 | +| app-includes | 102215 | 102686 | +471 (+0.5%) | +408 | +| app-join | 102212 | 102683 | +471 (+0.5%) | +387 | +| app-local-storage | 105055 | 105520 | +465 (+0.4%) | +383 | +| app-optimistic-action | 102288 | 102755 | +467 (+0.5%) | +570 | +| app-paced-mutations | 103326 | 103785 | +459 (+0.4%) | +497 | +| svelte-db | 2220 | 2231 | +11 | +11 | + +Svelte uses its own `svelte-package` build rather than Vite library mode. Its +package measurement compiles packaged `.svelte` and `.svelte.js` modules in +production client mode, then bundles every export with esbuild and external +package dependencies. It uses the same compression settings as the Vite +package measurements. It is a package measurement, not a Svelte app scenario. + +The typical app grows by 466 gzip bytes (0.5%) and 350 Brotli bytes. The +all-exports package measurements are separate views and must not be added to +that app's size. Tests and this evidence document are outside shipped bundles. +This task measures the merge; it does not attempt a new size optimization. + +The package proxy returned HTTP 403 for a newly locked tool dependency and +public npm was unavailable. Both source trees therefore used identical cached +build tools: Vite 7.3.6, esbuild 0.28.1, and TypeScript 5.9.3. The measurement +compares the exact main and merge sources with that common toolchain; it does +not certify a fresh installation of the merged lockfile. The normal commit +lint hook passed with pnpm's dependency auto-install disabled. diff --git a/docs/contributing/oracle-reviews/2026-10-26-pr-2257.md b/docs/contributing/oracle-reviews/2026-10-26-pr-2257.md new file mode 100644 index 0000000000..9c2ad31fd0 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-26-pr-2257.md @@ -0,0 +1,1214 @@ +# PR #2257 oracle review — 2026-10-26 + +Reviewed PR: https://github.com/TanStack/db/pull/2257 + +Reviewed head: `b585f92f20de9cd89a6783ac67784d4bdf9b7a18`. +Four independent reviewers used the repository's oracle-review skill to examine +contract authority, generated histories, listener recording, and deliberately +incorrect cleanup variants. Oracle-authoring was read before experimental test +changes. Production and oracle source in the shared review checkout remain unchanged. + +## Findings + +### P2: Generate cleanup during partial delivery and reset + +Location: `packages/electric-db-collection/tests/electric-sdk-delivery-oracle.property.test.ts:2415-2417`, with the response driver at 2337-2359. + +Every successful generated response includes both a row and `up-to-date`. +Consequently `cleanup-while-loading` means no delivered rows or only retries; +it never includes a transaction opened by a partial row batch, or an active +reset awaiting replacement data. The law promises listener release whatever +state the source reached. These are legal ordinary stream histories: +`electric-sdk-framing.test.ts:29-35` already demonstrates row-only responses +and HTTP 409 `must-refetch` delivery through the real SDK. + +Two single-condition changes to the production cleanup pass the existing +fixed/random listener campaigns and managed-cache rotation case: + +```ts +if (!transactionStarted) stream.unsubscribeAll() +// Separately: +if (!isResettingSnapshot) stream.unsubscribeAll() +``` + +The first also passes all 42 existing SDK delivery tests. A row-only response, +next actual HTTP request, then cleanup distinguishes the first change: the PR +leaves zero listeners; the incorrect variant leaves one. An initial HTTP 409 +`must-refetch`, next actual request, then cleanup distinguishes both changes +in eager, on-demand, and progressive modes. The Collection remains `loading` +before cleanup, and the final listener comparison is the failing assertion. + +Repair the primary history grammar with row-only and reset answers separate +from `up-to-date`. Permit cleanup before that later marker and declare reach +for partial-delivery and reset cleanup. Include nearby retry, give-up, abort, +and restart histories when claiming coverage of the full stated class. Keep +the independent active-run model; do not copy production transaction/reset +flags into it. This finding concerns oracle coverage. The PR's unconditional +production release passes these cases. + +### P2: Release retained listeners after a failing test + +Location: `packages/electric-db-collection/tests/electric-sdk-delivery-oracle.property.test.ts:331-334` and 2396-2401. + +The recorder's `restore()` restores method spies but leaves any remaining DOM +listener registered. With the original missing release, Collection cleanup +already completed; repeating that idempotent cleanup cannot remove the +listener. Thus each failing or shrinking run leaves the dead SDK stream +reachable from the shared document. ORC-010 requires test resources to be +released on failure too. + +A direct real-SDK/jsdom diagnostic recorded one actual listener after the +original cleanup, one after exact recorder restoration, and zero after +explicit SDK release. This is a diagnostic plus source tracing of the test's +cleanup path, not a complete generated-campaign teardown experiment. + +Preserve the primary assertion, then remove remaining registrations owned by +the recorder during final teardown, or retain stream handles for independent +test cleanup. Add a deliberate failure case showing both the original +mismatch and final resource release. Do not remove listeners before comparing. + +### Review-evidence gap: preserve the final evidence with the reviewed revision + +The PR adds a new property but no versioned exact-head review record. Its +reported RED/GREEN results, reach counts, and rejected incorrect variants +live in the editable PR description. ORC-012 requires verdict-critical +external evidence in a versioned record, including applicable requirement +outcomes and reasons for non-applicable ones. This does not imply the reported +runs did not occur. This local review record is tied to the reviewed head; +it has not been committed or published by this review. + +## Contract and scope + +`CleanupFn` in `packages/db/src/types.ts:420-429` and “Sync Implementation” in +`docs/guides/collection-options-creator.md:70-78` authorize release of resources +owned by a completed sync run. After completed cleanup, no run-owned visibility +listener may remain. Counting active runs independently of the adapter is +sufficient for that upper bound. Allowing zero listeners before cleanup keeps +valid freedom for a client that releases earlier, such as on abort. No +unsupported production defect or contract revision was established. + +The production path is a real Collection, the Electric adapter, installed +`@electric-sql/client` 1.5.15, controlled HTTP, and jsdom document registrations. +Experiments used archived copies of the reviewed head and read-only dependency +artifacts. Explicit Vite resolution confirms core and db-ivm source came from +the reviewed archive, not the dirty primary checkout. Scratch source changes +were restored. Coverage instrumentation and TypeScript checking were disabled +for runtime experiments; nonfatal unrelated Expo tsconfig diagnostics appeared. +No setup failure or timeout was counted as an assertion rejection. + +## Executed evidence + +| Case | Reviewed production | Incorrect cleanup variant | +| --- | --- | --- | +| New fixed/random listener campaigns + rotation | 3 pass | Both variants: 3 pass | +| Full existing SDK delivery file | 42 pass | Transaction-condition variant: 42 pass | +| Row-only response then cleanup | 1 pass | Transaction-condition variant: listener assertion fails, 1 versus 0 | +| HTTP 409 reset then cleanup, three modes | 3 pass | Both variants: 3 listener assertion failures, 1 versus 0 | +| Actual listener after recorder restoration | Fixed cleanup releases it | Original missing release: 1 remains; independent SDK teardown removes it | + +Evidence is retained in `review-evidence/pr2257-oracle-review/`. The partial +case patch appends to the reviewed oracle; the reset case text likewise uses +its existing helpers. Exact incorrect variants are the two lines above. + +From the experimental snapshot's `packages/electric-db-collection` directory, +reviewers used the installed Vitest entry point directly: + +```sh +node /Users/kyle.mathews/programs/tanstack-db/node_modules/vitest/vitest.mjs run tests/electric-sdk-delivery-oracle.property.test.ts --coverage.enabled=false --typecheck.enabled=false +``` + +Selectors restricted individual runs to the listener campaigns, rotation, or +appended review cases. Raw logs distinguish each execution and comparison. + +## Original failure and direct replay + +Removing only `stream.unsubscribeAll()` reproduces the original defect in both +listener campaigns: `run 0 cleanup: live listeners`, expected at most zero, +received one. The fixed campaign reduces to one eager run with no answers and +an open ending. Seed `20261024`, path `0:0:0:0`, and property +`electric.sdk-source-release` select exactly one replay test and reproduce the +same cleanup assertion. Restoring the reviewed production makes that identical +replay pass. Both scratch source files were restored to reviewed bytes. + +```sh +TANSTACK_DB_ORACLE_SEED=20261024 TANSTACK_DB_ORACLE_PATH=0:0:0:0 TANSTACK_DB_ORACLE_PROPERTY=electric.sdk-source-release node /Users/kyle.mathews/programs/tanstack-db/node_modules/vitest/vitest.mjs run tests/electric-sdk-delivery-oracle.property.test.ts -t replay --coverage.enabled=false --typecheck.enabled=false +``` + +Final review-checkout runtime validation: all 42 SDK delivery tests pass with +the installed local dependency override. `pnpm lint:changed` also passes. +Production and oracle source remain unchanged; only local installation files +and this review's report/evidence differ from the reviewed head. + +## ORC outcomes + +| Requirement | Outcome at reviewed head | +| --- | --- | +| 001 authority/limits | Supported by public cleanup sources; browser/native limits explicit. | +| 002 independent judgment | Active-run count reads no production classifier. | +| 003 literate responsibilities | Law, model, grammar, driver and comparison are explained together. | +| 004 grammar controls | Gap: row-only and reset cleanup require missing history rules. Other declared bounds/exclusions are stated. Full ablation evidence remains unverified. | +| 005 path/observation | Real SDK and cleanup listener comparison executed. No concrete missing-input hypothesis identified. | +| 006 calibration | Incorrect conditional variants survive existing checks and fail added cleanup comparisons; original missing-release replay recorded separately. | +| 007 campaigns/replay | Both existing fixed/random campaigns executed; direct original-failure replay recorded separately. | +| 008 minimality | Active versus cleaned-up run suffices for this resource law; source bookkeeping governs driver legality/reach. | +| 009 vocabulary | Sync runs/provider sessions distinguished; active-run abstraction explained. | +| 010 failure/cleanup | Primary mismatch preserved; retained DOM resource on failing teardown is a gap. | +| 011 second formulation | Not triggered: no supported shared semantic-model fault requiring another expected-result formulation. | +| 012 review evidence | PR lacks a versioned exact-head record; known reachable distinctions prevent a full coverage claim. | +| 013 distinguishing boundaries | Existing cleanup boundary works; partial-delivery/reset premises absent from generated histories. Added cases distinguish the incorrect conditions. | +| 014 receiving witness | Real installed SDK supplies listener/reset behavior; controlled HTTP supplies authored responses. No live-service or native result claimed. | +| 015 declared reach | Existing counters execute; loading reach does not establish partial-delivery/reset cleanup. Add those premises and report their counts. | + +## Remaining limits and setup + +No live Electric service or React Native AppState run was executed. General +EventTarget callback-reuse/capture semantics are not established by this +recorder, but the pinned SDK uses one fresh default-capture callback per +stream, so those theoretical recorder limitations are not additional findings. +Fixed managed-cache rotation is a valid receiving case; lack of arbitrary +rotation generation alone was not reported as a defect. + +The review worktree is detached at the exact PR head. No remote branch was +published. Dependency installation used `--ignore-scripts` and a local-only +pnpm override for `baseline-browser-mapping` from 2.11.32 to 2.11.29: +2.11.31 also returned 403, and 2.11.30 was absent from registry metadata. +The override and resulting lockfile changes must remain uncommitted. Global +`/Users/kyle.mathews/.codex/AGENTS.md` now records that recovery rule. + +## Repair and review evaluation — 2026-10-26 + +This entry evaluates the four raw reviews and records the authorized repairs. +The preceding entry describes the initial head, not the state after this entry. +Starting head: `b585f92f20de9cd89a6783ac67784d4bdf9b7a18`. +The tested oracle source SHA-256 is `4c997a507c116710a171c6703338b5b3964ff19f35b98ceac8124e9085486dad`. +The commit containing this entry identifies the corresponding repair tree; +production source is unchanged from the starting head. Raw reviewer notes, +full logs, and the append-only working ledger remain task-local rather than +being published as part of the change. + +### Laws, experiments, and inference + +| Law and authority | Previous evidence | Decisive experiment | Repair and remaining enforcement | +| --- | --- | --- | --- | +| Completed sync-run cleanup releases its resources (`CleanupFn`, creator guide). | Complete row batches, retries, terminal outcomes and restarts; incomplete rows/reset absent. | Two conditional-release variants passed the old checks but fail expanded generated cleanup comparisons, each leaving one listener instead of zero. | Generate independent row-only/reset answers; preserve pending authored answers across retries and terminal outcomes; clear them after up-to-date/reset/restart. Same active-run model and public cleanup comparison. No known open distinction in these bounded histories. | +| Test teardown releases owned registrations and preserves its primary failure (ORC-010). | Method spies restored; physical DOM registrations untested after a failure. | Before repair, retained callback fires after teardown. After repair, all eight subsets of three callbacks are removed and the identical primary Error survives. | Recorder finally removes captured registrations after comparison, then restores spies. An independent EventTarget-dispatch comparison checks actual release. No known open distinction for the fresh default-capture callbacks used by the installed SDK. | +| External review evidence remains tied to its reviewed revision (ORC-012). | Evidence only in editable PR prose. | Source inspection of the starting PR; exact-head initial report and tested-source digest recorded here. | This versioned record preserves findings, calibration, reach, limits, and requirement outcomes. This documentary obligation does not need an artificial runtime assertion. | + +The oracle grammar now accepts zero to three answers per run, from `row`, +`up-to-date`, `reset`, and `retry`; one to three runs; all three sync modes; +and open, give-up, or abort endings. The same driver also runs 18 minimal +histories: two incomplete answers × three endings × three modes. These remain +replay examples alongside generated nearby histories, rather than replacing +generation. Readiness reach reads public Collection status; pending-answer +reach derives only from provider inputs, without inspecting production +transaction/reset flags. + +Grammar controls: removing row-only or reset loses the demonstrated cases; +removing up-to-date loses completed-delivery comparisons; removing retry loses +incomplete delivery across retried failure; removing terminal endings loses +give-up/abort cleanup; removing modes loses their installed-SDK paths; removing +multiple runs loses cleanup/restart and already-aborted restart. Zero answers +covers immediate cleanup, three answers can cross reset, row and completion, +and three runs cover repeated release. Requests are answered only when the +real SDK sent them: after give-up or external abort, no later response is +invented. This is a bounded grammar, not a proof for arbitrary history length. + +### Lossless item ledger + +Raw claims and their original proposed repairs were copied into the working +ledger before new tests or edits. Source-order IDs remain distinct even where +multiple reviewers identified the same gap. + +| ID | Source claim | Evidence and technical verdict | PR action | Durable value / destination | +| --- | --- | --- | --- | --- | +| L01 | Cleanup authority and active-run upper bound are sound. | Public cleanup sources; early release remains allowed. | accepted-design | Preserve model and contract prose. | +| L02 | Partial delivery is missing; generate it before cleanup. | Conditional transaction release passed old full file, fails new campaigns. | fixed-now | Expanded grammar, new reach, minimal replay histories. | +| L03 | Failing/shrinking runs retain listeners; add independent final release. | Direct SDK diagnostic and old-recorder RED; bounded dispatch calibration GREEN. | fixed-now | Recorder teardown and primary-error calibration. | +| L04 | Exact-head versioned external evidence is absent. | Starting diff had no review record. | fixed-now | Initial-head record plus this repair entry. | +| H01 | Row-only cleanup needs a grammar rule; one example is insufficient. | Same-path real SDK witness; old 42-test survival retained as initial evidence. | duplicate of L02 | Independent source-resolution evidence and generated repair. | +| H02 | Already-aborted restart sends no requests; dead suffixes are intentionally ignored. | SDK request admission and driver conditions. | accepted-design | Preserve signal continuity and legal-history exclusion. | +| H03 | Fixed rotation is a distinct valid receiving case; arbitrary rotation absence alone is not a defect. | Existing replacement-session and final-zero checks remain. | accepted-design | Keep managed-cache rotation receiver and its stated limits. | +| H04 | Loading reach is too coarse; partial delivery needs declared reach and adjacent endings. | New partial/reset counters and generated retry/ending/restart combinations. | duplicate of L02 | Explicit partial/reset cleanup reach. | +| H05 | Original missing release and direct replay need calibration. | Expanded original-release RED and identical restored-source replay GREEN. | accepted-design | Replayed seed/path coordinates below. | +| R01 | Restoring spies leaves the actual original-defect listener alive. | Same diagnostic as L03; independent final cleanup now checked. | duplicate of L03 | Preserve physical-resource observation. | +| R02 | Recorder count ignores general callback reuse/capture ordering, but pinned SDK never reuses registrations that way. | SDK uses one fresh default-capture callback per stream. | accepted-design | Explicit narrow recorder domain; no general EventTarget counting claim. | +| R03 | Counts alone do not identify old versus current callbacks, but no in-domain surviving incorrect replacement was demonstrated. | Rotation and final zero remain observable; no new product-defect claim inferred. | accepted-design | Retain identity limitation without inventing a finding. | +| R04 | Requiring exactly one active listener would add unsupported timing policy. | Model law is an upper bound and exact zero after cleanup. | duplicate of L01 | Keep legitimate early release. | +| R05 | Warning suppression might erase secondary cleanup diagnostics. | Helper restores console spy before reporting failures. | refuted | Keep exhaustive helper and distinguish primary/secondary diagnostics. | +| R06 | Shrink filtering might erase the original failure. | Original checkpoint is retained; direct replay reproduces it. | refuted | Preserve failure identity and replay evidence. | +| M01 | Reset cleanup is also absent; generate reset separately in all modes. | Both conditional variants fail expanded campaigns; minimal reset cases cross every mode/ending. | duplicate of L02 | Distinct reset action and cleanup reach. | +| M02 | Active visibility behavior and request cancellation are neighboring obligations. | Release oracle explicitly judges resource ownership; surrounding Electric tests pass. | accepted-design | Keep partial-oracle scope and surrounding suite. | +| M03 | Browser/controlled-HTTP evidence does not establish native or live-service behavior. | Actual installed SDK path, explicit original limits. | accepted-design | Preserve external limits in oracle and coverage map. | +| S01 | During repair, source snapshot progress could be confused with public readiness in reach labels. | Independent rereview identified the distinction after reset. | fixed-now | Removed driver readiness flag; classify from public Collection status. | + +Original items: **18 = 3 fixed-now + 5 duplicates + 8 accepted-design + +2 refuted**. One additional repair-time item, S01, is fixed-now. No item is +deferred, awaiting a product decision, or confirmed-open. The final loss audit +returned to the four raw reports, including their non-finding hypotheses and +scope notes; all are represented above. The working ledger remains local. + +### Reviewer assessment + +- Law/authority reviewer: hire. Correct public authority and useful separation + of product correctness, coverage and documentary evidence; appropriately + limited source-only conclusions. +- History reviewer: strong hire. Demonstrated full-file acceptance of a wrong + conditional release, supplied a minimal legal case, and verified reviewed + source resolution rather than assuming dependency reuse was safe. +- Recorder reviewer: hire. Found a real failure-path resource issue, calibrated + physical registrations, and did not promote out-of-domain counting concerns + into unsupported findings. +- Independent variant reviewer: strong hire. Supplied the distinct reset case + across all modes and a second incorrect condition. Correctly distinguished + a coverage defect from a production defect. + +Prioritization and proposed repairs were sound. The initial direct SDK teardown +diagnostic was narrower than a full generated failure-path test; the new +bounded calibration supplies executable regression evidence. Independent +rereviews found no substantive issue with the grammar or teardown repair; +the readiness wording caveat was resolved by S01. + +### RED, GREEN, and replay evidence + +Unchanged PR production passes the expanded resource-release campaigns. +Before recorder repair, the new teardown calibration fails because a retained +callback is called once after cleanup; the identical test passes after repair. +Thus no product defect is attributed to this test-only update. + +Each incorrect production variant is run separately in an archived checkout +with the updated oracle. Both fixed/random campaigns reject each at the +post-cleanup listener assertion, not setup, timeout, or unrelated errors: + +| Variant | Fixed seed | Shrink path | Reduced case | +| --- | --- | --- | --- | +| Omit `stream.unsubscribeAll()` | 20261024 | `0:0:0:0` | One eager run, no answers, open ending. | +| Release only if `!transactionStarted` | 20261024 | `5:0:1:1:1:1` | One eager run, row-only answer, open ending. | +| Release only if `!isResettingSnapshot` | 20261024 | `5:0:1:2:2` | One eager run, reset answer, open ending. | + +Each captured path was replayed directly with +`TANSTACK_DB_ORACLE_PROPERTY=electric.sdk-source-release`, the listed seed/path, +and `-t replay`; each incorrect source fails the same cleanup assertion and +unchanged PR production passes the identical replay. Experimental source is +restored after execution. + +Reach counts are histories, not sync-run totals. A scratch copy logged the +existing reach counters without changing generation, comparison or the +40-history campaign budget: + +| Declared state | Fixed seed 20261024 | Unseeded campaign | +| --- | --- | --- | +| cleanup-while-loading | 10 | 10 | +| cleanup-after-ready | 8 | 5 | +| cleanup-after-give-up | 16 | 16 | +| cleanup-after-abort | 19 | 19 | +| cleanup-of-restarted-run | 27 | 25 | +| cleanup-after-partial-delivery | 4 | 15 | +| cleanup-during-reset | 10 | 12 | + +Commands from the repository root: + +```sh +pnpm --dir packages/electric-db-collection exec vitest run --coverage.enabled=false +pnpm exec eslint packages/electric-db-collection/tests/electric-sdk-delivery-oracle.property.test.ts +pnpm lint:changed +pnpm --filter @tanstack/electric-db-collection... build +``` + +The Electric package run passes 1,130 runtime tests (one skipped), with no type +errors. The changed-file ESLint and oracle-rule checks pass. Builds pass after +linking required cached workspace dependencies. Initial missing dependency and +build-output errors were setup failures, not successful checks. Production +source has no diff, so shipped production-byte weight is unchanged; only tests +and contract/evidence documentation grow. No new changeset is needed for this +test-only follow-up to the PR's existing release changeset. + +### Final requirement outcomes and limits + +ORC-001/002/003: authority, independent active-run judgment, and literate layers +preserved. ORC-004: row/reset reconstruction plus ablation/range/exclusion +accounted for above. ORC-005: real SDK, actual HTTP request fences, listener +comparisons after every answer/terminal step/completed cleanup; no concrete +missing-input hypothesis. ORC-006/007: original and two conditional variants +rejected by fixed/random campaigns; direct failing and passing replays checked. +ORC-008/009: model unchanged; authored pending answers are explicitly driver +reach bookkeeping and public readiness is read directly. ORC-010: primary +failure preserved and actual registrations released by the calibrated finally +path. ORC-011 is not triggered: no supported shared semantic-model fault needs +a second expected-result formulation. ORC-012: this versioned exact-base and +source-digest record closes the documentary gap. ORC-013: row/reset premises +and nearby completed/terminal histories distinguish the incorrect conditions. +ORC-014: installed SDK supplies stream/reset/listener behavior; controlled HTTP +supplies responses. ORC-015: seven declared reaches are checked per campaign, +including the two previously missing premises. + +The claims remain bounded to these generated legal histories, three sync modes, +the installed SDK, controlled HTTP, browser document in jsdom, and existing +managed-cache rotation receiver. There is no live-service or React Native +AppState execution claim. There is no known reachable counterexample inside +this bounded repair claim; sampled campaigns are not a universal proof. +Whole-PR merge readiness is **not assessed** by this review evaluation; remote +CI and later PR changes remain separate from the local checks above. + + +## Second review: enforcement repairs — 2026-10-26 + +Starting head: `1a5ca0edeacf0600f6f6a0d04df32ea90b7a4bea`. +Tested oracle SHA-256: +`5917bd5b125d71d952804aca26e513db99c75e7d46a8efb458cb91a2d563b543`. +The commit containing this entry identifies the repair tree. Production is +unchanged from the starting head. Four fresh reviewers independently examined +authority, histories, recorder behavior, and receiving paths. Raw reports and +the append-only evaluation ledger remain task-local; this entry preserves the +verdict-critical evidence. + +### Law, experiment, inference, enforcement + +Completed cleanup already releases all resources owned by the ended sync run +(`CleanupFn`, `packages/db/src/types.ts:419-429`; creator guide, Sync +Implementation). This deadline includes supported asynchronous error decisions +that have not settled. It does not require disposal on external abort alone, +zero listeners synchronously at cleanup entry, or any minimum listener count +while active. The active upper bound and one-add-per-start control are now +explicitly identified as installed-provider/driver premises. + +| Item | Evidence before repair | Repair and resulting evidence | +| --- | --- | --- | +| R01: removal attempts mistaken for actual listener release | An SDK 1.5.15 variant removes with capture=true after registering capture=false. The previous release campaigns pass, but actual EventTarget dispatch invokes the retained handler. Five of seven focused identity/order traces disagree with the old recorder. | Recorder replays add/remove calls in global invocation order, keyed by callback and normalized capture; duplicate adds coalesce. Native dispatch before fixture teardown independently calibrates every length 0..3 history over two callbacks, add/remove, and five capture spellings: 8,421 histories. The original SDK variant now fails the cleanup comparison. | +| H01: pending async error decision absent from release histories | A conditional release that skips `unsubscribeAll()` only while a user decision remains pending passes all 46 original runtime tests. A held-decision probe rejects it after cleanup, while current production passes all 6 mode/future-outcome cells. | Generate held retry/give-up endings, observe entry into real installed-SDK `onError`, keep each promise unresolved through cleanup and later runs, and resolve only in teardown. Declare pending-decision cleanup reach. Both campaigns reject the conditional design; direct replays fail it and pass unchanged production. | + +The expanded grammar retains row-only, up-to-date, reset, and retried-error +prefixes; all three modes; one to three runs; and the previous open, give-up, +and abort endings. Its fixed partial/reset examples now cross all five +endings, 30 histories. Expected resource ownership remains the independent active +run count. No production branch or internal retry/reset classifier computes +that result. + +### RED, GREEN, reach, and replay + +Recorder calibration was added before repairing its observer. On the old +recorder it fails a legal remove/remove/add history: recorded 0, actual dispatch 1. +The identical calibration passes after repair, including capture mismatch, +remove-before-add, re-add, duplicate-add, callback identity, dual capture, and +equivalent capture spellings. Existing final-release/primary-error calibration +still passes. Comparison remains before recorder.restore. + +Unchanged PR production passes the expanded fixed/random property. Each +campaign uses the same generator, observer, comparison and 40-history budget. +A private copy printed existing reach counters without changing semantics: + +| Declared reach | Fixed 20261024 | Random | +| --- | ---: | ---: | +| cleanup-while-loading | 21 | 22 | +| cleanup-after-ready | 13 | 10 | +| cleanup-after-give-up | 13 | 15 | +| cleanup-after-abort | 14 | 11 | +| cleanup-of-restarted-run | 27 | 24 | +| cleanup-after-partial-delivery | 6 | 17 | +| cleanup-during-reset | 11 | 17 | +| cleanup-during-retry-decision | 18 | 21 | + +Two supplementary 40-history random campaigns pass all reaches, with pending +reach 28 and 23. They supplement the normal pair and do not replace it. + +All four incorrect release designs fail both expanded campaigns at the +intended post-cleanup listener assertion (actual 1, allowed 0), not setup or a +timeout: + +| Incorrect release design | Fixed seed/path | Random seed/path | +| --- | --- | --- | +| Release only after user error decision settles | `20261024 / 1:1:2` | `-93353169 / 3:0:2:2:3` | +| Omit whole-stream release | `20261024 / 0:0:0:0` | `1599666023 / 0:0:0` | +| Release only without a pending source transaction | `20261024 / 5:0:1:1:1:1` | `-795275110 / 1:0:0:1:0:1:1` | +| Release only outside source reset | `20261024 / 5:0:1:2:2` | `-1735334928 / 0:0:1:4:3` | + +Three captured pending-decision replays, including the latest random path +above and `778119691 / 2:0:0`, fail the incorrect design and pass unchanged +production. Each selects exactly one replay test. The latest random witness +fails cleanup of a restarted run. The wrong-capture SDK variant also fails +with the final oracle at seed 20261024/path 0; the unmodified SDK passes that +same replay. Experimental production and SDK changes remain isolated from +the branch. + +Commands from repository root: + +```sh +pnpm --dir packages/electric-db-collection exec vitest run --coverage.enabled=false +pnpm lint:changed +pnpm exec prettier --check packages/electric-db-collection/tests/electric-sdk-delivery-oracle.property.test.ts docs/contributing/oracle-coverage.md docs/contributing/oracle-reviews/2026-10-26-pr-2257.md +``` + +Package result: 1,131 runtime tests pass, one skipped, no type errors. Changed-file +lint and formatting pass. The new bounded calibration is one additional test; +production bytes and behavior are unchanged. No changeset is needed for this +test-only follow-up. + +### Authority clarification and remaining decision + +The completed-run cleanup law above already has authority. Partial rows, +reset, and pending error decisions introduce no new exception or product rule. +The one-listener active bound and exact construction count are pinned SDK +premises, not universal API requirements. + +Same-run cache rotation is a separate timing question. The public restart hook +requires immediate old-callback retirement and replacement after cache rotation +(`types.ts:435-443`; SQLite guide, managed generations). It does not explicitly +state that every old host resource must be released before replacement. Current +Electric source already awaits session cleanup before replacement, while the +existing rotation assertion runs later, after replacement demands settle and a +late old response. That assertion does not establish the earlier deadline. + +Candidate contract: release all retired provider-session resources before +starting its replacement. Rival: permit inert resources until enclosing-run +cleanup, while fencing old rows/acknowledgements. The recommendation is the +candidate, to prevent accumulation over repeated rotations. A user decision +was requested; no new deadline or earlier expectation was silently adopted. +The two independent enforcement repairs above do not depend on that decision. + +### Review evaluation, requirement outcomes, and limits + +The 41 source-order items remain accounted for: 27 accepted-design, 2 fixed-now +(R01/H01), 2 already-fixed, 8 duplicates, 1 refuted, 1 design-decision; none deferred. +Each retains its original claim, evidence and durable destination in the local +ledger. The previous review-only stopping interpretation was superseded after +re-reading evaluate-review's explicit authorization to repair enforcement gaps. +All four reviewers merit hire for their bounded tasks: concrete observer and +history counterexamples, careful authority distinctions, and receiving-path +checks with honest environment limits. A separate source rereview of the final +repair found no further supported issue; runtime results above supply its +remaining execution evidence. + +ORC-001/002/003: existing resource authority and independent active-run result +preserved, with provider calibration distinguished. ORC-004: new pending endings +reconstruct the reported trace and partial/reset/restart neighbors; removing +that ending loses the demonstrated distinction. Recorder enumeration spans all +stated operation/callback/capture sequences; removing an axis loses the named +identity/normalization witness, and length 3 reaches re-add. ORC-005: real +installed SDK receives 503 and enters onError before cleanup; native dispatch +checks actual listener ownership before teardown. No missing source-information +hypothesis is needed to release known owned resources. ORC-006/007: old-observer +RED, final GREEN, incorrect-design assertions and direct fixed/random replays +recorded above. ORC-008/009: semantic model unchanged; held decisions are +authored driver promises, not copied adapter state. ORC-010: primary failure, +secondary diagnostics and independent final fixture release remain intact. +ORC-011: native dispatch supplies a different ownership formulation for the +named shared capture/order fault. ORC-012/013: exact-source record, distinguishing +pending/capture witnesses, and remaining rotation decision recorded here. +ORC-014: installed SDK supplies error-decision and listener behavior; HTTP and +jsdom supply explicitly controlled premises. ORC-015: all 8 declared states reached +in each normal campaign; pending-decision reach is based on actual callback +entry, not merely an authored ending token. Bounded recorder enumeration is +not an important generated property and asserts its exact 8,421-history count. + +Claims remain bounded to installed SDK 1.5.15, controlled HTTP, browser document +in jsdom, the stated captures and passive callbacks, and these finite histories. +Once/signal automatic removal, mutable/exotic option objects, arbitrary callback +mutation during dispatch, actual native hosts, every accepted SDK version, +arbitrary streaming/acceptance schedules, and post-decision effects after +fixture teardown are not established. Separate two-Collection and exact +SDK 1.5.23-source controlled AppState probes pass current production and reject +the base missing-release path, but do not claim checked-in generated peer +isolation or actual iOS/Android execution. No current production defect was +found in these paths. Whole-PR readiness is not assessed by this evaluation. + +## Approved rotation deadline and final rereview — 2026-10-26 + +Parent commit: `c73f47984c54c8625f9614f698a7b98b1d354e20`. +Final SDK-delivery owner SHA256: +`f046cb9cbdf26a8ffc8c746bf762f00d949faf054bc6cbe7e4e5545fd7d11b1f`. +Unchanged Electric production SHA256: +`4919ad548c416952fe51822b9e1d06c20dd5807b6dda40909bc3ed7046b66015`. +These source identities cover the executable change accompanying this entry. + +### Law decision and current production + +The user explicitly selected “Yes — require release before replacement.” A03 +is now implemented in the Electric guide: release the retired provider +session's adapter-owned resources before constructing its replacement +`ShapeStream`. Callback retirement remains immediate; replacement follows +cache-rotation completion. An HTTP request that ignores cancellation may +finish later without gaining authority over replacement rows or waits. + +This is the new explicit deadline that the earlier contract left ambiguous. +Current Electric already satisfies it; no production edit was needed. The +shared restart hook is unchanged: Query recovery reuses observers and shared +resources rather than constructing a replacement provider session. + +| Law | Authority and relation to production | +| --- | --- | +| Completed sync cleanup releases owned resources, including during partial delivery, reset, and a pending error decision. | Existing `CleanupFn` and creator guide. Base production violated this; the PR's unconditional stream release satisfies the measured paths. R01/H01 strengthen observation and reach, without changing this law. | +| Retired Electric session resources are released before replacement construction. | Newly approved A03 deadline, now in the Electric guide. Current production already awaits private cleanup before constructing a replacement. The earlier settled-rotation comparison did not prove this ordering. | +| Each retained subset demand completes after its replacement snapshot applies. | Existing Electric scoped-recovery guide and `LoadSubsetFn`. Current production passes; final rows alone had allowed a plausible early-settlement implementation. | + +### Experiments and repairs + +The existing real-SDK rotation driver now generates one to three sequential +rotations in the same Collection sync run. A retired snapshot arrives during +held rotation, before replacement snapshots, or after replacement settlement. +Fresh IDs and demand unloads preserve an actual pending old snapshot in every +round. A forwarding Proxy records document-listener ownership immediately +before invoking the real installed SDK constructor. It neither disposes +resources nor substitutes SDK behavior. The model requires zero old listeners +at that boundary, one replacement listener after delivery, and zero after +completed Collection cleanup. + +The following experiments distinguish that deadline and preserve its freedom: + +- Disposal immediately after replacement construction fails the new comparison: + expected zero, actual one. Removing only that comparison lets both 40-history + campaigns pass, including all later row, demand, and listener comparisons. +- Disposal after cache rotation fulfills but immediately before construction + passes both campaigns. A diagnostic replay observes one listener while + rotation is held and zero before construction. The oracle therefore does + not require synchronous disposal at rotation entry. +- Disposal only for the initial session fails when a generated second rotation + retires its replacement. A one-rotation example alone would miss this rule. +- Exact final-owner replay pairs use property `electric.sdk-rotation-release`, + seed `20261026`, and paths `0:0:0` and `0:1:1`, respectively. The two incorrect + variants fail at the constructor comparison; unchanged production passes + both. These are assertion failures, not setup failures or timeouts. + +Independent rereview found two additional oracle issues. Both are repaired: + +| ID | Original claim and decisive evidence | Repair and disposition | +| --- | --- | --- | +| S01 | A retired demand can start correct replacement work in the background and report success before its source response. The earlier owner accepted this variant in its example and both generated campaigns. A private public-promise probe observed early fulfillment. | Track both demands and compare pending outcomes while rotation is held and before the first replacement response; require both fulfilled afterward. The same generated histories now reject the variant with `[fulfilled, pending]` versus `[pending, pending]`. Final seed `20261026`, path `0:0:0` fails the variant and passes baseline. `fixed-now`. | +| S02 | Cleanup mutated the settlement array retained by an assertion: the message described `[fulfilled, pending]`, but the later Received diff showed both fulfilled. This lost diagnostic evidence while preserving the failed checkpoint. | Copy primitive settlement/construction arrays at comparison time. The final fixed-example failure preserves `[fulfilled, pending]` in both message and Received after cleanup. `fixed-now`, under ORC-010. | + +S01's provisional reviewer label “R03” is an alias only; the original ledger's +R03 primary-error item retains its identity. A broad variant that returns early +for every load fails the held-rotation pending comparison. The more selective +retired-demand variant fails the later pre-response comparison, so both +neighboring regimes remain distinguishable. + +### Reach, GREEN, and review requirements + +Both normal rotation campaigns run 40 histories with the same grammar, +observations, comparison, and budget; only seed selection differs. The property +is registered in both the oracle registry and named replay manifest. Final-source +diagnostics print counts without changing expectations or driver behavior: + +| Rotation state | Fixed seed 20261026 | Unseeded | +| --- | ---: | ---: | +| Old response during held rotation | 19 | 23 | +| Old response before replacement snapshots | 25 | 18 | +| Old response after replacement settlement | 17 | 22 | +| Initial-session retirement | 40 | 40 | +| Replacement-session retirement | 26 | 28 | +| Both demands await replacement responses | 40 | 40 | + +All eight source-release states also reach their minimum on this final source. +Their fixed/random counts, in declaration order, are +`21/22, 13/10, 13/15, 14/15, 27/31, 6/14, 11/21, 18/16`. + +Validation: + +- Full Electric package after S01: 1,133 runtime tests pass, one skipped, no + type errors. After S02's diagnostic snapshots, the complete changed owner + passes all 49 runtime and three type tests, including both generated laws. +- Core replay/manifest suite: all 38 tests pass, no type errors. +- Changed-file lint, Prettier, and whitespace checks pass. +- R01 wrong-capture and H01 pending-decision release replay pairs were also + rerun after introducing the constructor wrapper: each incorrect design fails + the cleanup assertion; its unchanged-production partner passes. + +ORC-001/002/003: approved deadline and existing subset law are explicit beside +independent zero-listener and pending-demand rules. ORC-004: length 1 reconstructs +the original case; lengths 2–3 distinguish replacement retirement; three delivery +cuts preserve legal late-response timings. Removing the earlier observation +admits the measured wrong ordering. The grammar answers admitted requests and +does not construct replacement before releasing the cache result. ORC-005/006: +real constructor/HTTP path reached; ordering and settlement variants fail their +intended comparisons. ORC-007: equal fixed/random budgets and exact direct +replays pass/fail as stated. ORC-008/009: no copied production state machine; +session/run and physical work/demand settlement remain distinct. ORC-010: +release the held gate before cleanup, retain primary/secondary diagnostics, +release fixture resources, and snapshot primitive observations. ORC-011: no +new shared semantic fault needing a second resource model was identified; +R01's native-dispatch formulation remains. ORC-012/013: this source-bound record, +legal-before/incorrect-after neighbor, and initial/replacement distinction +supply review and boundary evidence. ORC-014: installed SDK supplies real +construction, parsing, listener behavior, and promise delivery; HTTP and the +Map-backed cache supply explicitly controlled premises. ORC-015: all six new +states and eight existing release states are measured and required. + +### Final accounting and limits + +All original raw reports and the two new sibling findings were revisited. +Final totals: **43 = 27 accepted-design + 5 fixed-now + 2 already-fixed + +8 duplicates + 1 refuted**. There are no confirmed-open, deferred, or pending +design-decision items. A03 moved from design-decision to fixed-now after the +explicit approval and implementation. Independent rereview found no further +supported issue; the reviewers merit hire for concrete distinguishing examples, +authority discipline, and checking their own diagnostic limitations. + +This closes the named enforcement gaps within installed SDK 1.5.15, jsdom, +controlled HTTP, Map-backed persistence, sequential one-to-three rotations, +and the stated comparison cuts. Overlapping rotations, actual native hosts, +every supported SDK version, arbitrary failure schedules, every future private +SDK resource, and separate mutation-evidence wait semantics are not established. +No production code changed. Whole-PR readiness remains unassessed by this +targeted evaluation; CI for earlier commits does not cover these changes. + +## Package ESLint follow-up — 2026-10-26 + +CI on `04b1c4b2299a95746c6f64521d54ca4b2804a194` exposed two package +ESLint errors that the narrower `lint:changed` oracle check did not report. +Use a type-only namespace import for the real SDK module type, and remove a +null comparison excluded by the typed listener-call domain. The installed SDK +and bounded calibration both supply non-null callbacks. The measured histories, +expected results, and checkpoints remain unchanged. + +Final owner SHA256: +`f6d2a06d3cab1e1c5b18a2c2b6285b0b8cadb4f805d679feefc45caadd519a66`. +Package ESLint passes for the changed Electric owner and core registry/manifest. +The complete owner passes all 49 runtime and three type tests, including the +8,421-history native calibration and both generated laws. This is a mechanical +follow-up to the evidence above; the 43-item review accounting is unchanged. + +## CodeRabbit follow-up review — 2026-10-26 + +Review `5657996284` covers `04b1c4b2299a95746c6f64521d54ca4b2804a194`. +Its three reported spacing defects in this record were confirmed and corrected. +The intervening package-lint commit changes no oracle law or checkpoint. +Source and formatting checks are the appropriate evidence for these text edits. + +The review body and mutable summary were inventoried separately from the +43-item agent evaluation above. Their 43 source-order entries, including +individual endorsed ranges, repeated claims, metadata, and suggestions, resolve +as **35 accepted-design + 3 fixed-now + 1 already-fixed + 1 refuted + +3 duplicates**. No confirmed-open or deferred item was introduced. The CI +suggestion concerns the lint failure already repaired by `997d21d79`. + +The reported 60% docstring score is a vendor metric; no repository rule imposes +its proposed 80% threshold. The owner already documents its contract, model, +histories, driver, comparisons, and limits. Adding comments solely for that +ratio would not improve the evidence. The native AppState caveat remains valid: +the checked-in lane observes installed SDK 1.5.15 in jsdom. The separately +recorded exact 1.5.23-source probe reaches automatic AppState selection with +controlled native premises, passes current production, and rejects the original +missing release. It does not establish actual iOS/Android runtime behavior or +all supported SDK versions. These limits remain explicit; no native-wide +closure or whole-PR readiness is inferred. + +Reviewer assessment: the spacing correction is precise and useful; endorsements +and the scope caveat are consistent with the recorded evidence. The unsupported +documentation threshold and lack of a new distinguishing experiment limit this +review's depth. Do not hire for independent oracle review on this evidence +alone. The final loss audit revisited the full review body and summary, +including ancillary suggestions and repeated details. No behavioral repair or +new experiment was required by this review. + +## Retired durable-target observation — 2026-10-26 + +CodeRabbit review `5658197144`, item N01, identifies an observation gap in the +managed-rotation owner. The review covers +`997d21d7900bd6a9deed692942ea8d54bf2fdc6b`; this evaluation starts at +`c88dcfe5902be80892d2108f16b9ec764041dbe7`, whose intervening change affects +only this record. The reviewed owner hash is the `f6d2a06d` hash above. +N01 is **fixed-now**. It reveals no reproduced production routing defect. + +### Law and repair + +The SQLite persistence guide's managed-generation contract requires immediate +retirement of expired source callbacks. The Electric guide's “Cleanup and +resume safety” contract fences the retired provider session's late responses. +Its adjacent recovery contract permits another run with a valid claim to keep +using a retired persisted cache generation. Therefore this expired session's +late row cannot enter either durable target; the whole retired map need not be +empty or immutable. + +The independent source model already says that the old predicate has no row. +Each round uses a fresh odd key and now remembers its retired storage target. +The oracle compares that key's absence after the late HTTP response and after +replacement settlement. All prior current-cache, public-row, constructor, +demand-settlement, and cleanup comparisons remain. The grammar still generates +one to three rotations and all three delivery cuts. This adds an observation +and a seventh required reach state, without changing production or model rules. + +### Evidence block + +- **Law:** expired Electric provider-session callbacks have no authority to + persist their late row through either generation; the public guides above + supply the authority. A valid run's retained generation remains usable. +- **Reach:** normal fixed/random campaigns each run 40 histories. In declaration + order, the seven final counts are `19/23, 25/18, 17/21, 40/40, 26/22, 40/40, + 40/40`: during rotation, before snapshots, after snapshots, initial retirement, + replacement retirement, demands awaiting replacement, and retired target + observed. Every minimum is one history. +- **RED:** the old owner accepts a forbidden `oldId` write into the captured + retired map through the fixture's real `applyCommittedTx`, in fixed 40 and + random 40 histories. This confirms the missing observation. With the repaired + owner, both campaigns reject that incorrect result at + `retired response in retired cache: expected true to be false`. +- **GREEN:** unchanged production passes the repaired owner in fixed 40 and + random 40 histories. The complete owner passes 49 runtime and three type + tests with no type errors. Electric package ESLint passes. +- **Mutant:** this is a wrong-result control at the durable fixture boundary, + not a production mutation or evidence that production misroutes callbacks. + The fixed RED starts with `["after-snapshots", "before-snapshots"]` and shrinks + to `["during-rotation"]`, seed `20261026`, path `0:0:0`, preserving the + assertion. Exact replay fails with the control and passes unchanged + production. A second direct replay inserts the wrong row only at the final + cut and fails `retired response in retired cache after replacement`. +- **Limits:** real installed SDK 1.5.15, controlled HTTP, jsdom, one on-demand + Collection, Map-backed durable storage, sequential one-to-three rotations, + and the stated observation cuts. Actual SQLite peer-claim enforcement, + overlapping rotations, native hosts, all supported SDK versions, and a write + inserted and removed entirely between cuts are not established. + +A neighboring-state control seeds an unrelated negative key before expiry and +checks that it remains in retired storage after replacement. Both campaigns +pass. This calibrates acceptance of allowed retained contents; it does not +execute a real peer claim. An earlier scratch control named `valid-peer-write` +used the fixture's one claim ID and is not credited as peer authorization. + +Final owner SHA256: +`7ccf1eb989a5d5a33876601bbca816b35069a5c70352d45ade2631bf58217005`. +Unchanged Electric production SHA256: +`4919ad548c416952fe51822b9e1d06c20dd5807b6dda40909bc3ed7046b66015`. +The diagnostic baseline owner hash is +`1b2ed73a4177382b726445f6b028d945e466561e33f0b41d93b4ee99556b1af4`; +the immediate wrong-write owner hash is +`9db1697989749a49fc9a22fb2ea944c6a691928cb2730d3ee90ea83e40a6dcc0`. +Only diagnostics and the specified control distinguish these executed files +from the final owner. The private Vite config resolves the private production +source and real installed SDK explicitly; logs record the positive source +witness. The snapshot owner is restored after the controls. + +The normal command is: + +```sh +pnpm --dir packages/electric-db-collection exec vitest run tests/electric-sdk-delivery-oracle.property.test.ts --coverage.enabled=false --maxWorkers=2 +``` + +The direct replay uses the same command with `-t replay` and +`TANSTACK_DB_ORACLE_SEED=20261026`, `TANSTACK_DB_ORACLE_PATH=0:0:0`, and +`TANSTACK_DB_ORACLE_PROPERTY=electric.sdk-rotation-release`. Wrong-result +controls run that replay in the private source-bound snapshot, without editing +shared production. Raw owners, patches, manifests, commands, and logs remain in +the task's `/tmp/pr2257-ci-loop/retired-map/` evidence directory. + +### Guide conformance and disposition + +ORC-001/002/003: public authority and the absent-key prediction are explicit +beside the model; the five responsibilities remain distinguishable. ORC-004: +one round reconstructs the observation gap; repeated rounds distinguish +replacement retirement; each delivery cut supplies a different retirement +interval. The grammar answers only admitted requests. ORC-005/006: real +SDK/Collection/persistence paths run, and both new durable comparisons reject +their intended wrong result. ORC-007: equal fixed/random budgets and the direct +replay fail/pass pair are recorded. ORC-008 adds no stateful-model distinction; +the captured target is a driver observation. ORC-009 preserves the glossary's +separate provider session, sync run, and persisted cache generation. ORC-010: +shrinking retains the durable failure, primitive observations survive cleanup, +and fixture resources are released. ORC-011 has no named shared semantic +computation requiring a second formulation; the expected absent key comes from +the source model. ORC-012 is this source-bound record. ORC-013 distinguishes +immediate/final wrong writes and allowed retained contents. ORC-014 credits real +SDK delivery and limits the durable premise to the Map fixture, without a real +SQLite/native handoff claim. ORC-015 requires and measures seven reach states. + +The old comparison's false green came from an unobserved durable target, not +missing histories or a copied expected result. Capturing that target and +observing it across the existing grammar is the smallest general repair. +No known reachable in-scope counterexample remains from N01. This bounded +observation repair does not establish whole-PR readiness. N02, N03, and U05 +repeat N01 and retain their separate ledger identities. + +Reviewer assessment for N01: accurate, concrete, and appropriately low severity. +The proposed small assertion repair is useful. The valid-retained-generation +distinction required additional examination. Hire for this focused enforcement +review; this evidence alone does not establish broader law-design depth. + +A fresh read-only rereview checked final owner `7ccf1eb9` and found no further +supported defect. It independently passed the targeted three-test rotation +baseline. The complete second CodeRabbit inventory contains 21 source-order +items, including repeated prompts and mutable-summary metadata: +**9 accepted-design + 1 fixed-now + 11 duplicates**. No item from that review +remains open or deferred. This accounting is separate from the earlier agent +and CodeRabbit inventories. + +## CF01 — ordered replay oracle classification repair + +The DB CI counterexample is an oracle boundary-classification defect under the +accepted demand/replay contracts. Production is unchanged. Baseline is PR head +`c88dcfe5902be80892d2108f16b9ec764041dbe7`; shared integration is based on +`5e1b3ff0f09b176feac1c39ba47938c1ad79e35a`. + +- **Law:** a newly initiated lazy keyed request may fail terminally even when + truncate causes it. A failed reacquisition of an established request is an + ordinary replay and retains the last complete publication until authoritative + replacement. Authority: the architecture's Demand plane/current-demand + contract and “Replay participants and failure,” plus the coverage map's + explicit terminal `failDemand` policy. Nonterminal current-demand recovery + remains a future contract. +- **Boundary repair:** marker truncate synchronously creates fresh include + demand, while established marker reacquisitions start later. The old + `markerReplaying` call-stack flag misclassifies both. The driver now observes + initiating keyed `requestSnapshot` result receipts, matching rejection only + to exact authored root/marker load errors in a dedicated WeakSet. Release + faults cannot supply the allowance. No predicate identity, production replay + state/classifier, or terminal-status observation computes that allowance. +- **Reach:** `replay.keyed-request-failures` constructs both failure placements, + varying head rank, ascending/descending order and marker 3/4 membership before + failure. Only recoverable histories get generated later actions. Each fixed + and random campaign has 40 generated pairs, hence 80 concrete histories; + initiating-keyed failure reaches 40 histories and keyed-reacquisition failure + reaches 40. Reach records actual receipt/acquisition failures, not the selected + placement. A broad-random reach miss (zero initiating failures) was retained + as sampling evidence and repaired constructively, without weakening reach. +- **RED:** original property `replay.ordered-window-faults`, seed `-82695597`, + path `43:2:7:4:4:4:4`, fails `convergence: terminal error`, false versus true, + on unchanged original owner and production. The trace is hold, + truncateMarkers, settle(false), move(root 1, rank 1); scenario is ascending, + offset 0, limit 1, anti-join, on-demand markers, includes, no ties/peer. + The failed oldest load is a fresh include request, so the existing terminal + lazy-demand policy applies. +- **GREEN:** the repaired complete owner passes 31 tests, including broad + fixed 300/random 300 and boundary fixed 40/random 40 campaigns. The original + CI coordinates still replay exactly. Guarded original and new-property + replays each report one executed property; the latter uses seed `20261026`, + path `0:0:0:0`. Named neighbors preserve established include/marker replay + failures and a pending include whose initiating adapter succeeds before + reacquisition fails. +- **Mutant:** private production control makes every `onLoadSubsetError` + terminal via `recordSubsetError(error, true)`. Both boundary campaigns reject + it at the established-replay terminal-error assertion. Fixed seed `20261026`, + path `0:0:0:0`, shrinks to `[[], "asc", 1, false, false]`: exact replay is RED + with the control and GREEN after restoring production. The empty suffix shows + rejection at the intended boundary. Rebuilding every acquisition predicate as + `and(where, where)` passes both campaigns with 40/40 reach, preserving freedom + to reconstruct equivalent expressions. All production controls were restored + byte-for-byte. +- **Limits:** this status relation does not independently establish correct + settlement of the initiating receipt. The pre-existing history-level failure + allowance remains shared in peer scenarios, so per-receiver failure isolation + is unproved. The coverage map names the existing owner and a needed witness + that fails one receiver while checking another at the same cut. These laws + are not retired or claimed closed. Real-provider cancellation, framework + timing, every replay interleaving and nonterminal recovery of failed current + demand remain outside this result. + +Final oracle Git blob: `68c8339969fe4a87380e83e0194fdddc67f0f990`. +Final oracle SHA256: `7a476bec755a566d6eb59bac73dd272c386fa618b4b5aa3d9d00e887d776a66d`. +Original oracle SHA256: +`382f2f2f1975251d77ecaaa16e410fb4048b379c83d4e91834cd630ea6e98ada`. +Unchanged subscription/subscriber SHA256: +`c446932a8a1f8b2efd79d00846f1a110e65a6c6df75d2b24658053e6964b530b` / +`74e97fe498b44446d5ee21912175afab6a3e58d2787e04e890a26771d5c8e1a6`. + +Runtime evidence uses the private source-bound archive at +`/tmp/pr2257-ci-loop/db1-snapshot` and its `db1.config.mts`; coverage/typechecking +are disabled. Full commands and raw log hashes are in +`/tmp/pr2257-ci-loop/db1-evaluation.md`. The two exact guarded commands are: + +```sh +TANSTACK_DB_ORACLE_SEED=-82695597 TANSTACK_DB_ORACLE_PATH=43:2:7:4:4:4:4 TANSTACK_DB_ORACLE_PROPERTY=replay.ordered-window-faults node --import tsx packages/db/tests/oracle-replay.ts --config db1.config.mts +TANSTACK_DB_ORACLE_SEED=20261026 TANSTACK_DB_ORACLE_PATH=0:0:0:0 TANSTACK_DB_ORACLE_PROPERTY=replay.keyed-request-failures node --import tsx packages/db/tests/oracle-replay.ts --config db1.config.mts +``` + +Package ESLint, source/registry Prettier, `pnpm lint:changed` and +`git diff --check` pass. The manifest registers both replay targets and selects +only their replay campaign. No production edit, commit or push is part of this +repair. A fresh law/independence review accepts the final owner blob above; +whole-PR readiness remains with the parent CI evaluation. + +### CF01 guide applicability + +ORC-001/002/003: existing contract authority, independently authored load-error +identity, and the initiating-request observation are explicit beside the law. +ORC-004: constructive prefixes reach both distinctions, with useful pre-failure +variation and recoverable continuations; no full ablation of the inherited +checker grammar was performed or claimed. ORC-005/006: the real Collection and +live-query path reaches the intended comparison, which rejects the incorrect +terminal-error control and accepts equivalent rebuilt predicates. ORC-007: +fixed/random budgets match, and original and new exact replay coordinates are +recorded. ORC-008/009: no new semantic model state is introduced; driver reach +states name request initiation and reacquisition using the existing glossary. +ORC-010: inherited final draining and cleanup remain; the added observer is +restored and its rejection observation is handled. ORC-011 adds no independent +reference computation needing a second formulation; correctness of receipt +settlement and per-receiver error isolation remain explicit unproved limits. +ORC-012 is this source-bound evidence block. ORC-013: fresh and established +request failures, pending success before failure, rebuilt predicates, and the +incorrect terminal-error control distinguish the repaired boundary. ORC-014: +the internal receiving boundary runs through real DB code; the controlled +provider establishes no real-provider, framework or host handoff. ORC-015: +actual receipt/acquisition failures measure both declared states at 40 +histories per campaign. This accounting applies to CF01's bounded observation +repair and does not replace the earlier cleanup-review outcomes. + +The private full/reach and boundary-control runs add only console output of +`failureReach.counts()` to the shared owner; expectations, generated inputs and +production behavior are unchanged. The recorded final owner hashes identify +the shared file without that diagnostic output. + + +## Production reduction after `42e2d9ca8` + +The user requested less production code and fewer shipped bytes. This follow-up +removes the mutable callback-release handle and its no-op initial value. Each +of the four terminal paths now calls the private stream's `unsubscribeAll()` +before the existing abort. Cleanup no longer calls both release methods. +Earlier release at terminal resume/durability failure is permitted by the +accepted cleanup contract; this change introduces no new deadline or law. +The approved release-before-replacement deadline remains unchanged. + +The final production source is three lines and 149 bytes smaller than +merge-base `5649a07d4`. With dependencies external, the production comparison +reports **+1 byte gzip and -5 bytes brotli** against that base (gzip 10,162 to +10,163 bytes). The prior pushed implementation measured +9 gzip/+15 brotli. +The clean direct-call implementation was smaller than either a stop helper or +a release-only alias. Inlining the queued-batch temporary increased brotli +size and was not retained. This is not a claim of reduction in both formats. + +### Law and evidence + +- **Law:** cleanup releases resources owned by its sync run, under `CleanupFn` + and the collection-options creator guide. The installed-SDK source-release + and rotation owners retain their earlier laws, histories, observations and + sensitivity evidence. Removing the returned handle is valid because each + Electric provider session privately owns its stream; full release removes + that stream's callback as well as host listeners. Abort still owns request + cancellation. SDK subscription registration precedes synchronous delivery, + so full release also works before `subscribe()` returns its handle. +- **Reach:** no new product law, history dimension or scope claim is added. + Existing fixed/random campaigns and declared reach checks pass in the full + Electric package. The fixture calibration enumerates both release cuts: + during synchronous subscription and after its return. +- **RED:** a wrapper that only assigns the callback release after + `mockSubscribe()` returns fails the synchronous calibration: expected one + release, observed zero. It passes the after-return neighbor. This is an + assertion failure at callback release, not a setup or timeout result. +- **GREEN:** `pnpm --dir packages/electric-db-collection exec vitest run + --coverage.enabled=false --maxWorkers=2` passes 30 files, 1,136 runtime tests, + one skipped test, and typechecking on the exact source identities below. + The calibration's two cases pass with the wrapper's stopped flag. + Package ESLint has zero errors (23 existing warnings), Prettier, + `pnpm lint:changed` and whitespace checks pass. +- **Mutant:** the calibration rejects the missing-stopped-flag wrapper above. + Earlier installed-SDK listener, release-order, demand-settlement and retired + storage controls remain applicable; their results are recorded above rather + than presented as a new production defect. +- **Limits:** the descriptor fixture deliberately retains a raw callback to + deliver late responses. Its release-call observation does not establish that + the callback became unreachable. The installed-SDK owner remains the + receiving witness for actual SDK release. Native execution, every SDK + version, arbitrary overlapping rotations, real SQLite peer claims, and the + separate core receipt/isolation limits remain unproved. + +The two shared mock streams now create separate callback ownership per instance. +Prototype inheritance keeps request and resume controls live. Full release is +idempotent and handles synchronous release during subscription. Scheduler +restoration reinstates the same wrapper. The descriptor fixture rejects pending +snapshots on abort, matching the SDK responsibility boundary, instead of +making callback release cancel HTTP work. Its late-delivery control is named +`ignoreAbort` to match that premise. + +An independent reviewer checked the production reduction and fixture changes. +It identified the synchronous wrapper gap above. After its repair, the reviewer +accepted the instance ownership, live controls, scheduler restoration, +signal-owned cancellation and explicit raw-callback limit. No further supported +finding remained within that scope. The optional queue inlining was then +reverted to the original source. This is a bounded review of the reduction, +not a new whole-PR readiness verdict. + +ORC-001/002/003 retain the accepted law, independent model and nearby explanations. +ORC-004/007/008/009 retain the existing grammar, paired campaigns, model state +and vocabulary. ORC-005/006/013 add the two bounded fixture cuts and the +wrong-wrapper assertion failure. ORC-010 retains cleanup and failure handling. +ORC-011 has no new shared semantic-fault hypothesis. ORC-014 keeps the mock +release premise separate from the unchanged installed-SDK receiving witness. +ORC-015 retains existing declared product reach, with no expanded coverage +claim. ORC-012 is this source-bound append-only record. + +Final source identities (Git blobs): + +| Source | Git blob | +| --- | --- | +| `packages/electric-db-collection/src/electric.ts` | `1db20a316ae255ea6c479838bd2d45439286faf5` | +| `packages/electric-db-collection/tests/electric.test.ts` | `62a858e17e2a0404fe8cfe2e2d499ce207f421fb` | +| `packages/electric-db-collection/tests/electric-oracle.property.test.ts` | `81fb0f2f45ab089f3a0e5d16f041e0fb1dc57bab` | +| `packages/electric-db-collection/tests/electric-descriptor-isolation-oracle.test.ts` | `c1c4bbd4717a208b82046bf0251e5cbd923f401b` | + +Raw local evidence is retained under `/tmp/pr2257-ci-loop/`: + +| Log | SHA256 | +| --- | --- | +| `net-negative-reentrant-fixture-red.log` | `fd5decfa4ac4979d8a23dd4de4bcba87946b8a5bb981c8af3af52043c0d7a972` | +| `net-negative-exact-final-green.log` | `d4c7aa80b57adc70959eb20a59360b30b23480bad42b8234e2457a04adf6eb4c` | +| `net-negative-direct.log` | `3fe347113cbd673a169a470f3e950cd67d8c6f6bdc8adfa1d88462af2a4068bd` | +| `net-negative-lint-changed.log` | `89d7da780deb04850db632f478bc4155d23659849c0e5bec6d114c74dc9d5168` | +| `net-negative-eslint-final.log` | `2540c43d9881aa2984d5fb39cd006db954c7dd0083eb841b17d032713273a12d` | + +Reproduce the bounded calibration with: + +```sh +pnpm --dir packages/electric-db-collection exec vitest run tests/electric-oracle.property.test.ts --coverage.enabled=false -t 'retires its mock callback' +pnpm size:compare --packages electric-db-collection --no-scenarios --rebuild +``` + + +### Integration with current main + +GitHub reported a conflict after main advanced to +`6c1ca8f8f9efe437d3cea61b5b9d0a086e2a5e34`, so CI could not start. +The normal merge preserves published history. The only conflict was an import +reorder in the core replay owner: retain main's order and the PR's +`CollectionSubscription` import. The core model, grammar and comparisons are +unchanged by that resolution. + +Main also introduces ORC-016, which keeps ordinary unit tests outside oracle +files. The new two-case mock calibration now lives in `electric.test.ts`. +Both consumers import the same small `electric-stream-mock.ts` fixture, so the +calibration checks the exact wrapper used by the oracle driver. This extraction +removes duplicate mechanics without introducing a model or new expectations. +The missing-stopped-flag control was rerun on the shared helper: the synchronous +case fails at callback release (zero instead of one); its after-return neighbor +passes. Restoring the helper passes both. This is fixture calibration, not a new +product-law or broader oracle-conformance claim. + +With main integrated, the complete Electric package again passes 1,136 tests +and typechecking, with one skipped test. The complete core replay owner passes +31 tests, including both 300-history campaigns and both 40-pair boundary +campaigns. Package ESLint, formatting, changed-file lint and whitespace checks +pass after the two new helper imports follow the repository order. No further +production edit is included. The size comparison remains +1 gzip/-5 brotli; +main did not change the Electric production source. + +The earlier reduction review applies to unchanged production. Its shared +fixture extraction is a mechanical follow-up, verified by the same wrong-wrapper +control and full package run. All native/SDK/SQLite and core receipt/isolation +limits above remain. ORC-016 applies to the relocated calibration; this entry +does not claim a new audit of every inherited test in the repository. + +| Integrated source | Git blob | +| --- | --- | +| `packages/db/tests/query/load-subset-replay-refinement-oracle.test.ts` | `1818dea39be8895d7116bba552f8a71ab7b98d98` | +| `packages/electric-db-collection/tests/electric.test.ts` | `ea82d75c06af0ddd1f36ce71524583a4dac69b4f` | +| `packages/electric-db-collection/tests/electric-oracle.property.test.ts` | `dd4d13e0785e174c749c8ec8c02a5413e27ddaec` | +| `packages/electric-db-collection/tests/electric-stream-mock.ts` | `d66a7ebc9114ac1db0c6da1d87e37241c2f6cb86` | + +Runtime logs `merged-electric-green.log`, `merged-core-green.log`, +`merged-fixture-red.log`, and `merged-size.log` remain in the same scratch +directory as the preceding evidence. The body update passes the STE structural +linter and paragraph-formatting check. The GitHub rendered body has no unintended +`br` elements in ordinary paragraphs or list items. + + +### Integration with deferred-demand main + +This append records the normal merge of main +`1640de834bca0588f91432ea80a0f4569157e9dc` into PR parent +`f4a1fdbed014192cdbbf4029e18710f6fad0131d`. The source identities below bind +this evidence to the resolved files. Earlier entries remain unchanged. + +- **Law:** preserve both accepted error-ownership judgments in the replay owner. + The opening contract and main's retained-demand contract distinguish ordinary + failed demand from renewal of an established demand. Initiating keyed-request + results and the independent subscription-token/key-set model each constrain + terminal errors after every history command and at final convergence. + Neither classification can excuse a result rejected by the other. +- **Reach:** retain all three properties, their fixed/random budgets, named + histories and guarded direct selectors. The broad property runs 300 histories + per campaign. Demand-origin runs 75. Keyed-request failure runs 40 pairs, + giving 40 initiating failures and 40 reacquisition failures per campaign. + The broad campaign's seventeen declared dimensions all reached their minimum. +- **RED:** no new production defect is claimed. The fresh merge review found + row-local expected answers in two incoming named histories. Both now call + the same independent comparison as generated histories, under ORC-016. + A direct broad replay also exposed an empty unrelated suite after campaign + selection. That process exited 1 even though its intended comparison passed. + Campaign registrations now remain at file scope. No test-disabling condition + is needed, and the identical guarded replay exits 0. +- **GREEN:** the resolved core owner passes 35 tests and typechecking. + The complete Electric package passes 1,136 tests and typechecking, with one + skipped test. A broader core run passed 865 tests but one wrapper child + process exceeded its 30-second limit. Its focused single-worker rerun passed. + This timeout is not presented as a clean complete broad run. ESLint, + formatting, changed-file lint and whitespace checks pass. +- **Mutant:** temporarily making every subset failure terminal fails the + keyed-request guarded replay at the intended `history: terminal error` + comparison. Restoring production passes the identical replay. The temporary + edit was restored byte-for-byte. Core production matches main. +- **Limits:** preserve all controlled-provider, predicate, receipt-correctness, + per-receiver isolation, native application, SDK-version, overlapping-rotation + and SQLite limits recorded above and in the coverage map. This merge adds no + new product law or real-host coverage claim. + +The fresh merge reviewer accepted the shared-comparison repair and found no +remaining supported item. The loss audit records two items, both fixed: +source conformance and suite registration. Neither requires a product decision. +The merge retains incoming main production unchanged and adds no further +Electric production change. + +Measured reach from the merged campaigns: + +| Broad declared state | Fixed | Random | +| -------------------------------------- | ----: | -----: | +| settlementThrows | 12 | 19 | +| throwsAfterReplay | 50 | 45 | +| replayStartThrows | 56 | 66 | +| heldAcrossTruncate | 26 | 23 | +| failedLoads | 9 | 10 | +| settledWindowChecks | 246 | 234 | +| tiedWindows | 15 | 13 | +| peerChecks | 127 | 119 | +| windowMoves | 92 | 67 | +| descendingRuns | 139 | 143 | +| joinedRuns | 225 | 236 | +| includeChecks | 152 | 149 | +| markChanges | 87 | 73 | +| antiJoinedRuns | 76 | 81 | +| innerJoinedRuns | 81 | 76 | +| markerLoads | 132 | 144 | +| markerTruncates | 80 | 94 | +| new-demand-failed-during-marker-replay | 9 | 11 | +| settled-marker-demand-replay-failed | 32 | 35 | +| initiating-keyed-request-failed | 40 | 40 | +| keyed-reacquisition-failed | 40 | 40 | + +Each cell counts histories, not repeated events. Direct replay commands run +from `packages/db` through `node --import tsx tests/oracle-replay.ts` against +`tests/query/load-subset-replay-refinement-oracle.test.ts`, with +`--coverage.enabled=false --maxWorkers=1`: + +| Property | Seed | Shrink path | Result | +| --------------------------------- | --------: | -------------- | ----------------------------- | +| replay.ordered-window-faults | -82695597 | 43:2:7:4:4:4:4 | One guarded execution, exit 0 | +| replay.subscription-demand-origin | 2233 | 0 | One guarded execution, exit 0 | +| replay.keyed-request-failures | 20261026 | 0:0:0:0 | One guarded execution, exit 0 | + +Set `TANSTACK_DB_ORACLE_PROPERTY`, `TANSTACK_DB_ORACLE_SEED` and +`TANSTACK_DB_ORACLE_PATH` to the corresponding row. The keyed replay also +supplies the terminal-error calibration above. + +ORC-001 through ORC-005 retain contract authority, independent judgments, +literate explanations, both grammars and public checkpoints. ORC-006 and +ORC-013 use the terminal-error calibration. ORC-007 retains paired campaigns +and checks all three direct replays. ORC-008 and ORC-009 add no new model state +or terminology. ORC-010 retains cleanup and primary-failure handling. +ORC-011 keeps the two distinct failure judgments without a new shared-fault +hypothesis. ORC-012 is this source-bound record. ORC-014 adds no receiving-path +claim. ORC-015 preserves measured declared reach. ORC-016 now shares the named +and generated demand-origin comparison without row-local expected answers. + +Against the exact integrated main, production source remains three lines and +149 bytes smaller. The Electric package bundle changes by -5 bytes Brotli and ++1 byte gzip. The per-package table supplies these values, rather than the +size tool's aggregate summary. The local dependency-install workaround stays +uncommitted. + +| Resolved source | Git blob | +| ---------------------------------------------------------------------- | ------------------------------------------ | +| `packages/db/tests/oracle-config.ts` | `d225d933c301c226d6b2ae071e810caac07bc4c5` | +| `packages/db/tests/oracle-replay-manifest.ts` | `0a5591e77abf27d0303391081ab5ba1ecd81d79f` | +| `packages/db/tests/query/load-subset-replay-refinement-oracle.test.ts` | `2550b374e0894bb321507eba99c2a76c894d3c9c` | + +Raw command logs and the two-item merge ledger remain under +`/tmp/pr2257-merge-final/`. Relevant logs include `core-shared-green.log`, +`core-final-owner-green.log`, `core-final.log`, `wrapper-focused-green.log`, +`electric-final.log`, `replay-original-final-green.log`, +`replay-origin-green.log`, `replay-keyed-green.log`, `keyed-reach-green.log`, +`terminal-control-red.log`, `terminal-control-restored-green.log`, +`eslint-final.log`, `lint-changed.log` and `size.log`. diff --git a/docs/contributing/oracle-reviews/2026-10-26-ssr-bundle-isolation.md b/docs/contributing/oracle-reviews/2026-10-26-ssr-bundle-isolation.md new file mode 100644 index 0000000000..a00ec87ad9 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-26-ssr-bundle-isolation.md @@ -0,0 +1,89 @@ +# SSR observer code isolation + +This follow-up starts at `7b4d1c00f8737e19d6ace7a6ecce8ce7c5830eb2`, after +merging main `5649a07d468117891193c2e4cde9c7706308b1cd` into PR #2229. The +commit containing this record also contains its production and oracle changes. + +## Law and repair + +The maintainer requested that applications without SSR avoid shipping SSR +fixes. Previously the shared observer class contained server admission, +render-time capture, hydration reconciliation, and stream handoff even when no +DbClient was imported. Runtime guards prevented execution but not inclusion. +The repair moves that implementation to `live-query-observer-client.ts`, +instantiated by DbClient through a type-only boundary in the shared observer. +The existing hydration state and transitions are relocated, not replaced with +a second state machine. React delegates streaming discovery through this +boundary too. Existing runtime laws and oracle judgments stay unchanged. + +DbClient's current API includes hydrate/dehydrate and streaming support on one +class. A browser application using DbClient only for descriptors still retains +that implementation. Eliminating that cost requires an explicit SSR opt-in API +and a migration; this follow-up preserves the existing API. No universal +no-SSR-code claim is made for DbClient applications. + +## Executable evidence + +- **Reach:** the finite grammar enumerates all six combinations of three React + hook entries (ordinary, Suspense, infinite) and DbClient absent/present. + Each bundles real built ESM with production defines. The oracle observes the + final code and positive `bytesInOutput` for the transport module, rather than + merely inputs visited during tree shaking. +- **RED:** `node scripts/ssr-bundle-oracle.mjs`, before production edits, failed + `useLiveQuery client-only bundle must exclude _captureServerQuery`. +- **GREEN:** that command passes all six programs after extraction. All plain + hook bundles exclude the entire transport implementation; each DbClient + control retains it and both admission/capture operations. +- **Mutant:** `node scripts/ssr-bundle-oracle.mjs --calibrate` deliberately retains + DbClient in a plain program. It reaches and fails the same absence assertion; + it is an assertion rejection, not a setup failure or timeout. +- **Runtime:** 171 core tests cover observer snapshots, observer histories, + focused observer behavior, DbClient, and hydration authority. All 701 React + runtime tests pass, including generated SSR receiving histories and actual + React hydration. Svelte's admission, actual server-to-browser handoff, and + hydration suites pass 32 tests. Router passes nine tests. +- **Published output:** the minified public API check passes 108 error names, + query results, live updates, and index metadata. Production error erasure and + mangle-cache checks pass. These runs disable Vitest's optional test-file + typecheck; minified package builds generate declarations, but this is not a + claim of a full repository typecheck. +- **Limits:** the bundle law covers the six enumerated esbuild programs, not + every framework or bundler. Runtime laws remain with their existing owners. + A separate opt-in SSR API is needed for client-only DbClient applications. + No new generated runtime law or grammar is claimed, so random campaigns, + shrinking, and new generated reach requirements do not apply to this finite + build oracle. Its expected inclusion follows declared imports, independently + of the observer's implementation. Its calibration rejects the concrete + unwanted inclusion. No second reference formulation is needed to judge + whether the measured output contains that implementation. + +## Shipped bytes + +`node scripts/size-compare.mjs --packages db,react-db --files` compares identical +production build tools against main `5649a07d4`. It uses the cached baseline and +the same dependency-tool substitution documented in +[the merge report](2026-10-26-main-merge-size.md). + +| Bundle | Main gzip | Before extraction | After extraction | After versus main | +| --- | ---: | ---: | ---: | ---: | +| Typical app | 102142 | 102608 | 101791 | -351 | +| DB, every export retained | 103207 | 104108 | 104427 | +1220 | +| React, every export retained | 4921 | 4964 | 4942 | +21 | + +The typical app loses 817 gzip bytes versus the previous PR head and is 282 +Brotli bytes below main. Its observer contribution falls by 2207 minified bytes +relative to main. The new transport module contributes zero bytes to this app. +The remaining collection admission/ownership changes are runtime fixes used by +client-only applications, not SSR code. + +The full DB bundle grows 319 gzip bytes versus the previous PR head because the +client/observer boundary adds delegation and host callbacks. It retains all SSR +exports by definition. This is an explicit tradeoff, not a package-level saving. +The first attempted refactor moved only admission/capture: it left the app 330 +gzip bytes above main and the full DB bundle 995 bytes above main. Moving the +remaining hydration and stream implementation removes the broader client-only +cost. Keeping the old monolithic class is smaller when retaining every export, +but fails the maintainer's plain-app isolation requirement. + +Tests add one finite bundle oracle and its CI registration. Contract and review +documentation grow separately from production code; neither ships in app bundles. diff --git a/docs/contributing/oracle-reviews/2026-10-26-subscription-ssr-oracle-review.md b/docs/contributing/oracle-reviews/2026-10-26-subscription-ssr-oracle-review.md new file mode 100644 index 0000000000..da100d1649 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-26-subscription-ssr-oracle-review.md @@ -0,0 +1,626 @@ +# Subscription and SSR oracle review + +Reviewed production: `44d55dbc437178698b23f2b211ecde8344143d97`. +Compared main: `5649a07d468117891193c2e4cde9c7706308b1cd`. + +Five independent reviewers challenged the subscription, SSR, React/Router, +Svelte, and consumer-bundle owners. They first collected counterexamples without +changing production. The combined repair plan grouped their missing history +dimensions before the primary owners changed. Each production repair followed +the relevant unchanged-production comparison, including real receiving paths. +Later counterexamples received additional comparison gates before their repairs. + +## Which laws changed? + +| Law | Authority and relation to main | +| --- | --- | +| Ordinary subscription admission and deferred acquisition resumption throw during cleanup. | The maintainer chose rejection, including automatic GC. Main admits an ordinary subscription during held GC cleanup. Sync-run invalidation alone does not require this policy. Nonacquiring deferred subscriptions remain legal. | +| Ordinary SSR rejects incidental rows or readiness without reproducible preparation. | The maintainer chose a programming-error rejection. Main's server getter returns its ordinary snapshot without this check. Runtime provider errors retain their normal surface. | +| Prepared server queries may render ready, including empty results. | Prepared SSR and matching initial browser markup already require this composition. Normalized source rows alone do not prove query readiness. Render-time query captures supply that missing fact. | +| Each reader retains its own server view during hydration. | The earlier SSR guide already promises matching initial markup. The PR makes mixed ordinary/Suspense readers and later boundaries explicit. These failures are enforcement gaps in that promise. | +| Svelte distinguishes hydration from a fresh mount. | The maintainer retained the later-hydration guarantee and chose explicit hydration information. `DbProvider hydrating` supplies it; omitted nested values inherit it, and providers clear it after mount. | +| A hydration read waits for the first required DB record. | The maintainer explicitly chose waiting. A real Router serializer/transform/Chromium witness also reproduced delayed first delivery without reordering bytes. This is not merely an arbitrary stream permutation. | +| Infinite-query matching includes its initial window. | The DbClient query hash includes limit and offset with a nonempty `queryKey`. This specifies previously ambiguous matching. Framework query identity remains the glossary's recompilation concept. | +| Apps without DbClient exclude its SSR implementation. | The maintainer chose the current DbClient import boundary and preserved its API. Descriptor-only browser apps importing DbClient still retain that implementation. The bundle owner checks emitted bytes, not visited inputs. | + +Retained server snapshots and consumed active query ownership have different +purposes. A later hydration read can use retained rows while a new ordinary +browser mount uses live rows. Neither obligation requires preserving active +source Collection ID claims after their owner ends. + +## Falsified laws and enforcement + +| Items | Promised observation and missed history | Original result and repair | +| --- | --- | --- | +| Lifecycle-L1 | A failed subscription transfers no unreturned ownership. Pooled bootstrap callbacks could throw after ownership acquisition. | HEAD and main retain one source subscription after disposal and GC. The pooled owner now crosses ordinary/pooled input, callback return/throw, peer ownership and release. One release closure handles both normal release and failed bootstrap. | +| Lifecycle-L2 | Every admitted observer receives terminal cleanup status despite a sibling callback failure. | A healthy pooled observer stayed ready. Existing histories put healthy listeners before the throw and checked channel counts. The owner now varies failure position and checks each peer at held cleanup start and settlement. Shared callback dispatch attempts all peers before reporting the original failure. | +| React-R01/R03 | First hydration rows and status match server output; committed browser errors then become visible. | Server error became loading, or browser error replaced a ready server view. Shared histories cross server/browser outcomes and pre-hydration startup. React ordinary/infinite and native Svelte drivers compare both cuts. Error captures carry status without claiming arbitrary error-detail serialization. | +| Core-SC06 | An ordinary loading reader and a Suspense sibling retain their separate rendered views. | A Suspense-first pending record was queued before the ordinary view was captured. The actual Router payload omitted that view. Held readiness, reader order, empty/nonempty rows and split/together hydration now reach the omission. New initial facts advance the existing record's timestamp and stream through update events without replacing its promise or source claims. | +| Core-SC07 | A later stream failure cannot replace an ordinary reader's earlier loading markup. | The query error branch discarded an available initial snapshot. The mixed owner now generates held-source failure. Ordinary hydration retains its initial view; Suspense still receives the failure. | +| Runtime-Immediate | After subscription, a provider error publishes synchronously. | An initial repair deferred this publication. The existing unit rejected it; the primary observer owner now crosses pre-attachment/post-subscription errors, both modes and portable statuses. Shared handoff preserves the synchronous post-subscription cut. | +| Svelte-S01/Nested | Late native hydration uses retained server rows; fresh mounts and later children use live rows. | A second root read browser rows. Always selecting the server getter instead fails fresh mounts. Shared receiving histories now include initialization kind, prior readers, nested providers and children mounted later. Explicit scoped hydration information resolves the missing input. | +| React-R02 | Missing first records suspend without publishing browser rows. | The actual host rendered browser rows before the first record arrived. The shared receiving comparison uses real Router serialization, transformed HTML, manifest scripts and Chromium, with a semantic network release after a hook attempt. Missing-record and known-promise lifetimes remain separate. Explicit Suspense preloads retain their existing demand semantics. | +| React-R04 | A local preload before delivery cannot prevent receipt of the first server view. | A newer local timestamp rejected the older server timestamp even though no server record had arrived. The real-host delivery grammar now includes local preload before delivery. Version admission distinguishes active transported records from local records without changing the consumed/manual-input question. | + +The runtime-error grammar uses empty eager sources. It does not establish a +partial-row retention policy after failure. The mixed failure grammar likewise +uses empty results. Success histories cover zero through three rows. These +bounds do not authorize dropping initial status or known server rows. + +## Source-item accounting + +Source IDs remain separate even when they share a repair. The scratch ledger +also preserves preliminary IDs: preliminary B03 maps to Bundle-B06; preliminary +L02 maps to Lifecycle-GC; preliminary C01 maps to Core-SC06. + +| ID | Original claim or hypothesis | Judgment and destination | +| --- | --- | --- | +| Bundle-B01 | The oracle claims minified package input, but CI uses ordinary package builds. | Correct wording: built ESM enters a minified consumer build. Final minification was always exercised. | +| Bundle-B02 | Standalone execution can accept stale React/IVM artifacts. | Confirmed. Freshness now covers every aliased package, including TSX and configuration. CI already rebuilt them. | +| Bundle-B03 | The checker only detects input presence. | Refuted: it sums emitted output bytes. Preserve this stronger observation. | +| Bundle-B04 | Calibration fails during setup. | Refuted: it reaches the intended unwanted-inclusion assertion. | +| Bundle-B05 | Descriptor-only DbClient browser use violates isolation. | Accepted design under the chosen API boundary. Stronger isolation requires another API decision. | +| Bundle-B06 | Historical generated RED followed production edits. | Confirmed historical deviation, already disclosed. Later evidence cannot reverse its chronology. | +| Lifecycle-L1 | Failed pooled bootstrap retains ownership. | Repaired through the shared callback-history owner and pooled receiving path. Also occurs on main. | +| Lifecycle-L2 | A throwing pooled listener prevents healthy terminal delivery. | Repaired through failure-position histories and per-observer observations. Also occurs on main. | +| Lifecycle-GC | GC rejects subscriptions while public status stays ready. | Accepted consequence of the explicit cleanup-rejection policy. No automatic-wait contract was found. | +| Lifecycle-H04 | A recoverable source failure permanently terminates the partition. | No legal executable counterexample established in this round. Preserve the earlier evidence gap; do not label a different passing path a refutation. | +| Lifecycle-H08 | A notification throw permanently strands detached demand. | Not reproduced; replacement readiness reacquires. The broader earlier claim remains an evidence gap. | +| Lifecycle-Process | Historical production-first chronology. | Duplicate observation of Bundle-B06. | +| Lifecycle-Deferred | Deferring subscriptions remain legal during cleanup. | Accepted distinction between nonacquiring ownership and acquisition resumption. | +| Lifecycle-Dependent | Effects and pooled views represent cleanup differently. | Accepted API-specific surfaces: disposed Effects and terminal pooled views. | +| Lifecycle-Handoff | Recorded provider error differs from a thrown start handoff. | Accepted runtime-error distinction. The SSR programming-error decision does not erase it. | +| Core-SC01 | Retained snapshots necessarily conflict with consumed ownership. | Refuted as a necessary conflict by the distinct retained/active responsibilities. No runtime disproof is claimed. Preserve paired late-hydration/live-mount controls. | +| Core-SC02 | Infinite query identity conflates recompilation and result matching. | Correct prose to name the DbClient query hash. | +| Core-SC03 | The source-preparation driver materializes rather than awaits preload. | Confirmed fixture distinction. Explicit normalized source registration makes warmed rows portable; the guide's awaited-preload advice is sufficient, not an assertion that every fixture uses it. Clarify driver prose. | +| Core-SC04 | Older manual input after active consumption can replace newer retained rows. | Unexecuted hypothesis with unresolved multi-epoch semantics. No production change or broader version-order promise follows from it. | +| Core-SC05 | Browser runtime errors may replace a server seed. | Duplicate of React-R03's later executable evidence. | +| Core-SC06 | Pending Suspense registration loses a later ordinary loading capture. | Repaired; original primary comparison and event-only wrong repair reject it. Main replay did not reach collection, so no main RED is claimed. | +| React-R01 | A server error view hydrates as loading. | Repaired with runtime-outcome grammar and real React/Svelte receivers. | +| React-R02 | A first query record arriving after hydration allows local substitution. | Confirmed in the actual Router/Chromium host after earlier later-discovery controls passed. Maintainer also chose waiting. | +| React-R03 | A pre-hydration browser error overrides a ready seed. | Repaired while retaining normal committed error behavior. | +| Svelte-S01 | Later hydration reads live rows after another root commits. | Repaired with explicit host information and paired native hydrate/mount histories. | + +Additional findings discovered during repair remain separate: Core-SC07, +Runtime-Immediate, Svelte-Nested and React-R04 above. The explicit-preload control initially +required exact provider error identity. That assertion exceeded the contract; +the retained control requires rejection, including an undefined cause, without +inventing portable error identity. Query-prepared probes that threw before the +hydration comparison receive no assertion-RED credit. + +An initial receiving assertion also prohibited every early source start in a +Suspense tree. That restriction exceeded the first-view law: a Suspense read +is explicit preload demand. The host comparison instead forbids a successful +browser view before the required server record. Terminal checks prohibit new +work after the terminal cause. Ordinary render deferral retains its own owner. + +The final cleanup audit found another sibling, React-R05. `DbClient.cleanup()` +permits later rematerialization, but missing-record failures survived into a new +stream for the same hash. The lifetime grammar now crosses cleanup and a new +stream. Both campaigns rejected the old terminal cause before production changed +(fixed seed `2229`, path `3:0:1`). An initial unconditional reset then failed the +same-open-stream control (path `2:0:0`). The repair clears old waits only when a +new stream starts. Existing promises keep their settled result, and enabling an +already open stream preserves its pending wait. This enforces the existing +rematerialization contract rather than making a failed stream recover in place. + +## Verification record + +The comparisons below ran on starting head `44d55dbc` plus the repairs recorded +here. The production blob table binds the evidence to exact source. Temporary +setup failures and overstrong assertions receive no product-RED credit. + +### Reproducible comparisons + +All Vitest runtime commands use `--coverage.enabled=false +--typecheck.enabled=false`. Run them from the named package with its installed +`node_modules/.bin/vitest`. Set `pnpm_config_verify_deps_before_run=false` when +using pnpm with the existing installation. Type validation runs separately. + +| Law / primary owner | Original comparison and direct replay | Repaired comparison and distinguishing wrong implementation | +| --- | --- | --- | +| Pooled bootstrap ownership / `db/tests/query/pooled-live-query-gc-oracle.test.ts` | Final owner: 4 failures, 43 controls pass. Bootstrap fixed `2229/2:1:1:1`; cleanup peer fixed `2229/2:1:2`. Both reproduce the public ownership or held-status mismatch. Main also fails both. | 47/47. Bootstrap-only repair fails two peer-status campaigns. All-peer-only repair fails two bootstrap-ownership campaigns. Original random replays `-207208952/2:1:1` and `-469560171/0:2:2` fail, then pass repaired production through the guarded replay runner. | +| Mixed reader views / `react-db/tests/descriptor-query-binding-oracle.test.tsx`, selector `stream render snapshot cut` | SC06: 6 failures/10 controls, fixed `2229/3:0:0:1:1`. SC07 expanded owner: 4 failures/16 controls, fixed `2229/29:0:0`. Failure is the first ordinary hydration status, including empty results. | Final 20/20, including fixed/random 40 histories. Event emission without a new accepted revision fails the empty/loading comparison. Restoring error-first stream selection fails two together-hydration cells while split controls pass. Direct SC07 replay passes. | +| Runtime status / `react-db/tests/ssr-runtime-error-handoff-oracle.test.tsx` | 20 intended first-view failures/16 controls. Fixed `2229/6` reaches browser error replacing the ready server view. Query-preparation failures before hydration are excluded from this RED count. | 36/36. Each ordinary/infinite path executes fixed/random 64 histories and named outcome controls. Merely transporting errors fails 12 committed-error cuts. Original local-error precedence fails both primary observer campaigns. Direct runtime replay passes. | +| Error publication / `db/tests/live-query-observer-history-oracle.property.test.ts`, selector `hydration error timing` | An always-deferred handoff fails synchronous post-subscription publication in both campaigns, fixed `2229/2:0`. | Expanded owner plus observer examples: 89/89. Pre-attachment seed status and synchronous owned error publication are both asserted. Direct timing replay passes. | +| Svelte receiving / `svelte-db/tests/ssr-snapshot-handoff-oracle.svelte.test.ts` | Original getter: 4 first-view failures/16 controls. Nested inherited provider also fails before inheritance repair. Final original-getter replay: `2229/8`. | 24/24, fixed/random 120 histories. Always-server getter fails a fresh mount. Persistent hydration marker fails a later child. Original getter fails the exact late-hydration comparison. Direct replay passes. | +| Svelte runtime / `svelte-db/tests/ssr-runtime-error-oracle.svelte.test.ts` | 10 first-view failures/8 controls; fixed `2229/6`. Committed browser outcome already passes in RED. | 18/18, fixed/random 64 histories. Same shared runtime judgment as React, received through native Svelte hydrate. Direct replay passes. | +| Delayed first record / `start-ssr-e2e/e2e/router-stream-view.spec.ts` | Actual differing-row shell history substitutes browser rows. Normal delivery, later boundary, and known pending close controls pass before repair. Local-preload-before-delivery separately fails the shared first-view comparison. Empty/equal cases failing only the withdrawn acquisition ban receive no defect credit. | Corrected 13/13. Isolated no-wait variant fails server-ready/server-row versus browser-ready/browser-row. Closing every known pending result fails the explicit overlapping-preload checkpoint. Both use the same host/comparison. | +| Record lifetime / `db/tests/ssr-stream-record-wait-oracle.test.ts` | Original public-host RED precedes implementation. The later core driver adds lifetime dimensions. Old local timestamp admission fails both campaigns after arrival. With the final grammar, fixed `2229/9:1` and random `-903817574/1:1:1` reconstruct local preload then server arrival; exact replay fails and then passes. New-lifetime RED is recorded above. | Fixed/random 100 histories, including missing/pending/local records, arrival/close/error/cleanup, peers, late attempts, undefined causes, and a new stream after cleanup. Same-open reset control rejects an overbroad repair. Pending-result disposal during cleanup remains outside this grammar. | + +For direct replay, set `TANSTACK_DB_ORACLE_PROPERTY`, +`TANSTACK_DB_ORACLE_SEED`, and `TANSTACK_DB_ORACLE_PATH`, then select only the +random-campaign test. Property IDs are registered in +`packages/db/tests/oracle-replay-manifest.ts`. Core supports the guarded +`node --import tsx tests/oracle-replay.ts` runner. The table's `seed/path` +notation keeps both values, rather than claiming a seed alone records timing. + +The actual host command is `PLAYWRIGHT_BASE_URL=http://127.0.0.1:4197 +playwright test e2e/router-stream-view.spec.ts --workers=1` from the Start +example. Its HTTP sink holds all bytes from the first record until a browser +hook attempt, preserving byte order. The known-pending control receives a record, +closes the DB stream, starts explicit public query and Collection preloads, and +checks that the known query promise stays pending until provider release. +This is a real Router/Chromium mini host, not a deployed Start/Electric system. + +### Guide conformance + +| Requirement | Outcome for the changed owners | +| --- | --- | +| ORC-001 | Laws and authority appear above and beside executable judgments. Bounds distinguish core callbacks, finite eager sources, initial windows, and real framework receiving paths. | +| ORC-002 | Ownership uses returned-handle counts and independent terminality. SSR compares authored outcomes or separately recorded server/browser views. No production status, admission, version, or partition classifier computes the expected result. | +| ORC-003 | Law, model, grammar, driver, and checkpoint prose remain beside executable code. Shared modules name their drivers and distinguish lifecycle status from first hook view. | +| ORC-004 | Grammars reconstruct the recorded failures. Removing callback throw loses ownership/peer failures; removing held readiness loses SC06; removing failure loses SC07; removing runtime outcomes loses error handoff; removing later hydrate/fresh mount/nesting loses the Svelte distinctions; removing local records or stream restart loses R04/R05. Rows are 0–3, healthy peers 0–3, prior Svelte readers 0–2, and GC grace 1–3. Illegal unreleasable Suspense-only holds and pending-record cleanup are excluded explicitly. | +| ORC-005 | Core uses actual observers and Collections. React uses renderToString/pipeable streaming and hydrateRoot. Svelte uses separately compiled SSR/browser fixtures and native hydrate/mount. Router uses the installed serializer, HTML transform, manifest Scripts, and Chromium. First-view, held-cleanup, synchronous-publication, settlement, and final-release cuts are separate assertions. | +| ORC-006 | The table names intended comparison failures. Isolated copies supply wrong implementations without shared-source swaps. Setup failures, unreached main probes, selectors running no tests, and discarded work/error-identity assertions are explicitly denied RED credit. | +| ORC-007 | Important properties run the same grammar/comparison/budget with fixed seed 2229 and without a seed. Seed/path direct replays were checked. No changed property uses commands requiring a separate command replayPath. | +| ORC-008 | Callback and server/browser comparisons are stateless judgments. Lifetime inputs distinguish missing/pending/local records and old/new stream obligations. No second production lifecycle model was introduced. | +| ORC-009 | Returned logical handles, physical source subscription ownership, provider timing premises, framework hydration intent, and Collection status remain distinct. Framework query identity is not the DbClient query hash. | +| ORC-010 | Drivers release resources after failure and retain primary comparisons separately from cleanup diagnostics. Reduced failures retain the same public mismatch. Undefined causes use an explicit wrapper, preserving the distinction from an open stream. | +| ORC-011 | The named risk of classifier-derived SSR expectations is addressed by separately recorded server/browser observations. Another copied status classifier would add no independent evidence. Ordinary and pooled production paths are receiving controls, not two independent reference models. | +| ORC-012 | This record supplies exact source bindings, dispositions, commands, comparisons, and finite limits. It does not claim universal closure or erase the historical production-first deviation. | +| ORC-013 | Throw/no-throw and early/late peers distinguish callback boundaries. Ready-empty catches status-only errors. Late hydrate/fresh mount/later child distinguish scoped hydration. Missing/known-pending and close/error distinguish stream obligations. Same-open/new-stream controls distinguish reset boundaries. | +| ORC-014 | The Router mini host supplies actual byte-order receiving evidence. Native React and Svelte supply their own hydration cuts. Controlled provider errors and callback failures stop at those declared boundaries; no deployed Electric or arbitrary provider claim follows. | +| ORC-015 | Each important property enforces declared reach with minimum one outside direct replay. Counts below report normal campaigns, excluding RED shrinking. Each actual host case also asserts its receiving premise before comparison. | + +### Final loss audit and reviewer assessment + +The 25 original items remain separate: **11 fixed-now, 2 confirmed-open evidence +gaps, 1 stale historical process finding, 3 refuted, 5 accepted-design, 1 +design-decision, and 2 duplicates**. The sum is 25. Five additional siblings +(SC07, immediate error publication, nested Svelte hydration, local timestamp +admission, and client reuse) received their own repair gates. None is a deferral. + +H04 needs a legal eager-source subscription throw plus authority requiring +same-partition recovery after it. Ordinary markError/markReady does not reach +that catch and recovers; on-demand probes are ineligible for pooling. H08 needs +a live demand still detached after the supported replacement-readiness cut. +Current real-throw histories reacquire through that listener. These historical +claims remain evidence gaps, not proved defects or refutations. The coverage +map names their owners and the missing witnesses. Core-SC04 remains an +unexecuted multi-epoch manual-input policy question. R04 intentionally does not +decide it by comparing every retained timestamp. + +Reviewer assessment: retain all five reviewers for similar work. Lifecycle and +SSR-core supplied small executable failures and useful narrow-repair controls. +React/Router and Svelte found receiving paths omitted by green controlled +tests. Bundle review distinguished wording, standalone freshness, CI behavior, +and emitted bytes. The main corrections were an initially unproved deployed-host +claim, an overstrong source-work restriction, and error-object identity beyond +the portable contract. Those corrections remain visible. Useful findings and +precise evidence outweigh those revised hypotheses. + +Accounting is complete for this round. Final CI status and whole-PR readiness +remain separate judgments. The historical process violation and unresolved +evidence questions prevent treating this audit as a universal readiness proof. + +### Declared reach + + +Runtime counts below come from the final full React run. Each hook/campaign executes64 histories and asserts all named minima. Error views are also separately enumerated across16 authored combinations per hook. Reach events are recorded after actual server/hydration/commit observations, not just generated inputs. + +```text +react-ssr-runtime ordinary fixed { + 'server-error-browser-cold': 18, + 'server-ready-browser-failed-before-hydration': 8, + 'server-error-browser-ready-before-hydration': 6, + 'matching-ready': 18, + 'matching-error': 14, + 'undefined-cause': 35, + 'defined-cause': 29, + 'browser-commit': 64, + 'explicit-preload-after-error-view': 31 +} + +react-ssr-runtime ordinary random { + 'server-error-browser-cold': 12, + 'server-ready-browser-failed-before-hydration': 10, + 'server-error-browser-ready-before-hydration': 5, + 'matching-ready': 22, + 'matching-error': 11, + 'undefined-cause': 31, + 'defined-cause': 33, + 'browser-commit': 64, + 'explicit-preload-after-error-view': 22 +} + +react-ssr-runtime infinite fixed { + 'server-error-browser-cold': 18, + 'server-ready-browser-failed-before-hydration': 8, + 'server-error-browser-ready-before-hydration': 6, + 'matching-ready': 18, + 'matching-error': 14, + 'undefined-cause': 35, + 'defined-cause': 29, + 'browser-commit': 64, + 'explicit-preload-after-error-view': 31 +} + +react-ssr-runtime infinite random { + 'server-error-browser-cold': 21, + 'server-ready-browser-failed-before-hydration': 6, + 'server-error-browser-ready-before-hydration': 9, + 'matching-ready': 13, + 'matching-error': 17, + 'undefined-cause': 29, + 'defined-cause': 35, + 'browser-commit': 64, + 'explicit-preload-after-error-view': 39 +} +``` + +Immediate observer counts from final post-reset core run;64 histories per campaign. Both modes, all portable seed statuses and before/after ownership cuts reached. + +```text +observer-hydration-error fixed { + granular: 33, + wholesale: 31, + 'before-attach': 31, + 'after-subscribe': 33, + ready: 21, + loading: 24, + error: 19, + 'defined-cause': 29, + 'undefined-cause': 35 +} + +observer-hydration-error random { + granular: 29, + wholesale: 35, + 'before-attach': 29, + 'after-subscribe': 35, + ready: 23, + loading: 20, + error: 21, + 'defined-cause': 34, + 'undefined-cause': 30 +} +``` + +Wait counts from final post-reset core run;100 histories per campaign. A known pending result with cleanup is excluded because existing result-promise disposal is a different law. Missing/local cleanup and subsequent client restart are included. + +```text +ssr-stream-record fixed { + missing: 37, + pending: 25, + local: 38, + arrival: 28, + close: 28, + error: 25, + cleanup: 19, + 'late-attempt': 44, + 'peer-first': 45, + 'undefined-cause': 50, + 'restart-after-cleanup': 59 +} + +ssr-stream-record random { + missing: 39, + pending: 24, + local: 37, + arrival: 23, + close: 26, + error: 30, + cleanup: 21, + 'late-attempt': 42, + 'peer-first': 62, + 'undefined-cause': 52, + 'restart-after-cleanup': 48 +} +``` + + +Svelte receiving fixed/random counts, 120 histories each: first-hydrate 29/19, +late-hydrate 39/51, fresh-mount 52/50, fresh-child 26/25, inherited-provider +16/22, explicit-provider 25/21, fresh-nested-mount 34/36, earlier-authoritative +91/101, multiple-earlier-readers 30/38, same-rows 72/81, different-rows 48/39, +loading-server-view 63/49, ready-server-view 57/71, authoritative-view 120/120. +Svelte runtime fixed/random counts, 64 histories each: server-error/browser-cold +18/14, server-ready/browser-failed-before-hydration 8/8, +server-error/browser-ready-before-hydration 6/6, matching-ready 18/16, +matching-error 14/18, undefined-cause 35/31, defined-cause 29/33, browser-commit +64/64. + +Mixed-stream final fixed/random reach, 40 histories each: held ordinary-first +13/13, held Suspense-first 5/4, held handoff 18/17, synchronous readiness 22/23, +synchronous handoff 14/11, held-source-error 11/9. Every declared minimum is one. + +Pooled callback campaigns execute 80 histories per row. `path`, first-return, +and release each reach 80. Cleanup-start and settlement also reach 80 for each +cleanup row. Remaining declared counts follow. + +| Law/path/campaign | return | throw | peers | no peers | throw before | throw after | undefined | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | +| Bootstrap ordinary fixed | 43 | 37 | 54 | 26 | 16 | 9 | 43 | +| Bootstrap ordinary random | 29 | 51 | 63 | 17 | 18 | 21 | 41 | +| Bootstrap pooled fixed | 43 | 37 | 54 | 26 | 16 | 9 | 43 | +| Bootstrap pooled random | 45 | 35 | 51 | 29 | 12 | 9 | 42 | +| Cleanup ordinary fixed | 43 | 37 | 54 | 26 | 16 | 9 | 43 | +| Cleanup ordinary random | 42 | 38 | 59 | 21 | 18 | 13 | 40 | +| Cleanup pooled fixed | 43 | 37 | 54 | 26 | 16 | 9 | 43 | +| Cleanup pooled random | 40 | 40 | 57 | 23 | 13 | 15 | 33 | + +### Shipped weight and rejected alternatives + +`pnpm size:compare --packages db,react-db,react-router-with-db --files` compares +identical minified consumer builds with main `5649a07d4`. Final deltas: + +| Bundle | Gzip | Brotli | +| --- | ---: | ---: | +| Typical app | −338 B | −266 B | +| DB, all exports | +1,562 B | +1,314 B | +| React DB, all exports | +21 B | +18 B | +| Router DB, all exports | +26 B | +31 B | + +The typical app excludes observer SSR transport at the DbClient import boundary. +All-export DB growth pays for capture, per-reader views, status transport, and +missing-record lifetime behavior. Per-file minified changes versus main include +observer −2,153 B, observer-client +4,534 B, client +2,195 B, subscription +admission +320 B, and pooled query +111 B. Per-file bytes are uncompressed and +cannot be added to compressed bundle deltas. + +The repair reuses the same idempotent release for normal and failed bootstrap, +the existing all-callback dispatcher, and one hydration handoff for ready/error +publication. The Svelte repair merges client and hydration context, removing a +second context key/read. Against the reviewed head with fixed core externals, +identical Svelte compilation/minified consumer builds cost +17/+17 B for the +hook, +82/+75 B for provider plus hook, and +95/+93 B for all exports. Merging +contexts saves 17/18 B for provider plus hook versus the first repair. These +adapter-only deltas are not added to the whole-PR app figure. + +Rejected cheaper designs have executable evidence: no rollback leaks ownership; +short-circuit dispatch skips peers; event-only updates omit the initial view; +error-only transport misses browser handoff; permanently retaining a seed hides +committed errors; always-server Svelte breaks fresh mounts; persistent markers +break later children; getter-only terminal throws permit false browser success +on React recovery; closing known pending results violates their separate promise; +and unconditional stream reset strands same-open demand. No retry lifecycle, +placeholder server record, or cross-clock timestamp ordering was added. + +Tests and contract documentation are separate proof cost. They do not enter +production bundle measurements. Line-count bookkeeping below distinguishes +production, tests, documentation, and build/configuration support. + +### Integrated validation and source binding + +The broader combined run passed 1,099 core tests in 11 affected suites. After +the last client reuse repair, 151 core tests in five affected suites passed +again. Full React passed 749/749 in 22 files on that final implementation. +Svelte passed 229 runtime tests and seven type assertions, with zero errors +from svelte-check. Router passed nine runtime tests and one type assertion with +its existing typechecker enabled. DB and React test TypeScript checks, Router +TypeScript and declaration builds, Svelte package build, and changed-file lint +passed. These scoped runs are not a full `pnpm test` result. + +Router's first combined invocation had 157 unhandled TS6059 diagnostics despite +passing assertions. Later native and explicit TypeScript runs pass without +configuration changes or disabled typechecking. Current configs match main. +A deliberately package-only rootDir reproduces that error shape, but the +original transient option's origin remains unproved. Svelte's first combined +invocation exposed 12 new callback-inference diagnostics. Explicit test callback +types fix them. Neither failed invocation counts as a clean GREEN. + +Final minified output checks pass: six consumer programs, 108 public error +names plus metadata/query/update controls, 251 erased production error literals +(including 20 shared), generated error documentation, and the 414-name mangle +census. Forcing DbClient into a plain consumer fails the intended inclusion +assertion. Stale React and IVM source controls previously passed incorrectly; +all-alias freshness now rejects them. These build laws use actual emitted bytes +and artifact checks rather than a runtime state model. + +Commands from the repository root: + +```sh +pnpm --filter @tanstack/react-db test --coverage.enabled=false --typecheck.enabled=false --maxWorkers=2 +pnpm --filter @tanstack/svelte-db test --coverage.enabled=false --maxWorkers=2 +pnpm --filter @tanstack/svelte-db test:types +pnpm --filter @tanstack/react-router-with-db test --coverage.enabled=false +pnpm exec tsc -p packages/db/tsconfig.test.json --noEmit +pnpm exec tsc -p packages/react-db/tsconfig.json --noEmit +pnpm exec tsc -p packages/react-router-with-db/tsconfig.json --noEmit --incremental false +pnpm test:ssr-bundle +pnpm test:minified-db +pnpm test:production-errors +pnpm check:mangle +pnpm lint:changed +``` + +The existing installation was reused. A pnpm dependency-check attempt aborted +without a TTY before removing modules. No install override or lockfile workaround +was committed. Ordinary builds produce ESM/CJS input, while consumer comparisons +perform final minification. Source changes require fresh aliased distributions. + +Source SHA-256 values below bind runtime evidence to this record's containing +commit without needing a self-referential commit hash. Historical reviewed head +and baseline remain at the top. Test/driver hashes are included because later +formatting changes their bytes even when the comparison stays the same. + +| File | SHA-256 | +| --- | --- | +| `packages/db/src/client.ts` | `79156819785ca62b8eb362af15842306e64d879cd2741c5fb3db2a39901f1c05` | +| `packages/db/src/live-query-observer-client.ts` | `9cdddb316671d391aa6c99bef5f32e088f4347c1d7bd4acea2900154d0c75d0a` | +| `packages/db/src/live-query-observer.ts` | `0a29dbc376ced35924c1793be506c087c57ac0b3d61798fc60337f1f9d87d016` | +| `packages/db/src/query/pooled-live-query.ts` | `1115cd6f589fae712893607de24d29dc84f1cdfb862774eabead60b7382a1da7` | +| `packages/react-router-with-db/src/index.tsx` | `9721cc2d847cb42f70741756606766e102ac39e9074c81e574d2f6ce4f88ac44` | +| `packages/svelte-db/src/DbProvider.svelte` | `26f60dd8b2b4f0488b4a91cc4d7e59e43eaf070077210914e3a98ac49c2d75a6` | +| `packages/svelte-db/src/db-context.ts` | `9d7d42efd128b33e4f6000fa486a391e0fa152939793b0ca381f8e1a663d3dae` | +| `packages/svelte-db/src/useLiveQuery.svelte.ts` | `629d788541f04be3dfa0864dc2938ce1aaeaa481a79637b6285c09484c83c333` | +| `packages/db/tests/ssr-stream-view-oracle.ts` | `196bf72daf8f6195994de54d174352c0e1dbb8fda25849d4e37d4305f00773b5` | +| `packages/db/tests/ssr-stream-record-wait-oracle.test.ts` | `284d7b1d47e2c57b40efb3323d9583c9a81ab67ca52ea52accf4db0de315abc5` | +| `packages/db/tests/live-query-observer-history-oracle.property.test.ts` | `82beb7deb80d093b0a5a7ef2e7f81b49a2d5d6a7e4dc159799764562ce2640a3` | +| `packages/db/tests/query/pooled-live-query-gc-oracle.test.ts` | `08f84e9489a0e09753784991c92e0ee53fbdfc5e5e480bdb755640677b2b5266` | +| `packages/db/tests/ssr-snapshot-admission-oracle.ts` | `cffbb2398f8e6a628815406a5d3e4f5a0505e9e77b33dba49a29c5f09c6a70a7` | +| `packages/react-db/tests/descriptor-query-binding-oracle.test.tsx` | `a399991de46a3c61b5c642740ab1ddf421fd3083c17a713250973a84ecaca9bd` | +| `packages/react-db/tests/ssr-runtime-error-handoff-oracle.test.tsx` | `4a895440735ae1b3345187cb9dc7af65157f4b7ec761067d42c3b2532ce725c8` | +| `packages/svelte-db/tests/ssr-snapshot-handoff-oracle.svelte.test.ts` | `05cfef8f6ac8be3d2ad1690a269259a2084b990c5495e178a44e696ef550c05f` | +| `packages/svelte-db/tests/ssr-runtime-error-oracle.svelte.test.ts` | `41640b69f05edcee6f784bd5a1f44840f5f44fb4cfe7cc55704daed8ee838f66` | +| `examples/react/start-ssr-e2e/e2e/router-stream-view.spec.ts` | `bf0a3690d09eb926c34d22278407006af5a0ee2d2e16bce317d4436af5bfd9d3` | + +CodeRabbit review IDs `5618234197` and `5620262151` were already evaluated in +[the migration record](2026-10-22-dbclient-migration-feedback.md#subsequent-coderabbit-evaluation). +The source-reference and two wording findings are fixed. The proposed automatic +cleanup retry was superseded by the maintainer's explicit rejection policy. +The mutable summary remains paused at `da37bc9b`; its stale minimal-risk label +does not review these repairs. No new CodeRabbit review appeared during this +round's pre-push poll. + +Repair-round line bookkeeping, excluding this review record: + +| Kind | Added | Removed | +| --- | ---: | ---: | +| Runtime production | 196 | 69 | +| Tests and receiving drivers | 3111 | 85 | +| Contract documentation and changeset | 178 | 46 | +| Build and configuration support | 18 | 11 | + +### Native integration closeout + +The complete Start example suite passed **15/15 Chromium tests** in 12.8 seconds: +the existing two Vite Start application cases and all 13 new Router receiving +histories. It ran with the example's existing Playwright configuration and +Vite on port 4175, using the final rebuilt DB distribution. Command from the +example: `playwright test --workers=1`. + +The first integration attempt passed the 13 receiving histories but failed the +two application routes before hydration because Babel could not resolve +`baseline-browser-mapping`. Its local dependency link named absent version +2.11.32. A reversible ignored node_modules resolution to the already cached +2.11.27 allowed the complete suite to run. The original dangling link remains +preserved. No source, package manifest, lockfile, dependency override, generated +route tree, or Vite/Playwright configuration changed for this local repair. +The initial dependency failure receives no application-assertion credit. + +This real Vite Start check supplements the byte-preserving mini-host witness. +It does not establish deployed Electric timing or every provider/renderer +version. Final library sources are unchanged from the binding table above. + +### Post-commit source-binding correction + +The pre-commit ESLint task sorted two imported names in +`packages/svelte-db/tests/ssr-snapshot-handoff-oracle.svelte.test.ts`. No test +logic or runtime source changed. Its committed SHA-256 is +`e71adfcf7052b2040a3cc8cb90f693fd815c34deda80bc31761db938a18d2d06`, +replacing `05cfef8f6ac8be3d2ad1690a269259a2084b990c5495e178a44e696ef550c05f` +in the binding table. The final Svelte invocation passed all 236 runtime/type +assertions with zero source/type errors. All other bindings match the committed +files. The repair commit is `0cf7294da3c14fbda6c8186da3c11eebb89ce064`. + +### CI replay-registration correction + +At `255312c4068714a7778c17bd6de4e325b8bddce0`, CI run `40397107769`, +job `121834000852`, failed the replay manifest's literal-owner check. +The React runtime receiver constructed both registered property IDs from the +hook name. An explicit mapping now supplies the same IDs to the same campaigns. +The manifest check, model, history grammar, run budgets, reach requirements and +public comparisons are unchanged. No production source changed. + +The focused manifest test reproduced the failure before the edit and passed +afterward. The complete `oracle-replay.test.ts` passed **38/38**. The React +runtime receiver passed **36/36**, with no type errors; each fixed/random +campaign executed 64 histories and satisfied its declared reach. Guarded +replays of `react.ssr-runtime-ordinary` and `react.ssr-runtime-infinite`, each +with seed `2229` and path `6`, reported exactly one completed property run. +ESLint and `git diff --check` passed. This corrects registration representation; +it adds no behavioral coverage or broader closure claim. + +The receiver's updated SHA-256 is +`b616de7426e75970a6a8fd409d539b7daf2bc805602fbbc90ff37af2bb71fa3f`. +All production bindings above remain unchanged. CodeRabbit review IDs and +inline comments were unchanged on this CI poll; its summary remains paused at +`da37bc9b`. GitHub reports a conflict with main, separately from CI status. + +### Main integration after CI registration repair + +The maintainer authorized merging main after the conflict blocked CI. +The merge combines branch `07d43c750025b07e8c77e4c475a70841a233d093` +with main `45c33292b2c4f09a63f856a829c1e15ca2b470aa`. +Its only conflict was the replay property registry. The resolution retains all +branch IDs and all four persisted-options IDs from main, without changing any +model, grammar, budget or comparison. The local dependency override and +lockfile patch are byte-for-byte unchanged and remain uncommitted. + +The combined tree passed **38/38 replay tests**. Core and browser each passed +all four persisted-options campaign tests, including their generated consumer +type comparisons and declared reach. The initial commands also invoked global +TypeScript 7.0.2 for the separate harness typecheck and exited nonzero with +source diagnostics; those exits are not GREEN evidence. With the workspace +compiler, TypeScript 5.9.3, both harness typechecks passed with no errors, as did +all nine existing core persistence type assertions. ESLint for the resolved +registry and both staged and working-tree whitespace checks passed. +No subscription or SSR production source changed during this merge. + +### Integration with opt-in operators + +The next requested merge combines `bb83e42a4a00d0ea772c4a211fd19d83bb7c5a7e` +with main `6c1ca8f8f9efe437d3cea61b5b9d0a086e2a5e34` (#2232). +Eight conflicts retained both sets of tests, coverage statements and error sites. +The SSR helper now imports the pooled-source lookup from main's new lightweight +module. The mangle-cache writer preserved safe assignments and assigned unique +names to the combined private members; its 418 entries pass validation. + +The existing production-error census caught an integration defect before its +repair: main allocated class codes 247/248, which this branch used for two SSR +sites. `never reuses a class code` failed on both sites. Their codes are now +252/253, with matching fixtures and regenerated documentation. The unchanged +independent uniqueness check passes for the complete combined census. No model +rule or assertion was relaxed. + +Combined-tree verification passed: **168 core checks**, **780 React checks**, +**241 Svelte checks**, **10 Router runtime/type checks**, **38 replay checks**, +and **15 Start/Chromium checks**. Framework typechecks report no errors. All +affected packages built; six SSR bundle comparisons, minified public API and +production-message erasure checks passed. The PR diff passes +`git diff --check origin/main`; a first-parent check also flags trailing spaces +already present in main's generated reference pages. The local dependency +patch is unchanged and stays outside the merge commit. Earlier evidence limits +remain; these runs verify integration with this main commit. + +Updated production and receiving-source bindings before the merge commit: + +| File | SHA-256 | +| --- | --- | +| `packages/db/src/client.ts` | `b8209ae4a18372271a7a6d94e270043adf8aa8499e6c66f3a8ff171d5337c0f8` | +| `packages/db/src/live-query-observer-client.ts` | `de6a592194f8c6a468b76f2bcae335cde6a466c52a5d7e4b282ffab604f95232` | +| `packages/db/src/live-query-observer.ts` | `0a29dbc376ced35924c1793be506c087c57ac0b3d61798fc60337f1f9d87d016` | +| `packages/db/src/query/pooled-live-query.ts` | `20a2998f60289026fbacb291079ebdc86cea5437c63eb04229b8e8eed9c710b8` | +| `packages/db/tests/query/pooled-live-query-gc-oracle.test.ts` | `38ff3a3805ac0d662a5f4a8fa12e64a28132607723bb7fd04cf14005b1c7b35b` | + +The React descriptor receiver remains at its earlier `a399991d…` binding. + +`pnpm size:compare --base origin/main --exact-base --files` passed against +`6c1ca8f8f`. The typical app decreased **412 B gzip / 375 B Brotli**; all eight +app scenarios decreased. The complete DB package increased **1,457 B gzip / +1,276 B Brotli**, and React increased **22 B / 17 B**. Other measured packages +were unchanged, for a package total increase of **1,479 B / 1,293 B**. These +package totals retain every export. The six bundle comparisons above separately +verify that ordinary, Suspense and infinite hooks exclude SSR implementation +without DbClient; the controls retain it. This integration measurement uses the +earlier SSR extraction and refactoring, without claiming a new size reduction +from the mechanical merge repairs. Full measurements are in the task scratch +file `second-main-size.log`. + +### Documentation CI integration + +At `7fff3a7e0`, CI's new documentation typecheck failed on the manual-index +example. Its isolated fragment left `todoCollection` undeclared, so the checker +supplied a Collection where `DbClient.collection()` requires a descriptor. +The focused check reproduced that error. The example now accepts an explicitly +typed descriptor and imports its API names. One obsolete known-failure baseline +entry for a persistence example was removed; no failure was added to the +baseline. No runtime source or oracle assertion changed. + +The complete `pnpm test:doc-snippets` check passed: 760 examples across 255 +files, zero new failures, 279 existing known failures (61 do not parse). +`git diff --check` passed. + +The previous manual CI run also exposed an ordered-work mismatch. Its exact +replay fails on both `7fff3a7e0` and main `6c1ca8f8f`: property +`ordered-work.eager-indexed-window`, seed `1902868771`, path +`19:2:2:4:4:4:5:9:18:15`, two full-source requests where the comparison expects +zero after the final signalling refetch. Each replay executes one history and +reports no type errors. This is the existing unindexed-continuation contract +question recorded in the coverage map, with another distinguishing joined-filter +history. Main's matching failure establishes that this PR did not introduce +this counterexample; it does not establish whether production or the work +comparison is correct. The owner and needed contract witness remain recorded +there, and the comparison remains unchanged. diff --git a/docs/contributing/oracle-reviews/2026-10-27-convex-ci-types.md b/docs/contributing/oracle-reviews/2026-10-27-convex-ci-types.md new file mode 100644 index 0000000000..116583c0b2 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-27-convex-ci-types.md @@ -0,0 +1,11 @@ +# CI type integration follow-up + +Parent: `23a097ca6716384c4e8b29bc68b032b9152dded3`. + +PR workflow 40569615912 failed at two type boundaries. The React receiving witness accessed an optional Collection during cleanup without narrowing it. The documentation checker rejected five Electric examples whose untyped key callbacks returned `unknown`. + +The React cleanup callback now checks for a missing Collection, throws if absent, and otherwise performs the same cleanup. Its independent model, page histories, publication recorder, and comparisons remain unchanged. This is a test typing repair, not a product behavior change or new oracle closure claim. Earlier law, reach and mutant evidence retains its original source binding. + +The Electric examples declare the string ID expected by their key callbacks. The schema/parser example imports Zod so its output type includes Date and Electric can infer the matching parser extension. No casts, suppression, baseline additions, or runtime changes were introduced. + +Validation: rebuilt DB; all four React receiving cases passed with Vitest typechecking reporting no errors. The full documentation check passed all 769 examples against the existing baseline: zero new failures, 276 known failures, including 61 parse failures. Changed-file oracle lint and targeted React ESLint passed. The RED evidence is the parent CI log, including React TS18048 at cleanup and five Electric TS2769 diagnostics. These checks establish type integration and the existing four React page-size histories, not a new product law or cloud-provider guarantee. diff --git a/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-app-abort-source.json b/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-app-abort-source.json new file mode 100644 index 0000000000..3dc3f052d9 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-app-abort-source.json @@ -0,0 +1,433 @@ +{ + "starting_head": "8c24922026fb4a714cb54b992ac85faa2e876742", + "binding": "SHA-256 of final dirty local sources; this manifest and the unchanged local pnpm override/lockfile workaround excluded", + "files_sha256": { + ".agents/skills/oracle-authoring/SKILL.md": "bdaf4cc887a9e89ade347eb938cb37a0de480de617e6bc8128af953dac90420d", + ".agents/skills/oracle-review/SKILL.md": "cf1f034838b5d04b6f95c4123bf32c18623a791b3bc5a02888bd43709f9308fb", + ".changeset/convex-collection.md": "c7ad56ad983fe653f0054f0a82fcd47cad46c1d59087d62d79a21ffb077eb324", + ".github/workflows/e2e-tests.yml": "62f257aab4d6629d4227edbf29e659d3d9a4149ca103add59f5d218924ccb50f", + "AGENTS.md": "dc10902d2eebee9d8e452d8e4b934e4764647b7f9eeedf8a1bd9b5a4bf570e64", + "docs/collections/convex-collection.md": "3901d5d040f8e101369f1a16a160d4e5d0df391ec48408f99d995191ad013105", + "docs/contributing/glossary.md": "0cf3403016fa085079c72b301ef0e1c7533b4f625be1edbf66a78019a1c1e542", + "docs/contributing/oracle-coverage.md": "1d3ca3e76908285cb28fcce9bfb699b45d54857a7f4f23b32c0f23555519f05f", + "docs/contributing/oracle-reviews/2026-10-26-convex-lazy-paging-source.json": "a4af1a586c058d08644fead26679a2894209aa6cbe3803dcf1c8cbefab97bcb1", + "docs/contributing/oracle-reviews/2026-10-26-convex-lazy-paging.md": "1de8506081336953f4b1c53933375960c84af277c04cb3dc07b91b79df391810", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-app-abort.md": "12f0d22c630c1461b32b91fb56092421a58776c45b72fec4de6f0a7ca7d2296a", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-backend-concurrency-source.json": "b859cc163b55b7b9076a0d2c94bad1b524fd0405de39a0689c503c142975ba62", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-backend-concurrency.md": "adea3ed30c6f5df32e5af261497afd84c5c5519ca1ef71130681e899e7eba450", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-boundaries-source.json": "8b6780ecc7cfa6bab39524f20cba9999425507a9860ded3ed20b47e7a2485760", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-boundaries.md": "dae5fd558a6fe1e7cc2adaff2f977496edfed349be1494994bd3f890c00c4845", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-interleavings-source.json": "0fa1e828f4fee657e9011770bef73da2ac02cb4ca068d386d3a662c81a3e7c8b", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-interleavings.md": "f105899875a4129b7d816cacc1d6f094c68ad6048cf70350e8d105bd0acd89e6", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-paging-source.json": "d25abd9b66a277ef20186bc507ecbcd082f8562a7acbfd2b6d691704434a110d", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-paging.md": "d057323e0dee9bab5cf8fbba85fa6d6c525ffc0be7843f1e351d7e48a6e42fff", + "docs/contributing/oracle-rubric.md": "0ced21586bcddb8959d380575103cc3b66e7045a3b22736a173df923847ee166", + "packages/convex-db-collection/README.md": "2b92d0ce849e926acdc2d204e09fd9af3a662c8c8314ad8db5d223e74b77d12c", + "packages/convex-db-collection/e2e/backend/convex.json": "e7106add2ff21d61a430927bb9b1ba96b036b93fcecc31ff786149a6070ecd67", + "packages/convex-db-collection/e2e/backend/convex/schema.ts": "319611bbb3886d4b5c7bdbfbccab1f42792769304e2be8db009b73baf90aa92f", + "packages/convex-db-collection/e2e/backend/convex/tasks.ts": "b2e1d75b0c225d950df2070840955bf1b26b6a827d21a88a2b1e08108ada680e", + "packages/convex-db-collection/e2e/backend/package.json": "8e2e33603aa201acb7c8d88a55130bc07275d8fb53693e0965539ae4bc4e3585", + "packages/convex-db-collection/e2e/lazy-app-write-oracle.test.ts": "ad265bbbf78b617e1adf1c5df77991c4a2ce789ce8e78423ff56249dc353700b", + "packages/convex-db-collection/e2e/lazy-boundary-oracle.test.ts": "c14b42dcfbd28e1c875204593c10d7f98ebc6323cdcba32736be8315147ddccb", + "packages/convex-db-collection/e2e/lazy-concurrency-oracle.test.ts": "69a02d34d0efd0d1881cfbd321f22aac2e820597efd2cd32fc3fa1dec745b625", + "packages/convex-db-collection/e2e/lazy-paging-oracle.test.ts": "7a5c1390879e4e01757d020e22bfa24f5852a2cf5bb4e18faa4174ce47a02e48", + "packages/convex-db-collection/e2e/local-backend.ts": "e38be0c546a885f55b004f23bc894ab86d3c3b5880bfd9dde8208866a42aea5d", + "packages/convex-db-collection/e2e/network-gate.ts": "73851db1ae46e83b7ee1bdb88faf01aaec27178644a73e1f7835f94681806dd6", + "packages/convex-db-collection/e2e/setup.ts": "d448bab619ac7243957578b2565cdb647a5e71a70df3c65af3c3dc378b5df6bb", + "packages/convex-db-collection/package.json": "0d1420d6d318ab205560bb09b8be2fac7398998c963ce670f612c73611107851", + "packages/convex-db-collection/src/index.ts": "4247e2bbd168a80791861f4553c10d0d878504a7cdc6a65d4bc648e15b1e51ba", + "packages/convex-db-collection/src/offline.ts": "55e70bdbbc310226a9f0175d0c0912218413ba67433b5b47ccc78d6e7dadd5c3", + "packages/convex-db-collection/src/options.ts": "ea2c2efc4d574080c5e03da8e121b79406640c667a1e260ead9eff22f6253d33", + "packages/convex-db-collection/src/paginated-options.ts": "de0e92849d363f2545823d96829964084b1a115b890f0f521f9bdc0958ea2b76", + "packages/convex-db-collection/src/pagination-demand.ts": "d2be74c1f3610c153c2b7c3c9411aa6ea216db7e76c9b0a7f32d63ef2bde57ce", + "packages/convex-db-collection/src/pagination.ts": "0308668d07db701d1f7f891d001a4aca2798973ea29bf1d4b00931eaa0e05f1d", + "packages/convex-db-collection/src/subscription.ts": "a0c312caf2af3667c18e96c565ceb159c20d65550426cdd44aca69a43a3543e5", + "packages/convex-db-collection/tests/helpers.ts": "11f2c79c6872373e8f77efae62d2fabfead0e5af6673c86920ae2ef4abdb7274", + "packages/convex-db-collection/tests/lazy-paging-abort-replay-oracle.property.test.ts": "ba40916ea9c3d813d5f9df2c86ee1c6b849fd1decede97f3477f25cddeb432c3", + "packages/convex-db-collection/tests/lazy-paging-campaign.ts": "bf5dc54dd05db268c88ac1b8b9b32aa5a2765245658608e52163089d4601d63c", + "packages/convex-db-collection/tests/lazy-paging-driver.ts": "f8d04cdd895a06fedcae907b2416ca550191ad5fadf8215ad29953cd1a739a6f", + "packages/convex-db-collection/tests/lazy-paging-failure-history-oracle.property.test.ts": "65ff6c00d61014e841684ec7783f3175b7a013183f4bf671bcbeedb45212f272", + "packages/convex-db-collection/tests/lazy-paging-history-oracle.property.test.ts": "5bc8808d6f71648f48e40acb05943132448f3e8f5bba31d97e261f1741674384", + "packages/convex-db-collection/tests/lazy-paging-interleaving-oracle.property.test.ts": "f3a0238452746233224bc0b21adf790b594cacef82d786a0ba419d19980bb773", + "packages/convex-db-collection/tests/lazy-paging-load-outcome-oracle.property.test.ts": "bdccff66dcc336a2ebd687e31d5bddac0f0eedb096b15e503a40b53ed714c521", + "packages/convex-db-collection/tests/lazy-paging-model-oracle.ts": "7a0ce99a3442b15d516f03f0494b5a5cb39cc7106452ce50e257af79a014fc7b", + "packages/convex-db-collection/tests/lazy-paging-native-receipt-oracle.property.test.ts": "5714e2c25507f1ea4aef5bb6eb9bed69bc9a9b079540667570227aa1014a0822", + "packages/convex-db-collection/tests/lazy-paging-oracle.property.test.ts": "3a7b5b6284076bc8dbdb59ac1d735f509c6bc19201ad355a0382e7a797a2d569", + "packages/convex-db-collection/tests/lazy-paging-receipt-model.ts": "b3714d83e0a90ceb9184a1701059f027907ff663ce829fa58895007147cf1453", + "packages/convex-db-collection/tests/lazy-paging-receipt-oracle.property.test.ts": "06b60b0a86926070dd57dd068f4abe572d48924e2cea29acb77f417bf8115d6a", + "packages/convex-db-collection/tests/lazy-paging-revalidation-oracle.property.test.ts": "130b4e11498b9983cf123d03dd116957f089ac14c18ed7cbbd4ed6a165bdc10a", + "packages/convex-db-collection/tests/lazy-paging-version-model-oracle.ts": "5aee0ba04f556d312e25f03c6077949d47daf6d0e4eae6cd3dad0a9fe9ce86a0", + "packages/convex-db-collection/tests/lazy-paging-version-oracle.property.test.ts": "8f4f115806a65dc71b3382dade7a89e326decc2b3c25505369783f30163a210e", + "packages/convex-db-collection/tests/lazy-paging-write-model-oracle.test.ts": "134d1a0cad9e8397657df4fba055ecbaf1587244dfd0b68c3c481025cd56b163", + "packages/convex-db-collection/tests/lazy-paging-write-model-oracle.ts": "7220c5df1f39b1ee12159d63c7272afec157221e91bec8d7b22e51be7e09cc93", + "packages/convex-db-collection/tests/sdk-cache-oracle.test.ts": "0e011a20ce991195742bdfa0cd570d831105604ad8e3e2cc4b8fab4b62daad58", + "packages/convex-db-collection/tests/types.test-d.ts": "3ff1a42d6caf95d14e5270b632340524a9f796b978ccd031835bd1f2d0dab6dd", + "packages/convex-db-collection/tsconfig.json": "623bc70a72fd718e7d209c90a08df12cced0e9045a3565058623e34e946a5710", + "packages/convex-db-collection/tsconfig.test.json": "5168aca44188a9be3b0bee215bfc2f222010dedb8f4e1fc08029c6873dcb6cf1", + "packages/convex-db-collection/vite.config.ts": "3f4d5abb1fb86d41ee205bf6364e6f38ffb8de98f5d42cb3d233e52e674902bf", + "packages/convex-db-collection/vitest.e2e.config.ts": "b43b7b797860455efa827774e9847975bd9850f81b9b284f43665c422e45b440", + "packages/convex-db-collection/vitest.lazy.e2e.config.ts": "f70fe4252fa4814f8a48c0a264de8384868c9f47abe7166bd2e3363c211f4176", + "packages/db/package.json": "bb6df01ff9589c8b75437683c935b8457f65e9216d37173a4730e1a169e0ab43", + "packages/db/src/collection/subscription.ts": "c446932a8a1f8b2efd79d00846f1a110e65a6c6df75d2b24658053e6964b530b", + "packages/db/src/query/live/ARCHITECTURE.md": "aa6a41b651809819d414d364421140bab02f9b992e9f5dbe73cf634b43c46478", + "packages/db/src/types.ts": "2b4c5926922db049e7ccd58c3e3fc22503f6d8a9400f3fb01900e82da85c7a1e", + "packages/db/tests/collection-subscription-lifecycle-grammar-oracle.ts": "39d6bcdbeeab1d4121618b23d392b1bf38eade5361b32ece9ee6d8f5e804ea96", + "packages/db/tests/optimistic-history-oracle.ts": "ccfbfd1e62f7692a49cc9fbe4bf7c8440e9631b4fe4d1ed369f60c2ee4a9da10", + "packages/db/tests/query/load-subset-joint-replay-oracle.property.test.ts": "975585ef1d1bc921afcab2e5384318c5f661dd0a2577e642351b2aafed0ee98d", + "packages/react-db/tests/convex-lazy-paging-oracle.test.tsx": "0a33955d45816d766100b89e45dd63ce32125864cf3e2f0ea46533a582b0333b", + "scripts/agent-lint-feedback.mjs": "ec3d4783f5f3d7d71ea3e3f93ec57e300c272b219448407982a1f4247d5d82cf", + "scripts/commonjs-declarations.mjs": "3dae7c394287bd5d74467947267ab35207a6290b2fb5b12dcf7b2664115b9d66", + "tsconfig.json": "79e4282995ed94ce7e73583a6ef896db6dd8968b276f2a900de8d63493ace99b" + }, + "runtime_binding": "Prior verified DB runtime, source maps and embedded-source bindings rechecked unchanged", + "runtime_js_sha256": { + "packages/db/dist/esm/SortedMap.js": "ecbe71a4b4387932ed64817362c31eeb58c151d1fda5ba6824a491e4d6eeaf84", + "packages/db/dist/esm/client.js": "4191f1ff58a2b3eae418d2e8dc56b0fe16a290ff233129535b0945f41a34f0da", + "packages/db/dist/esm/collection/change-events.js": "cb5740b95d7c403e0ed7fd8b2aa831a921492782fc66c47bf1c91647070de0ac", + "packages/db/dist/esm/collection/changes.js": "084445baf434ffbfce6ceba34108c32dea5a58b5db68d6c068af6a8b762fff34", + "packages/db/dist/esm/collection/cleanup-queue.js": "3378b66f70261b2ce7eb14fdd982114e838ba6ce6d61ec18b0de976605cb9ba2", + "packages/db/dist/esm/collection/config-errors.js": "d6beb8dddab089d6dfb2516b5850b4b2a709b2bb2a9843bda854ad47723c35d1", + "packages/db/dist/esm/collection/events.js": "0cb3eadd7a9ce2e454e0c4a1a1b0023d2fb96724ad5a4dc806684f9ed4e47d17", + "packages/db/dist/esm/collection/index.js": "01e254fc0d024e7e716b4b09a10e7656130663ae587ad72314873f3b87f5fc2f", + "packages/db/dist/esm/collection/indexes.js": "9d1dbc166c3a44cd9348a62d96c7724335ef38c6f466575c0bbc2419e5647130", + "packages/db/dist/esm/collection/lifecycle.js": "2a26fe829e0183e77a33777f3003e1ab8699a4a55ec6fc309336b35c581ea03b", + "packages/db/dist/esm/collection/mutations.js": "42ed12785749b4098b422a538155ade6d002eaef3ad9cae227c0218f44c95e42", + "packages/db/dist/esm/collection/state.js": "d03955c898fc1672a1a708723d6e4506064665f8a24c57aea6be956f2c4dd0b4", + "packages/db/dist/esm/collection/subscription.js": "ac04fdd5bb56894afa45ecd9bef37abd4f96418c7912cd329f40e397370d27aa", + "packages/db/dist/esm/collection/sync.js": "82be78b67610f59fe60bad6e9f9776884287d38d6bc5641d1478875a559c6961", + "packages/db/dist/esm/collection/validate-config.js": "16a2b254fde0bc0d37c376107a5f394b17dab89165d99ff56a1897af9c234138", + "packages/db/dist/esm/collection-options-brand.js": "ae4fa09d0ac1ff1a9be961efa924d7dc769aad56a7e67cd1552c08b48fee3880", + "packages/db/dist/esm/collection-options.js": "1c6d225b2e8fd061f7e7616d67de43288583660e99a8a596b86d3f8c58c3a0de", + "packages/db/dist/esm/deferred.js": "d8ce1c72bd8f76a4e49aa17042e46a5432284730d1b2ceb4b5afbf927f881e2b", + "packages/db/dist/esm/error-message.js": "176a340e97f58cf9fbb21edc92cf43c6703ad2110310965b3da0f983e1cfab9f", + "packages/db/dist/esm/errors.js": "8c22d726d3878b029e2d9bf89c158ae2c25f915fdc9833711cf63fab3e48cd26", + "packages/db/dist/esm/event-emitter.js": "07add965808d5bd93b29d97e0e6d7db1c060856a1ea20cb7601a81785f2c67dd", + "packages/db/dist/esm/index.js": "fa5714b1da1264029dc75dd2f9e18edf145f128232feca5690392ff94dac6b8f", + "packages/db/dist/esm/indexed-db-wrapper.js": "5ca4ed531df467c317a8b302f218664a13ab579cc472eb4b9dda3b70854ec667", + "packages/db/dist/esm/indexed-db.js": "f7a7b78e057570562aa7eec0279e37a58ac7c4cfc05e820401f89728654dfbf7", + "packages/db/dist/esm/indexes/auto-index.js": "663f98c52c018571e87f398cfd4efe51bd27f48e2a4ac76b8e31327a725da8de", + "packages/db/dist/esm/indexes/base-index.js": "59652757da474e7119d759f784e954b56d9daecf586e385b2bda4ecd9219f465", + "packages/db/dist/esm/indexes/basic-index.js": "1ac365be033cb255d3b4005de57b7466b9ed4cd21daa08a77d8dfcfef0584626", + "packages/db/dist/esm/indexes/btree-index.js": "9ae7bf699fd9d44f9cc7dab823df85638efb2f43114eb33aaa7120efc367269b", + "packages/db/dist/esm/indexes/index-registry.js": "cd9448a61e290d32e23090cc57c8282aadb2d1736611a33509dbbe1f328d080b", + "packages/db/dist/esm/indexes/reverse-index.js": "94f0751672696624240f6e214a9b16517c9145269968e07ec7b2b8f439e3f25b", + "packages/db/dist/esm/live-query-adapter.js": "e787323960ffd3e848ad669ccfea1df3d21d80e084e75b6048740dfe23756e3c", + "packages/db/dist/esm/live-query-observer.js": "53eb064feaded0b54fb64e5a4741b2ed5cf94de00638a2bac0a95e573aab75d8", + "packages/db/dist/esm/live-query-options.js": "51ddfc4eb7ae745a75db197d67e23cd0b9b15f527b1fa706a759d90fa7920f2c", + "packages/db/dist/esm/live-query-primitives.js": "67f6abfb58ecba0abfa2e3c8094c98a86eadad39bd96afeb48912a77475bd76e", + "packages/db/dist/esm/live-query-window-binding.js": "3f239006fa71213f924008cccdb29b9ad57d97beff2947a30f0e0130370bac02", + "packages/db/dist/esm/live-query-window-controller.js": "bdb129aa7772774fe9fddb598d911bd7ae06cb8d581c05c41f47152b478f6805", + "packages/db/dist/esm/local-only.js": "0a4a5d855e4e3db629d20898eca261fa2dc02c3cb2d0c6feec32a052796930b4", + "packages/db/dist/esm/local-storage.js": "5a01856bf1eb5f1cb91aeb6536f3d685e8129aac6e9ea0c6c5feb62159aeb8ef", + "packages/db/dist/esm/optimistic-action.js": "ca9f92ae29a9710cd18e3e71c46a2cc0f204334525aab42174fdb4f53ec67b89", + "packages/db/dist/esm/paced-mutations.js": "01135f1c21fd5deefa89c92ecf7598bbe42d98aa284e646ac21c470c0d346369", + "packages/db/dist/esm/persisted-readiness.js": "160a5bdb6868f603e1bf1358ccabadd72cbe70c05d87f807972873bb03f9c850", + "packages/db/dist/esm/proxy.js": "33a224882c8dc0db103d6d0b92e50bb71021030e608937e7a800411ee9eb3fb6", + "packages/db/dist/esm/query/builder/clone-query.js": "9fdd9595f744fd286b55de26cd552bd630a358e4114dd605ad4d75a474ba70ec", + "packages/db/dist/esm/query/builder/functions.js": "d43c221d002d703965be1587f725255ceb58dd11258670b2882c3451340efe55", + "packages/db/dist/esm/query/builder/index.js": "c42986783a038350a694a9d8352f559017f62fc4b51197ca99f6ea1ce386a8fc", + "packages/db/dist/esm/query/builder/query-ir.js": "8d535e9be5fea36bbea302665f1d30a1b4a029fe4fdc95e6f674b6b0a3c5ad34", + "packages/db/dist/esm/query/builder/ref-proxy-identity.js": "fc756419ecb6b451640f729a44aa2e9cef4385d20ef1c68c049d36fde27d2b10", + "packages/db/dist/esm/query/builder/ref-proxy.js": "b71f544f804438a3c4a7ccec380fa0f4614ec7c859a983de4520d19390707993", + "packages/db/dist/esm/query/builder/wrapper-identity.js": "00c10f26051967ff8f284e550ab207552ac2632cbc08a7623617dad1c593bb06", + "packages/db/dist/esm/query/compiler/evaluators.js": "0ccf7503f360331210ffabf4a1003c96f904f831400ca2687f553f509a52f21b", + "packages/db/dist/esm/query/compiler/expressions.js": "eeb0c6dfca9871b4f7a1dd893c265e51e1b23d20400ac28579875446ac37ca87", + "packages/db/dist/esm/query/compiler/group-by.js": "ca935c6886fc9cb1574fc71e22efe688beceb36bd524a25a6aa29ae0bcf44dda", + "packages/db/dist/esm/query/compiler/index.js": "19667be77fa84da780beb102509c28d98abd365a330e8e7da4e9ac5a51154959", + "packages/db/dist/esm/query/compiler/joins.js": "af1bd496e41902c7e5b9759b4231d0487db2f67f8aa060b75fd326c7791c88b3", + "packages/db/dist/esm/query/compiler/lazy-targets.js": "04ed7aec7600a3ccf86ac28f06f8dd171770e56ca01a11b74d0b59fba6e083ea", + "packages/db/dist/esm/query/compiler/order-by.js": "e9844a5178d22908bf1ee6a723fe903d05110f6c333eefd8851a83165b39180d", + "packages/db/dist/esm/query/compiler/parent-routes.js": "bd0cf9a9781b321181ed3e2b6571d94dcbcd7bc784c53399b98051364c0fce80", + "packages/db/dist/esm/query/compiler/query-equivalence.js": "139c14fa078e5ba4c4a3d7490854c336870eea4c30f5960cd8f15980ceb00b57", + "packages/db/dist/esm/query/compiler/route-metadata.js": "6889f6b3dec56ef8fe5e6ee05e892dab1c22c0c0a13cbe09afa76774dae8a614", + "packages/db/dist/esm/query/compiler/select.js": "3f7d19d780395dd84c1b2b2c506e2b4f847a5a8a9442016bc5f72597f454af18", + "packages/db/dist/esm/query/effect.js": "59f37b190830c138368260801b09e7bc7a30e347fe36681b4796f6902c7e7c3f", + "packages/db/dist/esm/query/equality-conjunct.js": "ca0bee4e908fd4dbe52dc78feb172dca90492edca49cabe509c3cb4e4fd67fd7", + "packages/db/dist/esm/query/equality-value-identity.js": "5091c8c6fbb1e06127ef19db34258fd0d765b3c1b96c4a4f55b6fb553e37fcca", + "packages/db/dist/esm/query/expression-helpers.js": "5c957e7d0f23fb781aae8ead705404cf7bec3b1cd68db3fb46b356a1d25002ab", + "packages/db/dist/esm/query/ir-stable-identity.js": "a9cdba9efedc50caecaf7f8724f4146ab8d5f16d916f25707112a43cc36a0a8b", + "packages/db/dist/esm/query/ir.js": "9f2a67c11ecf08e1af74375eac7a603455e12ce56a75317376fd90b55193261a", + "packages/db/dist/esm/query/join-conditions.js": "0ede70428fc77b12d38c333b22767d629d50e88dca26af0c495c83c3308daa85", + "packages/db/dist/esm/query/live/bucket-facade-adapter.js": "daee317ea51844f450c893f7ce57e3383a0bf96c52e2bc4930d1aff951e5c7b3", + "packages/db/dist/esm/query/live/collection-config-builder.js": "93228d57a26e0e6dd5d51553c5fe10c9ed49f7803979ad21a76aa6805edec3b4", + "packages/db/dist/esm/query/live/collection-registry.js": "05e27e3d041d58aefc8820e523163b2e21ee53a3940680a0b468bb8e81e6d46d", + "packages/db/dist/esm/query/live/collection-subscriber.js": "11dc6621b866033008c2d6c9c2ba90ce36eb94d5f0f0ae3435ea55ecee736f3f", + "packages/db/dist/esm/query/live/graph-scheduler.js": "d3888f67b6e09ac29a3e45a120e3cbbceda49be7415eacaf0856cecf2cbc9e9a", + "packages/db/dist/esm/query/live/internal.js": "244a7f35268790408879ed8b1774fdd767b78829dc1a5ef6a0df4c2c372c0258", + "packages/db/dist/esm/query/live/materialized-pipeline.js": "6d98c2d4ee9412f5816c4151c14f73224b449738df183d0c82c37c08056c64c2", + "packages/db/dist/esm/query/live/ordered-source-loader.js": "63481aa7985a8e9de91f7e3499deb0e5d6f53d6abec6dd6614609ac1f1b5efda", + "packages/db/dist/esm/query/live/subset-demand-controller.js": "1163906179d8c68fbaf36507411c258ce664e8d4141f0a8c4b01f905f1cd1c27", + "packages/db/dist/esm/query/live/utils.js": "3df2bfd90ae95fda8c421e3131e0aaf90875e2cb1af514a09da42ec67e78f899", + "packages/db/dist/esm/query/live-query-collection.js": "f6024029a340a675df1e36ed180ab0d800ac0e697399a5b0ffdab392fdb87181", + "packages/db/dist/esm/query/optimizer.js": "2992bfaaf19a94828538613dcfffd71e4908952fd7c401d4fa01509c53776093", + "packages/db/dist/esm/query/pooled-live-query.js": "0c83e1139162d0314a0a6f1076911d4cf635877d6c9dbc89b8983d27cc90ac46", + "packages/db/dist/esm/query/query-once.js": "efa18058ba8d8abf2f201cf0a6418ab3f1c133d4d7464748f55c0ff8839150f8", + "packages/db/dist/esm/query/runtime-reference-identity.js": "3a5de8435888ede6b0edf799e5f0c192192aa1e0fad922450e9774ebceb4de18", + "packages/db/dist/esm/query/subset-dedupe.js": "06203c388ec4f1e525de8fe770775736222b62e273007e9a31bc37ba5fbab15a", + "packages/db/dist/esm/scheduler.js": "d9b4d34d6a4c656c0f38859426d39830ff62053ab47d22a14acc8b34bb6a8136", + "packages/db/dist/esm/strategies/commit-completion.js": "bd177de52002dceb9d28e02223da81b05c00ca5f72f5c06aa70699142904b263", + "packages/db/dist/esm/strategies/debounceStrategy.js": "88c73e9970201acbe24c3ea11632f2747f174cfb2840a53fdc4ba25224feffff", + "packages/db/dist/esm/strategies/queueStrategy.js": "abf15dc18ce9f481ef876478b0e2190188b960d76d7cedcf4f0c8b2a6818f9b8", + "packages/db/dist/esm/strategies/serial-pacer.js": "84c01c2f15ac57689b7f80a8ca0b152035cc2dedd8573e7e816b1a2bd0dfb08f", + "packages/db/dist/esm/strategies/throttleStrategy.js": "f646adf4f396ceddebca7580b398dc57edf1a22dfc2dd2e6c1eb607b412c0de0", + "packages/db/dist/esm/sync-persistence.js": "16292216a685c02ce1f3e1b2945a2409600e42d951c6acd19090dae99a38dacc", + "packages/db/dist/esm/sync-receipt.js": "c4365d1f36cff69c10194edef4903526fa85078cdf135172978075b5a3d56652", + "packages/db/dist/esm/transaction-commit-work.js": "c6838b1423ba8b0a1bb2ebf848fa37394416d52369d0d7ee5e8508d67f87066b", + "packages/db/dist/esm/transactions.js": "901116611280b5cbd71c3d9c49b6bad061174d90fdd0d2ab29f8cb66d5fb12d1", + "packages/db/dist/esm/utils/array-utils.js": "e8b8006009a956a5520822c9bb1ac8a9b13750613b0be937c2c2cb8d14c5d4c0", + "packages/db/dist/esm/utils/browser-polyfills.js": "620828c9fcf0df2181fed5d866a5ad20f1ac01ad5a296dcb04fd8be7012f6d75", + "packages/db/dist/esm/utils/btree.js": "2db68fc77ef20eb576502fd284668da1c9e04b18befc88dbf3d6842b722e75a6", + "packages/db/dist/esm/utils/callbacks.js": "0e5c79079dcbd00609aaeea1d769ae56fd881f4245740dfce408419773c599da", + "packages/db/dist/esm/utils/comparison.js": "19d37b5b97b84c1a19d331e277f47042e87ac01696d90a74fd3f76030d45e7ae", + "packages/db/dist/esm/utils/cursor.js": "1051977f9f8acbb6fb6884faf467c5c379cb54fdf0b02d22c9c1eb14cc52ca86", + "packages/db/dist/esm/utils/error.js": "a19d18998688b8a2ebd796bf668c694fe5f0c18fd4a4f8434f91589a7e436e40", + "packages/db/dist/esm/utils/get-or-create.js": "b1b71fa6f0e123e44f4908ea606ad6002515afbe204dd5841f72789f8fdaf3d3", + "packages/db/dist/esm/utils/index-optimization.js": "24ffd884fb0c24a49f614c5cc7fa9b37d9942bf59ccb12ccba07c7a0a328ea1c", + "packages/db/dist/esm/utils/source-record.js": "dcb726a12fb21ca74fafa5eb78ab7b83f1d18c2176fee2af11f430322ada6f48", + "packages/db/dist/esm/utils/type-guards.js": "a0327ad9ec010fa5f75a73b01b302c19433258db3ae23ce563a97eff8d981fa6", + "packages/db/dist/esm/utils/uuid.js": "fae77bf0bc610f2fe2e2b7b3ac289f48b572b223973780c6b895bb878c68e8f4", + "packages/db/dist/esm/utils.js": "ab6496511234ca87fd1c59c51c7b8f2f8b8601ea5d7ed15057f3068a88210f4f", + "packages/db/dist/esm/virtual-props.js": "76a0a0e70ee0f959dc7f4c2dac49bdcd24d8a7427b1cd59fb708d7bf47a7fde0" + }, + "runtime_source_map_sha256": { + "packages/db/dist/esm/SortedMap.js.map": "9fa31bea314653a026871313192f228eb75af82e8cec748e8041d1d7bc3d6978", + "packages/db/dist/esm/client.js.map": "33a90696c3b077207b8b1ccd57b1e9299f505a9d468466746066dfe3936c9a32", + "packages/db/dist/esm/collection/change-events.js.map": "c53651fdb6c5b229a2280d45af00a4ec969d5edd48d11608e96c8e813e109ceb", + "packages/db/dist/esm/collection/changes.js.map": "66ba53f09b63f0e668f7522297736919261a64518ec2c43b16eec770e413dbe1", + "packages/db/dist/esm/collection/cleanup-queue.js.map": "84777c1baefeb9e15bfad6f2144decb14ad0236895f8eb59d6721221aacf25e4", + "packages/db/dist/esm/collection/config-errors.js.map": "6bb878aebe57ff145f43dd2afb85c80041cff4b39546ae5e6eb3c7c1b7ec57ad", + "packages/db/dist/esm/collection/events.js.map": "4c035f44d2a25221e333af7799d8dbb06d8b69a12584fe6668f517c71ea87b4c", + "packages/db/dist/esm/collection/index.js.map": "2e50904cadd394695e45c5e82bbdaf1f22f29d6787e7ece5e225250aa721f438", + "packages/db/dist/esm/collection/indexes.js.map": "415a842ba3570ad1a6f341edc761c80b7c37a41c1dcc568121a854d95fcd91f5", + "packages/db/dist/esm/collection/lifecycle.js.map": "12486a4c6d0a7dcaca4489596e2e6da0deb1284c63538c3547773cfd1a1af3c4", + "packages/db/dist/esm/collection/mutations.js.map": "57b450f3fe34ff7c279e349338b525140fe287754763aa4e335cbae08f4374b7", + "packages/db/dist/esm/collection/state.js.map": "f11a3431edc53bcffebcd0bbd6c27af38c2a8c38e5b36e2d2b66b934031b835b", + "packages/db/dist/esm/collection/subscription.js.map": "73c1f1ca5d23cae10c45175164f5e2c59aa10138f13c86ed6ee775f1655765bd", + "packages/db/dist/esm/collection/sync.js.map": "cccc3eeceefdfebde3c092cc3944e23a2aae67eb0c57ff7f3a0694fec98d1d76", + "packages/db/dist/esm/collection/validate-config.js.map": "fd8cea162de4fe8ef094df6522aa8705b83c12fbfe8f9433ee556b3aa76d069e", + "packages/db/dist/esm/collection-options-brand.js.map": "006f1ec5a86b56a67c81e1450d129a8c18c2ff40aeedbac730ff344cac539248", + "packages/db/dist/esm/collection-options.js.map": "5e0b50e274db3185ed9b0c7192920a562c2d9669e57bdb4317ff27a0c7bc0bf8", + "packages/db/dist/esm/deferred.js.map": "c87f06ed0c6b0f229700d59008329205ff3fe7fd5466306982bd932ae273bfcd", + "packages/db/dist/esm/error-message.js.map": "1ddef42c2961d5d65ffd8bafd3a639431a539cf90da41cd5ee794710f3f2884c", + "packages/db/dist/esm/errors.js.map": "6d4e905f2f6c38ea74cef7cbb5fa6f46ccef861488bb57eb2096d3fe4e8fdd9d", + "packages/db/dist/esm/event-emitter.js.map": "b0b3a8588c21ee62b7d3f23d2dffe07ae67f3a451f5151b6f7dbc528c7d009f4", + "packages/db/dist/esm/index.js.map": "f8108774caf02d99844c09563d578fbc3b23ff1a41815163eb053569fa2d1544", + "packages/db/dist/esm/indexed-db-wrapper.js.map": "0203fa669e4b0329df4575a721ca2a3a170180209aa1e4ef6083223f9339c6e8", + "packages/db/dist/esm/indexed-db.js.map": "c29f3c555810dfc2a4b0c75c9ea1f7ba76ebae042d9380cf1f8c69a5471a20c1", + "packages/db/dist/esm/indexes/auto-index.js.map": "511e7a724df721c7decc03223fc765a289f06274cdd379b7de89383a2673d2e6", + "packages/db/dist/esm/indexes/base-index.js.map": "fd5416f04da3991df1fdca5605cec332b7eda9e2517962bbfa437ad10028b286", + "packages/db/dist/esm/indexes/basic-index.js.map": "8a1d3483240e52f21e82f22424b4168b4e17d7f0127e74d6cd2fb1abcc491645", + "packages/db/dist/esm/indexes/btree-index.js.map": "24afa9523a33e8c7ae06c0543991f83b454c9ca67e667a125e363bd3665b4c87", + "packages/db/dist/esm/indexes/index-registry.js.map": "5c0bbfbf82671306e89a15c7ef3bbb8a4813b268b4f317d75be63f88eb41a75e", + "packages/db/dist/esm/indexes/reverse-index.js.map": "633cce9cc9774fbe8854d1d9a338e26b3158107d1edbd0f2161fe952eb4700fc", + "packages/db/dist/esm/live-query-adapter.js.map": "26c5f830609eff6e54e4520eb0ec269a3aad375c2fc857a3ae77140e0179e7f2", + "packages/db/dist/esm/live-query-observer.js.map": "5475b3532cd994257fd71961200597b079078521b81440265ce455fbba901986", + "packages/db/dist/esm/live-query-options.js.map": "ad90acb33887c695fdfa862cd8d5d49e68cd4db37198db903b56c6382e83a7b3", + "packages/db/dist/esm/live-query-primitives.js.map": "41afad71afdcd23aada590c459cd4533d2737a7c28c21ebb5eb23e7fcf7e6386", + "packages/db/dist/esm/live-query-window-binding.js.map": "6c1bd8174569741a154f5bba07fd3a09f18391e599e42d4b9b697089aa60154e", + "packages/db/dist/esm/live-query-window-controller.js.map": "298367ea2b702476de2c969a4226821c6ccfa033d3b90947b330c21060d146ce", + "packages/db/dist/esm/local-only.js.map": "c8eace5d10617bb0c5e178db897b6b5905a51b3b1e6c10e3b1018b14aa08c373", + "packages/db/dist/esm/local-storage.js.map": "95e266c329925a1a2ce6c984f4509f6366d40fb3ef02f50998532eca55538611", + "packages/db/dist/esm/optimistic-action.js.map": "1ff3eaf6f6ae7e2a08f1988eac3f9f80b307cfeb61b7031579f19cee774d2207", + "packages/db/dist/esm/paced-mutations.js.map": "209978b76decd4e48ccd0eca467a8fe1878a0b24145f4903df2a30aa66fea200", + "packages/db/dist/esm/persisted-readiness.js.map": "b36b855729c7fe5e9cf49a0eaebaf66f48ec69e87acf762e21079907062d7c78", + "packages/db/dist/esm/proxy.js.map": "7e93b8d55ec84bfa6435b535abf642a4ea817750ad0af18bfb4b0841af5052ab", + "packages/db/dist/esm/query/builder/clone-query.js.map": "a458b5f869bccb52d83f5ccc2a022e617198b07d71161599925d2b179b22ce8a", + "packages/db/dist/esm/query/builder/functions.js.map": "7209aeb6b70bc74fc1631dee124bdba42525c9de6c8e63257a9cb75c67225703", + "packages/db/dist/esm/query/builder/index.js.map": "57f18c1d77caee866a05de3343ab14417dce54b5b3d84e484a90c64f5b305212", + "packages/db/dist/esm/query/builder/query-ir.js.map": "404592f23d86e39fed95862bcd6c3a40ea9899bf64ce2b3e454b7a246d799096", + "packages/db/dist/esm/query/builder/ref-proxy-identity.js.map": "b0a54507e2de7678cc902067c315216eddde48cb9de7bee7ecebe9ca89c88e22", + "packages/db/dist/esm/query/builder/ref-proxy.js.map": "c5c6ae0637ae9be5ff1a2e3cf54d8788f57baaa8c1ff6629c962444dfe7851c2", + "packages/db/dist/esm/query/builder/wrapper-identity.js.map": "b7b11e683600fd94d16e869cb38dfa250ff56eb7b34b4e95d20ac35b497004b2", + "packages/db/dist/esm/query/compiler/evaluators.js.map": "d68150a8ca3406a2960168688fed45f5e5fe07573802691e4bb400499977e6b1", + "packages/db/dist/esm/query/compiler/expressions.js.map": "f41f8b46979dce48936373f86cc990357a82699f51585aa7ff34fb284430a704", + "packages/db/dist/esm/query/compiler/group-by.js.map": "c677452c235bbb7b08ed8c380953304c2fe462c5ac5a9de7a22098f00b65f11b", + "packages/db/dist/esm/query/compiler/index.js.map": "619130f951479e75e3a261b7cfff523dadd61d491ff5f4b66d0919a4b0509f1f", + "packages/db/dist/esm/query/compiler/joins.js.map": "c2c321244483a65edb382c6f2c55cd7f395bd383671f1d6b63e07b9b8df65104", + "packages/db/dist/esm/query/compiler/lazy-targets.js.map": "a95673b8a853f10dbd239bf3612f4f644e582bc5dc0a7f12873898a7ce33d7b6", + "packages/db/dist/esm/query/compiler/order-by.js.map": "898ad1b7bd1bdbd1c2c0a8edbb78e7ce14811bb4a9fed3e148befc1127a9509d", + "packages/db/dist/esm/query/compiler/parent-routes.js.map": "e18ec4a4c12e4c4f2e176e07c47eb3d58e36cc82eb5fa6327c92106552c1d523", + "packages/db/dist/esm/query/compiler/query-equivalence.js.map": "0e569092d8666ed94fb82c83ec29c5e9663f5d1ae1afadb45856fc4278b5b19a", + "packages/db/dist/esm/query/compiler/route-metadata.js.map": "3bf70196b5be0b9dcadfaee25e7c4764874d76a50ae88c15486f64fa6aef65a9", + "packages/db/dist/esm/query/compiler/select.js.map": "b400ce63f5ce3a505045241de9b5d2b9cb1dccb3e2fbf04475c7d1204717b779", + "packages/db/dist/esm/query/effect.js.map": "0d8f6b6535196a2bc2038830616750b99efbb47505ffab786c7a7c018c296835", + "packages/db/dist/esm/query/equality-conjunct.js.map": "1bc30e55bad05f535c5dde6ff2e427ed80dc73139563652e9169ff78ed6e52cc", + "packages/db/dist/esm/query/equality-value-identity.js.map": "53e543a540f24c9bf28b549388e7b47e4d0dc2fe0a32d2b4c2fd42496f016d09", + "packages/db/dist/esm/query/expression-helpers.js.map": "552f3f643aaf98d1fa50328892bbff6c3cf166a788f1a9bed22450ea835a3e3b", + "packages/db/dist/esm/query/ir-stable-identity.js.map": "54629136d3760ce211cf87b7795dbe629795a59789d26b6b4b4c33f00c50da6e", + "packages/db/dist/esm/query/ir.js.map": "ccb4d95ae5caa7bfc320e2bc66ef54f685dbe867301c463e4793c80397c581a3", + "packages/db/dist/esm/query/join-conditions.js.map": "39d95b4fd518dce35318bbdf38cf3b9f105a4e3b5db15d4ff4048ce261a8caeb", + "packages/db/dist/esm/query/live/bucket-facade-adapter.js.map": "5346a9f6853231e91b63e7303be2ff234560ec0653c748f257fd103ea0b5fd41", + "packages/db/dist/esm/query/live/collection-config-builder.js.map": "7fc086bb59ab0dd80007cc16b7164a7a4c4d1306fc29ba2357e8ac2df67f2b09", + "packages/db/dist/esm/query/live/collection-registry.js.map": "4f4be3b283e3daa91838fa9ce44dbc11d29b599328d7db419319ed6fd7d9c8f4", + "packages/db/dist/esm/query/live/collection-subscriber.js.map": "1b1bec3ab4af5b8f568be4f298d5984ca74170929574b41ac7490ddf47b0eb32", + "packages/db/dist/esm/query/live/graph-scheduler.js.map": "f9d01ec493d292f27a6136816a61082939d7ff9b2915e73b9f04a58f573d48ee", + "packages/db/dist/esm/query/live/internal.js.map": "8f3093c6b396f3b3b0ebdaf9d330f23f71cc6ff9ad6813c598b590c497a56382", + "packages/db/dist/esm/query/live/materialized-pipeline.js.map": "9924a8437663a9ff914fc8e733903449f322b9fb9786a873b72f63e10aefd5b0", + "packages/db/dist/esm/query/live/ordered-source-loader.js.map": "eaa8b13f6b4d995d35579816e23e53041c925dc1f027271ce6bb99e89b3f67fe", + "packages/db/dist/esm/query/live/subset-demand-controller.js.map": "3c0ef841c2f6ce3f6c8c4e1f5ef6fe2b885b52140977f198479440b62dadb065", + "packages/db/dist/esm/query/live/utils.js.map": "0d1b870b1c6b91edcbb7d553fb4bf12122eb1331a56ffeaafac7312581c1443c", + "packages/db/dist/esm/query/live-query-collection.js.map": "f1ce1849148cd27103b5f0dcf55cb97a738d26f17706fe7685b7262cfc86e5ff", + "packages/db/dist/esm/query/optimizer.js.map": "fe4f220fd03842d9d518b44df4ffac8a03d9dc4c40fe3b5a1456ca05cb7c5a47", + "packages/db/dist/esm/query/pooled-live-query.js.map": "82bf9a5f6131a287cc09c05cd35d0f12910189f86ce2009529ecdb91cebf6bb7", + "packages/db/dist/esm/query/query-once.js.map": "cea3bbe5c87666103f83c8d02a8feaaf9f718f318b821f76ff5c2a5d77dbfea9", + "packages/db/dist/esm/query/runtime-reference-identity.js.map": "d06fece2e78c5ea3ae21e74ce22fb24a7422cd57632bc28d5cbd7292dd049dab", + "packages/db/dist/esm/query/subset-dedupe.js.map": "d86e0085d0097a37f35375a94d52063829177fa6ec2aefce3cdce46c42133e82", + "packages/db/dist/esm/scheduler.js.map": "d0ca54ee8e793d30ba55632a0f59d8c141724698b4d1f82236c70c7ef36307b1", + "packages/db/dist/esm/strategies/commit-completion.js.map": "0ccb2df9d5ed0561721f8459a6f029a031f774250aaaaadb7f68a5556bd4729c", + "packages/db/dist/esm/strategies/debounceStrategy.js.map": "8dcdddd38e78f32c844279e92845359b7a95f4b91cb6add26474b680e32e5421", + "packages/db/dist/esm/strategies/queueStrategy.js.map": "e61023e3380ae9594484c9e673b078454955b85ef8d37fd57b9ecdef1bdf6da2", + "packages/db/dist/esm/strategies/serial-pacer.js.map": "4e86b6c2203e220ab7096e2e624463c92c2bf6c8cb1098eaebad2df56f5e4a69", + "packages/db/dist/esm/strategies/throttleStrategy.js.map": "8abc6c79e822c3f6de0496b6c42bd730680f8b48989016e3f4257e25ab01a25d", + "packages/db/dist/esm/sync-persistence.js.map": "a6e395d89daebfc769642fa8bd00a0b4f3c9bfb4090a200d97f6637363d89df2", + "packages/db/dist/esm/sync-receipt.js.map": "037f7d6167e90947a5468a36757d4bd32dfe0c937d8b5f1ab7a83bd28c23f756", + "packages/db/dist/esm/transaction-commit-work.js.map": "10ae9167e5a3a0b030a5a9cb34b347d4bb6f719e023a84db6a0a9da85f39accf", + "packages/db/dist/esm/transactions.js.map": "d071193784fd02d4365ba1db134d53c13932fdfbeceab2e300211335e6582873", + "packages/db/dist/esm/utils/array-utils.js.map": "543f01857219d2625c3dab6f7c8da41e45ffc29f34643681f8be3d42b61099b6", + "packages/db/dist/esm/utils/browser-polyfills.js.map": "259ca9a616f32a59ae240606903456aae505856a03b01d7f0c5133fe724316c5", + "packages/db/dist/esm/utils/btree.js.map": "3d96225772c3794f2e65962fbe29cad56738f45a382b7a4e40f1370dcf780830", + "packages/db/dist/esm/utils/callbacks.js.map": "61b12884fd3fb07d404d53734577a254af387dde4d178862901ed43bf154d713", + "packages/db/dist/esm/utils/comparison.js.map": "4c3232f26a3487b36546b34c0af682b0880282c63fa021da36b070ba0506ec19", + "packages/db/dist/esm/utils/cursor.js.map": "50e78e47699a68d3ac3828eb37760f3d8331e25ac2bd833b74796c2f121056f3", + "packages/db/dist/esm/utils/error.js.map": "302fbea3014e7d353c8e19456d5c7b930876a738e868945fd0eba88498886626", + "packages/db/dist/esm/utils/get-or-create.js.map": "049135923c3a435c8a6435c2330978b7272b1f954481a71ede0450453b3c7d1f", + "packages/db/dist/esm/utils/index-optimization.js.map": "36d3be1f0e57af2e910d9d61f01f4757fe68637e84e0f59a360718b197505d97", + "packages/db/dist/esm/utils/source-record.js.map": "e52ab96575ec8e4fb99084e788ceb8e2f06451369503a5d3792a32b27f7311fe", + "packages/db/dist/esm/utils/type-guards.js.map": "0b96a21f6d742d02da8e62350804eb0e494dc2c10d90f03f29750988c843befc", + "packages/db/dist/esm/utils/uuid.js.map": "439824094f7d0ab0ce52e065cc80e717bac86ba96717435f0befc616495b83aa", + "packages/db/dist/esm/utils.js.map": "a635c14883c73c14c69a03637c408429e951b99fc3055062915245a2359f9c04", + "packages/db/dist/esm/virtual-props.js.map": "73b6876085e95170f2a932adddcf3c333175b3ff80ba00340ebcc150afc6358a" + }, + "verified_runtime_source_sha256": { + "packages/db/src/SortedMap.ts": "dbbef7d2842d265cf5268f3ca8c512b0b79ea927135116d3fc4e08ea2799856a", + "packages/db/src/client.ts": "fdac0f151242b349c53e3a8bb0e9788cc432b35f306d2ae91d74bf832fc97f38", + "packages/db/src/collection/change-events.ts": "754f897ba843d004eaf5bfe3f047fc53aa24eb49a16ae1eae4073e32ec17ba49", + "packages/db/src/collection/changes.ts": "c6deb4b04891feec0f2c4368ffa9ea652d12593f4e7ddf4edbe392a706f314df", + "packages/db/src/collection/cleanup-queue.ts": "d6d24ba9216d9dfacf57623d14e61861b044a23cac1b8e475dbfe71795a0c84d", + "packages/db/src/collection/config-errors.ts": "ffe046ad75542c863e1781c192191479cdcb68e7dcfaf92c9ce180ee48522cc0", + "packages/db/src/collection/events.ts": "fd95280be3bdd9a1177d4d57b425f484b9daf06b4b257ed28d6b17e3ef1bd132", + "packages/db/src/collection/index.ts": "458e71b1478a6150ef9c1854fb7df483cb32466217cec050993f21befcb1dba0", + "packages/db/src/collection/indexes.ts": "1a0b6633e5f5f24c1b29fe8e544ee3c52df6e886d2a61c8fe88750f3f811520e", + "packages/db/src/collection/lifecycle.ts": "5bb9027353ff30b9c467c1c3c749e739aef741ce1762c770ccd0ac90502763b5", + "packages/db/src/collection/mutations.ts": "735bdd4872c97afbe6d7333d42402635bdb54c914e6b5cacf703444fbf933f31", + "packages/db/src/collection/state.ts": "3c6caf3829455db5e6c037bf56e06c7a3b0e6abebc35368da0dbf886b4729e76", + "packages/db/src/collection/subscription.ts": "c446932a8a1f8b2efd79d00846f1a110e65a6c6df75d2b24658053e6964b530b", + "packages/db/src/collection/sync.ts": "4ff2e29f8fb27af6e71a46f959ad764a0d9dbd390bfba66604ba1c1836676add", + "packages/db/src/collection/validate-config.ts": "10bc3f83c357cf8b835694c73df9adf413c6190f924256786f17e123a688489a", + "packages/db/src/collection-options-brand.ts": "93df3fc8d90531ca926c6e9ca48653dbda9259cfaaade9c23cbbf35131b2e1ed", + "packages/db/src/collection-options.ts": "0b0f9713312f9554f62c020dda3bae8291303dbeadb85adfee76515a28442feb", + "packages/db/src/deferred.ts": "9c99553d7ab92ac89d377cb3382a9dfbdf592635c87b86eac8aa1f18efc4da68", + "packages/db/src/error-message.ts": "9ab4a64436c3db3a61f3a84b5ccff11359ccdf5676c81b81dc3be9db9c1dff13", + "packages/db/src/errors.ts": "ee54c0fb19ef95630e833b62562a4a2310ebd79337746355b8f67676cce0e416", + "packages/db/src/event-emitter.ts": "1d8775f3c2dbc9c833f03d2b9bb0d55d1606419e43d661064cc95cd91eb187e8", + "packages/db/src/indexed-db-wrapper.ts": "216fcac76c01b1af8363cda8a93678f53cd2cd58c9df918cd3d56d2a1f25fd11", + "packages/db/src/indexed-db.ts": "4c9960e7020389ab784f3ea6ce3fa1684b36ad1f6aef28a100cc15517374832b", + "packages/db/src/indexes/auto-index.ts": "d226516a7514c40e83010ab211020adba17965af96bd6d3b96e88989c6f31b51", + "packages/db/src/indexes/base-index.ts": "63e35c6bf011ad361882509a521407acf29b9404ce4e810a8f4d54b50c0e6685", + "packages/db/src/indexes/basic-index.ts": "9a128909d6df2c6d5b9ef07295a55f9f587170721cb7c6a652dd9c43eb674633", + "packages/db/src/indexes/btree-index.ts": "b006f65cf1c2ab14004e7311f4ac4b89958e919fb8439cd9ca25090789e37186", + "packages/db/src/indexes/index-registry.ts": "d44e34c9e5d90db56dee7f91e402f2547d91647ae100cc4752bc13bcd49e2719", + "packages/db/src/indexes/reverse-index.ts": "ebf2d22eb0eec10acb4df154488862243ec20be117bc069fe307fc991853dd0b", + "packages/db/src/live-query-adapter.ts": "cc6e68e05afb876a46989357304a2e9315cac918d2d7623e4272c3a4ce3b470c", + "packages/db/src/live-query-observer.ts": "37f3bdd904af1ba51d6d8821d4711b4e29789e307363590972e52a50e77a4d0f", + "packages/db/src/live-query-options.ts": "8ba7980c64de78500e56b3c822e19e8e451d77ee50352beaaf89644f47c3f411", + "packages/db/src/live-query-primitives.ts": "16fa17f9cefbb2dad98fda23651d792f8c8cd5bb4b812bd1197de7ae8f1f99c1", + "packages/db/src/live-query-window-binding.ts": "7564bac73ec3aab26dbc4c4ed44bc972f108fc6241c158eb63c8b7ae22b67747", + "packages/db/src/live-query-window-controller.ts": "ea439e6b6f0ce1bdd03f259a39d6d912024845e8343d013b67a7b2251740c8fc", + "packages/db/src/local-only.ts": "06d516a29e6f72b87f08c83f1fad2454dbc1cd3a3d296562f74fa5eb5aaec408", + "packages/db/src/local-storage.ts": "62331d402b5a7b958617e45ddb9de90a358f2fb1645ac589c5d412deb079cb5f", + "packages/db/src/optimistic-action.ts": "3b3d98ec0561927c301c6979256bbcd98faf0ee41b2b6690abe1c189eeaf4fec", + "packages/db/src/paced-mutations.ts": "ea9239b269c2622d23cb2fee6df2895534d64b43fb245f8160e054587b9b5458", + "packages/db/src/persisted-readiness.ts": "3fdff536570ed25347928bfac3043482d87a1506ea8bfd4d2c4e7f4c532fb598", + "packages/db/src/proxy.ts": "97fd4841cd8532a8bd2f08197d0d755cf348a50e5df0b2f4705079895f2a42c1", + "packages/db/src/query/builder/clone-query.ts": "7265db09e09e441d598d5b5774759f4ece6dc1cf5d5723d49d1c342a36e3798d", + "packages/db/src/query/builder/functions.ts": "71ff875be3b94602be517fd03d2ffd0fc1866a189a4849403623d9865e35227e", + "packages/db/src/query/builder/index.ts": "c658662a2ff50d85a7a1d662b728873e16a568d58e38248bbc57d03f884b463e", + "packages/db/src/query/builder/query-ir.ts": "6aa35045e8a83c63a68e3af2de951879aaa2b91c132606db6d0bfd32247af108", + "packages/db/src/query/builder/ref-proxy-identity.ts": "909d40e4425647ae58850decd6891fa7f6aa4690cd76e82c3ced13b70dc93b10", + "packages/db/src/query/builder/ref-proxy.ts": "da99a9d725676d7c29b1bc2c10913465389c6eb442ca651d06ab658a2561d0c1", + "packages/db/src/query/builder/wrapper-identity.ts": "3053d30505c57f0eb16cc573e196291e617aff7479151508bbaa397fa3757f57", + "packages/db/src/query/compiler/evaluators.ts": "499cb252c6e2c166cf626bf88c6c08ced102020558bdccab46a6d12acd52ec92", + "packages/db/src/query/compiler/expressions.ts": "70925f2d29f57aec189d0e7f485d0531534e3482f0696a5e9a1141d8cf36c42d", + "packages/db/src/query/compiler/group-by.ts": "f30cb4ebd6940dd4a3888fde633e16f952225e4f9d35be4e450d6ad912a22bc7", + "packages/db/src/query/compiler/index.ts": "0d53aa7d27fba0e0f472af55404adbc437e69a537aa2f529710e81d08b16944e", + "packages/db/src/query/compiler/joins.ts": "8f0aacfc3e09bbc2672cc21af75ac8b27e1f957a47ee8e36ce3b451137beff08", + "packages/db/src/query/compiler/lazy-targets.ts": "6949ebfac1f0d76b64dc9a3c1cfee87490702a21de235c320fdd7aaa02b77587", + "packages/db/src/query/compiler/order-by.ts": "271b3195a4d24cc0faf9c19261a1e18cd91eea8794ab9e611257929c4e3a880b", + "packages/db/src/query/compiler/parent-routes.ts": "d59e9d64534c53bb58349f07c121158adc9b37cac7a0b5150493e6368442c18b", + "packages/db/src/query/compiler/query-equivalence.ts": "7171fa0f1aa960297ba1269193ed3527967d78ebf1c19ef03544604280aa4480", + "packages/db/src/query/compiler/route-metadata.ts": "53283291ce25656339492bff678b8742a7fdc7b60c49d3b7f7015569e5c06788", + "packages/db/src/query/compiler/select.ts": "a4b60959e37ea674bfac8f3cc4076c35facb9378891f72603c309e92c2882760", + "packages/db/src/query/effect.ts": "013d653074e139caf2e2089574c3a985c7f408c9434d2d70d7c3b2dd9cc82258", + "packages/db/src/query/equality-conjunct.ts": "02041d880376b46814ab072747a9b0493e24cd4bd72df8389726780e535cc584", + "packages/db/src/query/equality-value-identity.ts": "a7b65759c20ebec4d83c47ec744bbd382da9a86ffa4c9e59028827127081d016", + "packages/db/src/query/expression-helpers.ts": "15d6635f8b28dafee0ca0afe2e8598545f09995a5b992798916d789167383019", + "packages/db/src/query/ir-stable-identity.ts": "2369d6ad584c2546f4fdf2051055a3bfa9bedb0c53cd8ff3d86b2b2a86cfd80e", + "packages/db/src/query/ir.ts": "9b319bd3728640d2ab941b167ea16b09d721ccff112647f597c9761dd43b5ada", + "packages/db/src/query/join-conditions.ts": "c2659d14f7a15203343de19b66cb83195c7ab5c26f953b52bbf6f40bbe012802", + "packages/db/src/query/live/bucket-facade-adapter.ts": "cbd6ea77a9d103fea6253e20656e31ff41ac353d134cb0c49710c56ffdd4b3c3", + "packages/db/src/query/live/collection-config-builder.ts": "aa55faec9bc8db4597357989d9466e4ab030e743ee6a197691807b775f0f8440", + "packages/db/src/query/live/collection-registry.ts": "c24da0f80292e4fa22370711cdf66e52ec44989f8fda50430025aa5b25510e65", + "packages/db/src/query/live/collection-subscriber.ts": "74e97fe498b44446d5ee21912175afab6a3e58d2787e04e890a26771d5c8e1a6", + "packages/db/src/query/live/graph-scheduler.ts": "bdd37b3814814a2bf32fa55422a2fb039107d04cbf4fbd79df67056c5bc4654b", + "packages/db/src/query/live/internal.ts": "3aa788cfb3647e2758ff9befe18e4503bc68797fd97d55dc5c3905db9a7145fc", + "packages/db/src/query/live/materialized-pipeline.ts": "9e0ad27c197c9571e242c7a7ccb286739ecdd748b25f00f4f4f8aae4f0894407", + "packages/db/src/query/live/ordered-source-loader.ts": "95ea59a251cd540406d3dd2dac1ba85f41d1f6422c4ffd44f973d9ea4ca01e18", + "packages/db/src/query/live/subset-demand-controller.ts": "28e49884847544356975afd781bb2d485844c2f43f9f33097aef28d06ba2a1d0", + "packages/db/src/query/live/utils.ts": "00d2a0462ea2a997f50d104f641e9837cdf1341fc763116a0df773f6888f1646", + "packages/db/src/query/live-query-collection.ts": "d9fa71c30a1dbb69d58e2e11c2ba4acbeaa727f8d1ee70ce3d225be08ce7224f", + "packages/db/src/query/optimizer.ts": "384be668674194cb69a3b6165ffc97bb3b7edf066e107c444f36f6bbfa43571c", + "packages/db/src/query/pooled-live-query.ts": "d2b9d1c77cb7b2f9fa0c0ed6c546e0738390d20207ebde466636d0c95f459554", + "packages/db/src/query/query-once.ts": "1399a24809ce83173f0d952ab35620bc4430874cb96ab14229495fad3ef57478", + "packages/db/src/query/runtime-reference-identity.ts": "c1c695e80771ab545e58254381333bd24469813d69f448106df2dc5a5de1db48", + "packages/db/src/query/subset-dedupe.ts": "c535bb2a38db491d1c11024c88c71dc1664a59bae3c19f1f5bec35d37d5b99de", + "packages/db/src/scheduler.ts": "b21cbbdc088b3c1dc4840b7db857ee7f262e6958dc8bc300398ad8e93da4f01a", + "packages/db/src/strategies/commit-completion.ts": "242b52fc7a041f5f9d17172c356dc4e1ec06f2cbeaa85ba5995294356fba9b15", + "packages/db/src/strategies/debounceStrategy.ts": "8ab894803575f1cfab85939adbf67637342bf2e230e34d0db9bb642f205cdce5", + "packages/db/src/strategies/queueStrategy.ts": "c21794ea5707e75de082b87c80ee5a19fd8b1752fd7725adf7435b377a5e4196", + "packages/db/src/strategies/serial-pacer.ts": "63f4aa8f7aacd4bbe5cc3d56173fdb9231fedea68bb721bb2d420cf2303ec679", + "packages/db/src/strategies/throttleStrategy.ts": "028b5a000cc5a49a0176a42f0bb0c5cbb0a71849e543b523b080942947f40cb7", + "packages/db/src/sync-persistence.ts": "eb64401db52d9d2f269e5b6d92f85ebb5d4cfe6479dd1d4832918cd7b9dbad6b", + "packages/db/src/sync-receipt.ts": "5ab4074603d96c23decd4fae499ed55f3a6ee5077e379f370d9b76fd90565399", + "packages/db/src/transaction-commit-work.ts": "b3c341c01f7a8b1207683fb3388b66b1173481f43ea59160e1922778e64872fe", + "packages/db/src/transactions.ts": "60acaa21a7c158e2617bb382778bec1aa2b5fcaff7d527ab902e47b364d140d7", + "packages/db/src/utils/array-utils.ts": "a151c6b1779cf975e85e53a7b2328865889738df1f0d0fc3b8a8e466d15911f2", + "packages/db/src/utils/browser-polyfills.ts": "b3212d22af7254eb8c286d89393eb043324abd0b5a7ddbc8eaaa505e292b437b", + "packages/db/src/utils/btree.ts": "e2172d757bfdaf3e528fe94864876fac9de24a66302ed1930d108daa1f525b1e", + "packages/db/src/utils/callbacks.ts": "da4d6fc8d94bf93d07442eefbebd1ec0c183ef559fa7eebceb51b3652816708a", + "packages/db/src/utils/comparison.ts": "ed8c22f7315f0119025d47a0ea6432bab974c618665b167012c6c12da2287449", + "packages/db/src/utils/cursor.ts": "b728649d395a8e4e61cd6a63479be92ce2d4d4a08dc73beb549b92dafec5181f", + "packages/db/src/utils/error.ts": "133924e3809af4a69826c08774455f12f10aba675cbc92a4298525868bba4ece", + "packages/db/src/utils/get-or-create.ts": "eb56ba9e1a6eddecc8241062cb9a4ea0afca7437fbb50f0404de1276a611ee82", + "packages/db/src/utils/index-optimization.ts": "034310477e43f29bfe3493a56ee3b3524378ae3f941f4a452e6a15b9aec24425", + "packages/db/src/utils/source-record.ts": "48023571bd2e850170b78a34a3a9d14940d97742f510f96cad4baaf4a75694e3", + "packages/db/src/utils/type-guards.ts": "b7a63accd5abd4dd5c93bb451650fc962a2fdcc3981e70f0b7b373ebeb5f07db", + "packages/db/src/utils/uuid.ts": "0365c5c02b46eabac53562dac87b342656e2774b36385a094c16b3a9db3a5ca1", + "packages/db/src/utils.ts": "67e7143cfb324dc8f97c310679730682ecfb8088c2dc0d9e0efde6bc57ff9c76", + "packages/db/src/virtual-props.ts": "6add3fb9226251bf4dddadc7c5227469d4183a4df7799047f2fdc6b51ef4d9c0" + }, + "unchanged_this_round_sha256": { + "packages/convex-db-collection/src/paginated-options.ts": "de0e92849d363f2545823d96829964084b1a115b890f0f521f9bdc0958ea2b76", + "packages/convex-db-collection/src/pagination.ts": "0308668d07db701d1f7f891d001a4aca2798973ea29bf1d4b00931eaa0e05f1d", + "packages/convex-db-collection/src/pagination-demand.ts": "d2be74c1f3610c153c2b7c3c9411aa6ea216db7e76c9b0a7f32d63ef2bde57ce", + "packages/convex-db-collection/src/offline.ts": "55e70bdbbc310226a9f0175d0c0912218413ba67433b5b47ccc78d6e7dadd5c3", + "packages/convex-db-collection/src/index.ts": "4247e2bbd168a80791861f4553c10d0d878504a7cdc6a65d4bc648e15b1e51ba", + "packages/convex-db-collection/src/subscription.ts": "a0c312caf2af3667c18e96c565ceb159c20d65550426cdd44aca69a43a3543e5", + "packages/convex-db-collection/src/options.ts": "ea2c2efc4d574080c5e03da8e121b79406640c667a1e260ead9eff22f6253d33", + "packages/db/src/collection/subscription.ts": "c446932a8a1f8b2efd79d00846f1a110e65a6c6df75d2b24658053e6964b530b", + "packages/db/src/types.ts": "2b4c5926922db049e7ccd58c3e3fc22503f6d8a9400f3fb01900e82da85c7a1e" + }, + "production_files_changed_this_round": [], + "scientific_models_added_this_round": [ + "packages/convex-db-collection/tests/lazy-paging-write-model-oracle.ts" + ], + "existing_scientific_models_unchanged_this_round": [ + "packages/convex-db-collection/tests/lazy-paging-model-oracle.ts", + "packages/convex-db-collection/tests/lazy-paging-receipt-model.ts", + "packages/convex-db-collection/tests/lazy-paging-version-model-oracle.ts" + ], + "prior_frozen_artifacts_verified": { + "convex-lazy-boundary-strengthening-evidence": 507 + }, + "review": "Self-review only; no new delegated review or TLA+ execution" +} diff --git a/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-app-abort.md b/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-app-abort.md new file mode 100644 index 0000000000..258eebe9a7 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-app-abort.md @@ -0,0 +1,176 @@ +# Convex: application writes, load outcome equivalence and obsolete replay work + +Starting head: `8c24922026fb4a714cb54b992ac85faa2e876742`. +The changes remain local. This record supplies the current source bindings for +the files in its [manifest](2026-10-27-convex-lazy-app-abort-source.json). +Earlier versioned records and frozen evidence remain unchanged. No new TLA+ +execution or independent delegated review is claimed; the final review is a +self-review under the oracle-review skill. + +## Law and authority + +The [#2269 maintainer decision](https://github.com/TanStack/db/issues/2269#issuecomment-6448490334) +motivates these three additions. Collection mutation handlers in +`packages/db/src/types.ts`, `optimistic-history-oracle.ts`, LoadSubsetFn, +SyncAppliedReceipt and live-query architecture laws 7, 9 and 14 supply this +checkout's receiving contracts. The task-local `laws-before-edits.md` records +the rival predictions before implementation and before the composition repair. + +1. A confirmed application operation reaches the backend and the source key is + correct when its application promise settles. A rejected atomic backend + operation changes neither source truth nor the final promised window. + Independently authored remote operations remain visible on both sides. +2. A synchronous throw and a rejected load promise give equivalent failed-load + public observations and subsequent recovery. Their acquisition leases have + different lifetimes; raw unload/status-event schedules need not agree. +3. Without accepted writes, native cancellation cannot wait for an absent + provider response. Late transport success/error cannot corrupt a later + successful replay or resurrect rows it excludes. Accepted writes retain + their existing application/cleanup obligations. + +Version reconciliation is explicit. #2168 remained open at +`2bc79fc7c416ac0e2d691c0b622f1364e4eb5c12`; its types and this checkout still +describe waiting for uncancelable baselines. The later maintainer comment ends +core request bookkeeping on abort. This change exercises cancellable native +preacceptance work, which satisfies both accounts. It does not relax the older +receipt/replay models or claim the prospective immediate-core-publication law +for an arbitrary adapter that ignores abort. That remains #2168 integration. + +## Scientific model and receiving boundaries + +`LazyWriteModel` holds authored rows and one optimistic operation. The ledger +predicts backend truth independently of every backend read. Its publication +relation combines a monotone source revision, a monotone requested window and +the current optimistic operation. It keeps all compatible explanations when an +operation hides source changes. Acknowledging backend work and dropping a local +optimistic operation are distinguishable: a rejected delete can restore an +older applied source while a newer remote edit still waits. Once the newer +source is public, the older source cannot return. Pages, caches, queues and +commit partitions never enter this model. + +An initial flat list of optimistic/source windows rejected that legal restored +view under held SDK notifications. The old/new predictions and authority were +recorded before replacing that formulation. This was a model-scope gap, not a +production-defect RED. The existing source-only version model remains unchanged. +Comparator controls preserve lawful optimistic rollback and reject source +rollback, atomic mixing, lost confirmations and leaked atomic rejections. + +The real-backend receiver uses actual Collection insert/update/delete handlers, +both SDKs and an independently queried backend. Every history has all six +operation/outcome cells. Each handler is held before the server call and after +its response; independent visible and unread rows change during its optimistic +operation. The first insert overlaps larger demand and an actual held receipt. +Later, real SDK caches advance while their notifications are withheld. The +source-key comparison runs at application settlement before notifications are +released. Every graph callback reaches the scientific publication relation; +quiet checkpoints additionally require the exact latest window and backend. +The server rejection throws after attempted edits, so remote rollback is real. + +The outcome receiver uses the same generated replay history for throw/rejection +and for direct/live-query observation. Failed replacement and ordinary later +writes retain the old publication; retry succeeds with the original failure +identity preserved. Adapter-owned partial resources are released before a +throw; accepted acquisitions release on unload or cleanup. + +The abort receiver uses both controlled native SDK shapes. It proves a response +is still withheld, aborts before any native acceptance, observes rejection, +then delivers with no demand and checks that no rows/receipts were accepted. +A fresh graph then receives two same-run core truncate replays with disjoint +source keys. Retired callbacks deliver both success and error after each replay. +Every graph callback and every retained sync error is checked. The test driver +supplies truncate through real controls; production Convex does not initiate it. +Core may lawfully request a full-source repair, so this profile makes no bounded +work claim. An early fixture permission was too narrow and was corrected; its +rejection was a harness error, not product evidence. + +## Grammar, reach and validation + +The app grammar varies 12–16 initial rows, page sizes 2–3, growth from width two +to 5–6, short text and all permutations of the five-cell suffix after confirmed +insert. Each normal fixed/random campaign has four histories; both SDKs receive +every history: sixteen backend episodes, 96 application operations. Every one +of the fourteen declared reach states reaches four histories per campaign. +The multiplier-three campaign reaches twelve histories per state per campaign: +48 SDK/backend episodes, 288 application operations. + +The outcome grammar varies 1–4 rows, short text, partial/no failed writes and +1–4 rejection turns. Its normal campaigns each have thirty histories and four +receivers per history: 240 direct/query/outcome episodes. The abort grammar +varies 12–18 rows, page sizes 2–3, windows 2–4, short text and both stale callback +and delivery orders; thirty histories per campaign, both SDKs: 120 episodes. +Every declared state reaches thirty histories in each normal campaign. Their +multiplier-three campaigns use ninety histories per campaign. Campaign helpers +enforce nonzero reach and retain both SDK failures. + +Reconstruction: minimal row/page/window forms reconstruct each captured shrink. +The fixed constructive sequences guarantee failure/retry, cancellation/replay +and all write outcomes; the generators vary the relevant neighboring values and +orders. Ablations lose independent distinctions: no ledger permits shared lost +writes, no rejection loses rollback, no notification hold loses exact settlement, +no growth loses held acquisition, no partial failed write loses unauthorized +publication, and no disjoint replay loses stale-key resurrection. Nearby invalid +histories include overlapping app operations, local settlement before backend +acknowledgment and publication without any authored source/optimistic explanation. + +Final commands/results are preserved in the frozen evidence directory. The +package suite, full backend regression suite, deeper new campaigns, TypeScript, +ESLint, oracle lint and diff checks are the validation boundaries. Individual +runner cases are not counted as independent oracle owners. + +## Calibration and direct replay + +All correct production sources passed before calibration; no product-defect +RED or production repair is claimed. The calibration script saves the current +bytes, applies one semantic mutant, shrinks/replays the failure, restores the +exact bytes in `finally`, verifies the hash, and reruns the direct replay GREEN. +Backend calibrations run serially against isolated local deployments. + +| Calibration | Boundary rejected | Seed/path | +| --- | --- | --- | +| Remove handler source-acceptance wait | Actual source key is stale at application settlement, both SDKs | `20261030` / `0:0:0` | +| Swallow a synchronous load throw as success | Failed replay fulfills instead of rejecting with its original error | `20261030` / `0:0:0:0` | +| Leave a retired SDK subscription active | Late error poisons current sync / retired React cache is read, both SDKs | `20261030` / `0:0:0:0` | +| Misdirect the handler's backend key | Actual backend differs from independently authored ledger, both SDKs | `20261030` / `0:0:0` | + +The first three are production-decision mutants. The fourth is a hostile driver +behavior at the actual write boundary. Each failure is a semantic assertion, +not a timeout/setup failure. Permanent model controls separately challenge the +publication and two-sided comparisons. Calibration JSON records commands, +source hashes, exit codes and checked replay paths. + +Run from `packages/convex-db-collection` using the installed Vitest binary or +`pnpm --config.verify-deps-before-run=warn exec vitest`. That command option +avoids an automatic reinstall of the existing local dependency workaround. +Normal selectors are `CONVEX_LAZY_APP_ORACLE_SEED/PATH`, +`CONVEX_LOAD_OUTCOME_ORACLE_SEED/PATH` and `CONVEX_ABORT_REPLAY_ORACLE_SEED/PATH`. +Backend commands additionally set +`CONVEX_TEST_BACKEND_VERSION=precompiled-2026-10-10-72ce13c`. + +## Self-review against the oracle guide + +| Requirement | Evidence / limit | +| --- | --- | +| ORC-001 | Authorities and version difference above; scoped executable prose. | +| ORC-002 | Authored ledger, independent projection and explanation relation; no production semantic imports. | +| ORC-003 | Laws, models, grammars, drivers and public cuts remain distinguishable beside executable code. | +| ORC-004 | Reconstruction, ablation, bounded ranges and nearby invalid histories above. | +| ORC-005 | Real Collection APIs, original error identity, callback observations, source at settlement and independent backend reads. SDK cache/notification premise is explicitly witnessed. | +| ORC-006 | Four semantic calibration failures and positive/negative model controls. | +| ORC-007 | Equal fixed/random budgets, three direct selectors, shrunk and checked replay; no commands arbitrary/replayPath is used. | +| ORC-008 | Source truth, optimistic operation, source/window explanations and settlement boundary each have a distinguishing next observation. | +| ORC-009 | Put combines insert/update; backend acknowledgment differs from optimistic settlement; no transport state in the model. | +| ORC-010 | Subscriber and handler failures are retained; every cleanup is attempted; both SDK failures survive aggregation. | +| ORC-011 | Throw/rejection metamorphism, independently queried backend versus ledger, and atomic-mixing controls challenge shared-fault hypotheses. | +| ORC-012 | This record, source manifest, calibration classifications and explicit limits. Self-review only. | +| ORC-013 | Zero accepted work versus existing receipt owners' accepted work; held receipt versus application; failed replay versus successful retry; optimistic rollback versus forbidden source rollback. | +| ORC-014 | App mutations/rollback/cache advancement are received through both real SDKs/backend. Adversarial stale callbacks stay a controlled-boundary claim. | +| ORC-015 | Every named state is declared and its fixed/random/deep counts are captured. | + +Limits: one app operation at a time, unique ascending finite ranks, bounded +strings, one source, finite responsive schedules. Rich values/schema variation, +reload/snapshot/resume and new health APIs retain their existing/deferred owners. +Initial-load outcome equivalence and arbitrary callback reentry remain with the +core lifecycle owners. No arbitrary nonquiescent temporal progress, real SDK +adversarial-callback emission or immediate core cancellation of uncooperative +work is established. Generalized field/backend normalization and the public +cross-adapter kit remain outside this change. diff --git a/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-backend-concurrency-source.json b/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-backend-concurrency-source.json new file mode 100644 index 0000000000..958bb2fc5b --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-backend-concurrency-source.json @@ -0,0 +1,386 @@ +{ + "starting_head": "8c24922026fb4a714cb54b992ac85faa2e876742", + "binding": "Current working-tree source SHA-256; self and unchanged local installation workaround excluded", + "files_sha256": { + ".changeset/convex-collection.md": "c7ad56ad983fe653f0054f0a82fcd47cad46c1d59087d62d79a21ffb077eb324", + "docs/collections/convex-collection.md": "3901d5d040f8e101369f1a16a160d4e5d0df391ec48408f99d995191ad013105", + "docs/contributing/oracle-coverage.md": "007218a899ed4ca0d2860533a7dcaa258f10c3ff74e8aa0beda95ba90cc8115e", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-backend-concurrency.md": "adea3ed30c6f5df32e5af261497afd84c5c5519ca1ef71130681e899e7eba450", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-interleavings.md": "f105899875a4129b7d816cacc1d6f094c68ad6048cf70350e8d105bd0acd89e6", + "packages/convex-db-collection/README.md": "53b984ef9156a52cea7cb92a81d34d6543508d2dbf048d8f2aeb361ae996d259", + "packages/convex-db-collection/e2e/backend/convex.json": "e7106add2ff21d61a430927bb9b1ba96b036b93fcecc31ff786149a6070ecd67", + "packages/convex-db-collection/e2e/backend/convex/schema.ts": "722077ed1d0b7a3b5172ac867d95c573fd38e7a45ada747016cdd2b4311a913c", + "packages/convex-db-collection/e2e/backend/convex/tasks.ts": "09066afb10452e8c19dd8cc7dc129d8a95aaecec6d5144f92654f5c40f73ffee", + "packages/convex-db-collection/e2e/backend/package.json": "8e2e33603aa201acb7c8d88a55130bc07275d8fb53693e0965539ae4bc4e3585", + "packages/convex-db-collection/e2e/lazy-concurrency-oracle.test.ts": "69a02d34d0efd0d1881cfbd321f22aac2e820597efd2cd32fc3fa1dec745b625", + "packages/convex-db-collection/e2e/lazy-paging-oracle.test.ts": "7a5c1390879e4e01757d020e22bfa24f5852a2cf5bb4e18faa4174ce47a02e48", + "packages/convex-db-collection/e2e/local-backend.ts": "e38be0c546a885f55b004f23bc894ab86d3c3b5880bfd9dde8208866a42aea5d", + "packages/convex-db-collection/e2e/network-gate.ts": "73851db1ae46e83b7ee1bdb88faf01aaec27178644a73e1f7835f94681806dd6", + "packages/convex-db-collection/e2e/setup.ts": "d448bab619ac7243957578b2565cdb647a5e71a70df3c65af3c3dc378b5df6bb", + "packages/convex-db-collection/package.json": "2f7708d14a108253b257b1771f85060642817c6896ba240c7a0d1b853428b799", + "packages/convex-db-collection/src/index.ts": "4247e2bbd168a80791861f4553c10d0d878504a7cdc6a65d4bc648e15b1e51ba", + "packages/convex-db-collection/src/offline.ts": "55e70bdbbc310226a9f0175d0c0912218413ba67433b5b47ccc78d6e7dadd5c3", + "packages/convex-db-collection/src/options.ts": "ea2c2efc4d574080c5e03da8e121b79406640c667a1e260ead9eff22f6253d33", + "packages/convex-db-collection/src/paginated-options.ts": "de0e92849d363f2545823d96829964084b1a115b890f0f521f9bdc0958ea2b76", + "packages/convex-db-collection/src/pagination-demand.ts": "d2be74c1f3610c153c2b7c3c9411aa6ea216db7e76c9b0a7f32d63ef2bde57ce", + "packages/convex-db-collection/src/pagination.ts": "0308668d07db701d1f7f891d001a4aca2798973ea29bf1d4b00931eaa0e05f1d", + "packages/convex-db-collection/src/subscription.ts": "a0c312caf2af3667c18e96c565ceb159c20d65550426cdd44aca69a43a3543e5", + "packages/convex-db-collection/tests/helpers.ts": "11f2c79c6872373e8f77efae62d2fabfead0e5af6673c86920ae2ef4abdb7274", + "packages/convex-db-collection/tests/lazy-paging-driver.ts": "9819960aa222777f9de3b4bbc3728d492fd9304865701dacbbe381591e88cea6", + "packages/convex-db-collection/tests/lazy-paging-failure-history-oracle.property.test.ts": "b96de9cf49b58cdbc07e364312692e677363c90a83a9c1d94b84cb2b6c4dbdbb", + "packages/convex-db-collection/tests/lazy-paging-history-oracle.property.test.ts": "5bc8808d6f71648f48e40acb05943132448f3e8f5bba31d97e261f1741674384", + "packages/convex-db-collection/tests/lazy-paging-interleaving-oracle.property.test.ts": "f3a0238452746233224bc0b21adf790b594cacef82d786a0ba419d19980bb773", + "packages/convex-db-collection/tests/lazy-paging-model-oracle.ts": "5c25e5765413eb6ad3d7e26bd10e7cd20c75a9c094cdca0e142b87da56d5dfc5", + "packages/convex-db-collection/tests/lazy-paging-native-receipt-oracle.property.test.ts": "5714e2c25507f1ea4aef5bb6eb9bed69bc9a9b079540667570227aa1014a0822", + "packages/convex-db-collection/tests/lazy-paging-oracle.property.test.ts": "3a7b5b6284076bc8dbdb59ac1d735f509c6bc19201ad355a0382e7a797a2d569", + "packages/convex-db-collection/tests/lazy-paging-receipt-model.ts": "ce9d4e5bcf6265536498462c218deb5d8c42a4f1c2f792dd98042ed7b077902d", + "packages/convex-db-collection/tests/lazy-paging-receipt-oracle.property.test.ts": "06b60b0a86926070dd57dd068f4abe572d48924e2cea29acb77f417bf8115d6a", + "packages/convex-db-collection/tests/lazy-paging-version-model-oracle.ts": "23f2b3624ecf0e086f2d9b04eb3f940ead4f9a6fe320dc7b100fc04ef82099e1", + "packages/convex-db-collection/tests/lazy-paging-version-oracle.property.test.ts": "8f4f115806a65dc71b3382dade7a89e326decc2b3c25505369783f30163a210e", + "packages/convex-db-collection/tests/sdk-cache-oracle.test.ts": "0e011a20ce991195742bdfa0cd570d831105604ad8e3e2cc4b8fab4b62daad58", + "packages/convex-db-collection/tests/types.test-d.ts": "3ff1a42d6caf95d14e5270b632340524a9f796b978ccd031835bd1f2d0dab6dd", + "packages/convex-db-collection/tsconfig.json": "623bc70a72fd718e7d209c90a08df12cced0e9045a3565058623e34e946a5710", + "packages/convex-db-collection/vitest.e2e.config.ts": "b43b7b797860455efa827774e9847975bd9850f81b9b284f43665c422e45b440", + "packages/convex-db-collection/vitest.lazy.e2e.config.ts": "cffb0cba6d38edd898e0f7b519d970ae28608351aacb66b5ef54f73e7c2c1905", + "packages/db/package.json": "bb6df01ff9589c8b75437683c935b8457f65e9216d37173a4730e1a169e0ab43", + "packages/db/src/collection/subscription.ts": "c446932a8a1f8b2efd79d00846f1a110e65a6c6df75d2b24658053e6964b530b", + "packages/db/src/query/live/ARCHITECTURE.md": "aa6a41b651809819d414d364421140bab02f9b992e9f5dbe73cf634b43c46478", + "packages/db/src/types.ts": "2b4c5926922db049e7ccd58c3e3fc22503f6d8a9400f3fb01900e82da85c7a1e", + "packages/db/tests/query/load-subset-joint-replay-oracle.property.test.ts": "975585ef1d1bc921afcab2e5384318c5f661dd0a2577e642351b2aafed0ee98d", + "packages/react-db/tests/convex-lazy-paging-oracle.test.tsx": "0a33955d45816d766100b89e45dd63ce32125864cf3e2f0ea46533a582b0333b" + }, + "runtime_js_sha256": { + "packages/db/dist/esm/SortedMap.js": "ecbe71a4b4387932ed64817362c31eeb58c151d1fda5ba6824a491e4d6eeaf84", + "packages/db/dist/esm/client.js": "4191f1ff58a2b3eae418d2e8dc56b0fe16a290ff233129535b0945f41a34f0da", + "packages/db/dist/esm/collection/change-events.js": "cb5740b95d7c403e0ed7fd8b2aa831a921492782fc66c47bf1c91647070de0ac", + "packages/db/dist/esm/collection/changes.js": "084445baf434ffbfce6ceba34108c32dea5a58b5db68d6c068af6a8b762fff34", + "packages/db/dist/esm/collection/cleanup-queue.js": "3378b66f70261b2ce7eb14fdd982114e838ba6ce6d61ec18b0de976605cb9ba2", + "packages/db/dist/esm/collection/config-errors.js": "d6beb8dddab089d6dfb2516b5850b4b2a709b2bb2a9843bda854ad47723c35d1", + "packages/db/dist/esm/collection/events.js": "0cb3eadd7a9ce2e454e0c4a1a1b0023d2fb96724ad5a4dc806684f9ed4e47d17", + "packages/db/dist/esm/collection/index.js": "01e254fc0d024e7e716b4b09a10e7656130663ae587ad72314873f3b87f5fc2f", + "packages/db/dist/esm/collection/indexes.js": "9d1dbc166c3a44cd9348a62d96c7724335ef38c6f466575c0bbc2419e5647130", + "packages/db/dist/esm/collection/lifecycle.js": "2a26fe829e0183e77a33777f3003e1ab8699a4a55ec6fc309336b35c581ea03b", + "packages/db/dist/esm/collection/mutations.js": "42ed12785749b4098b422a538155ade6d002eaef3ad9cae227c0218f44c95e42", + "packages/db/dist/esm/collection/state.js": "d03955c898fc1672a1a708723d6e4506064665f8a24c57aea6be956f2c4dd0b4", + "packages/db/dist/esm/collection/subscription.js": "ac04fdd5bb56894afa45ecd9bef37abd4f96418c7912cd329f40e397370d27aa", + "packages/db/dist/esm/collection/sync.js": "82be78b67610f59fe60bad6e9f9776884287d38d6bc5641d1478875a559c6961", + "packages/db/dist/esm/collection/validate-config.js": "16a2b254fde0bc0d37c376107a5f394b17dab89165d99ff56a1897af9c234138", + "packages/db/dist/esm/collection-options-brand.js": "ae4fa09d0ac1ff1a9be961efa924d7dc769aad56a7e67cd1552c08b48fee3880", + "packages/db/dist/esm/collection-options.js": "1c6d225b2e8fd061f7e7616d67de43288583660e99a8a596b86d3f8c58c3a0de", + "packages/db/dist/esm/deferred.js": "d8ce1c72bd8f76a4e49aa17042e46a5432284730d1b2ceb4b5afbf927f881e2b", + "packages/db/dist/esm/error-message.js": "176a340e97f58cf9fbb21edc92cf43c6703ad2110310965b3da0f983e1cfab9f", + "packages/db/dist/esm/errors.js": "8c22d726d3878b029e2d9bf89c158ae2c25f915fdc9833711cf63fab3e48cd26", + "packages/db/dist/esm/event-emitter.js": "07add965808d5bd93b29d97e0e6d7db1c060856a1ea20cb7601a81785f2c67dd", + "packages/db/dist/esm/index.js": "fa5714b1da1264029dc75dd2f9e18edf145f128232feca5690392ff94dac6b8f", + "packages/db/dist/esm/indexed-db-wrapper.js": "5ca4ed531df467c317a8b302f218664a13ab579cc472eb4b9dda3b70854ec667", + "packages/db/dist/esm/indexed-db.js": "f7a7b78e057570562aa7eec0279e37a58ac7c4cfc05e820401f89728654dfbf7", + "packages/db/dist/esm/indexes/auto-index.js": "663f98c52c018571e87f398cfd4efe51bd27f48e2a4ac76b8e31327a725da8de", + "packages/db/dist/esm/indexes/base-index.js": "59652757da474e7119d759f784e954b56d9daecf586e385b2bda4ecd9219f465", + "packages/db/dist/esm/indexes/basic-index.js": "1ac365be033cb255d3b4005de57b7466b9ed4cd21daa08a77d8dfcfef0584626", + "packages/db/dist/esm/indexes/btree-index.js": "9ae7bf699fd9d44f9cc7dab823df85638efb2f43114eb33aaa7120efc367269b", + "packages/db/dist/esm/indexes/index-registry.js": "cd9448a61e290d32e23090cc57c8282aadb2d1736611a33509dbbe1f328d080b", + "packages/db/dist/esm/indexes/reverse-index.js": "94f0751672696624240f6e214a9b16517c9145269968e07ec7b2b8f439e3f25b", + "packages/db/dist/esm/live-query-adapter.js": "e787323960ffd3e848ad669ccfea1df3d21d80e084e75b6048740dfe23756e3c", + "packages/db/dist/esm/live-query-observer.js": "53eb064feaded0b54fb64e5a4741b2ed5cf94de00638a2bac0a95e573aab75d8", + "packages/db/dist/esm/live-query-options.js": "51ddfc4eb7ae745a75db197d67e23cd0b9b15f527b1fa706a759d90fa7920f2c", + "packages/db/dist/esm/live-query-primitives.js": "67f6abfb58ecba0abfa2e3c8094c98a86eadad39bd96afeb48912a77475bd76e", + "packages/db/dist/esm/live-query-window-binding.js": "3f239006fa71213f924008cccdb29b9ad57d97beff2947a30f0e0130370bac02", + "packages/db/dist/esm/live-query-window-controller.js": "bdb129aa7772774fe9fddb598d911bd7ae06cb8d581c05c41f47152b478f6805", + "packages/db/dist/esm/local-only.js": "0a4a5d855e4e3db629d20898eca261fa2dc02c3cb2d0c6feec32a052796930b4", + "packages/db/dist/esm/local-storage.js": "5a01856bf1eb5f1cb91aeb6536f3d685e8129aac6e9ea0c6c5feb62159aeb8ef", + "packages/db/dist/esm/optimistic-action.js": "ca9f92ae29a9710cd18e3e71c46a2cc0f204334525aab42174fdb4f53ec67b89", + "packages/db/dist/esm/paced-mutations.js": "01135f1c21fd5deefa89c92ecf7598bbe42d98aa284e646ac21c470c0d346369", + "packages/db/dist/esm/persisted-readiness.js": "160a5bdb6868f603e1bf1358ccabadd72cbe70c05d87f807972873bb03f9c850", + "packages/db/dist/esm/proxy.js": "33a224882c8dc0db103d6d0b92e50bb71021030e608937e7a800411ee9eb3fb6", + "packages/db/dist/esm/query/builder/clone-query.js": "9fdd9595f744fd286b55de26cd552bd630a358e4114dd605ad4d75a474ba70ec", + "packages/db/dist/esm/query/builder/functions.js": "d43c221d002d703965be1587f725255ceb58dd11258670b2882c3451340efe55", + "packages/db/dist/esm/query/builder/index.js": "c42986783a038350a694a9d8352f559017f62fc4b51197ca99f6ea1ce386a8fc", + "packages/db/dist/esm/query/builder/query-ir.js": "8d535e9be5fea36bbea302665f1d30a1b4a029fe4fdc95e6f674b6b0a3c5ad34", + "packages/db/dist/esm/query/builder/ref-proxy-identity.js": "fc756419ecb6b451640f729a44aa2e9cef4385d20ef1c68c049d36fde27d2b10", + "packages/db/dist/esm/query/builder/ref-proxy.js": "b71f544f804438a3c4a7ccec380fa0f4614ec7c859a983de4520d19390707993", + "packages/db/dist/esm/query/builder/wrapper-identity.js": "00c10f26051967ff8f284e550ab207552ac2632cbc08a7623617dad1c593bb06", + "packages/db/dist/esm/query/compiler/evaluators.js": "0ccf7503f360331210ffabf4a1003c96f904f831400ca2687f553f509a52f21b", + "packages/db/dist/esm/query/compiler/expressions.js": "eeb0c6dfca9871b4f7a1dd893c265e51e1b23d20400ac28579875446ac37ca87", + "packages/db/dist/esm/query/compiler/group-by.js": "ca935c6886fc9cb1574fc71e22efe688beceb36bd524a25a6aa29ae0bcf44dda", + "packages/db/dist/esm/query/compiler/index.js": "19667be77fa84da780beb102509c28d98abd365a330e8e7da4e9ac5a51154959", + "packages/db/dist/esm/query/compiler/joins.js": "af1bd496e41902c7e5b9759b4231d0487db2f67f8aa060b75fd326c7791c88b3", + "packages/db/dist/esm/query/compiler/lazy-targets.js": "04ed7aec7600a3ccf86ac28f06f8dd171770e56ca01a11b74d0b59fba6e083ea", + "packages/db/dist/esm/query/compiler/order-by.js": "e9844a5178d22908bf1ee6a723fe903d05110f6c333eefd8851a83165b39180d", + "packages/db/dist/esm/query/compiler/parent-routes.js": "bd0cf9a9781b321181ed3e2b6571d94dcbcd7bc784c53399b98051364c0fce80", + "packages/db/dist/esm/query/compiler/query-equivalence.js": "139c14fa078e5ba4c4a3d7490854c336870eea4c30f5960cd8f15980ceb00b57", + "packages/db/dist/esm/query/compiler/route-metadata.js": "6889f6b3dec56ef8fe5e6ee05e892dab1c22c0c0a13cbe09afa76774dae8a614", + "packages/db/dist/esm/query/compiler/select.js": "3f7d19d780395dd84c1b2b2c506e2b4f847a5a8a9442016bc5f72597f454af18", + "packages/db/dist/esm/query/effect.js": "59f37b190830c138368260801b09e7bc7a30e347fe36681b4796f6902c7e7c3f", + "packages/db/dist/esm/query/equality-conjunct.js": "ca0bee4e908fd4dbe52dc78feb172dca90492edca49cabe509c3cb4e4fd67fd7", + "packages/db/dist/esm/query/equality-value-identity.js": "5091c8c6fbb1e06127ef19db34258fd0d765b3c1b96c4a4f55b6fb553e37fcca", + "packages/db/dist/esm/query/expression-helpers.js": "5c957e7d0f23fb781aae8ead705404cf7bec3b1cd68db3fb46b356a1d25002ab", + "packages/db/dist/esm/query/ir-stable-identity.js": "a9cdba9efedc50caecaf7f8724f4146ab8d5f16d916f25707112a43cc36a0a8b", + "packages/db/dist/esm/query/ir.js": "9f2a67c11ecf08e1af74375eac7a603455e12ce56a75317376fd90b55193261a", + "packages/db/dist/esm/query/join-conditions.js": "0ede70428fc77b12d38c333b22767d629d50e88dca26af0c495c83c3308daa85", + "packages/db/dist/esm/query/live/bucket-facade-adapter.js": "daee317ea51844f450c893f7ce57e3383a0bf96c52e2bc4930d1aff951e5c7b3", + "packages/db/dist/esm/query/live/collection-config-builder.js": "93228d57a26e0e6dd5d51553c5fe10c9ed49f7803979ad21a76aa6805edec3b4", + "packages/db/dist/esm/query/live/collection-registry.js": "05e27e3d041d58aefc8820e523163b2e21ee53a3940680a0b468bb8e81e6d46d", + "packages/db/dist/esm/query/live/collection-subscriber.js": "11dc6621b866033008c2d6c9c2ba90ce36eb94d5f0f0ae3435ea55ecee736f3f", + "packages/db/dist/esm/query/live/graph-scheduler.js": "d3888f67b6e09ac29a3e45a120e3cbbceda49be7415eacaf0856cecf2cbc9e9a", + "packages/db/dist/esm/query/live/internal.js": "244a7f35268790408879ed8b1774fdd767b78829dc1a5ef6a0df4c2c372c0258", + "packages/db/dist/esm/query/live/materialized-pipeline.js": "6d98c2d4ee9412f5816c4151c14f73224b449738df183d0c82c37c08056c64c2", + "packages/db/dist/esm/query/live/ordered-source-loader.js": "63481aa7985a8e9de91f7e3499deb0e5d6f53d6abec6dd6614609ac1f1b5efda", + "packages/db/dist/esm/query/live/subset-demand-controller.js": "1163906179d8c68fbaf36507411c258ce664e8d4141f0a8c4b01f905f1cd1c27", + "packages/db/dist/esm/query/live/utils.js": "3df2bfd90ae95fda8c421e3131e0aaf90875e2cb1af514a09da42ec67e78f899", + "packages/db/dist/esm/query/live-query-collection.js": "f6024029a340a675df1e36ed180ab0d800ac0e697399a5b0ffdab392fdb87181", + "packages/db/dist/esm/query/optimizer.js": "2992bfaaf19a94828538613dcfffd71e4908952fd7c401d4fa01509c53776093", + "packages/db/dist/esm/query/pooled-live-query.js": "0c83e1139162d0314a0a6f1076911d4cf635877d6c9dbc89b8983d27cc90ac46", + "packages/db/dist/esm/query/query-once.js": "efa18058ba8d8abf2f201cf0a6418ab3f1c133d4d7464748f55c0ff8839150f8", + "packages/db/dist/esm/query/runtime-reference-identity.js": "3a5de8435888ede6b0edf799e5f0c192192aa1e0fad922450e9774ebceb4de18", + "packages/db/dist/esm/query/subset-dedupe.js": "06203c388ec4f1e525de8fe770775736222b62e273007e9a31bc37ba5fbab15a", + "packages/db/dist/esm/scheduler.js": "d9b4d34d6a4c656c0f38859426d39830ff62053ab47d22a14acc8b34bb6a8136", + "packages/db/dist/esm/strategies/commit-completion.js": "bd177de52002dceb9d28e02223da81b05c00ca5f72f5c06aa70699142904b263", + "packages/db/dist/esm/strategies/debounceStrategy.js": "88c73e9970201acbe24c3ea11632f2747f174cfb2840a53fdc4ba25224feffff", + "packages/db/dist/esm/strategies/queueStrategy.js": "abf15dc18ce9f481ef876478b0e2190188b960d76d7cedcf4f0c8b2a6818f9b8", + "packages/db/dist/esm/strategies/serial-pacer.js": "84c01c2f15ac57689b7f80a8ca0b152035cc2dedd8573e7e816b1a2bd0dfb08f", + "packages/db/dist/esm/strategies/throttleStrategy.js": "f646adf4f396ceddebca7580b398dc57edf1a22dfc2dd2e6c1eb607b412c0de0", + "packages/db/dist/esm/sync-persistence.js": "16292216a685c02ce1f3e1b2945a2409600e42d951c6acd19090dae99a38dacc", + "packages/db/dist/esm/sync-receipt.js": "c4365d1f36cff69c10194edef4903526fa85078cdf135172978075b5a3d56652", + "packages/db/dist/esm/transaction-commit-work.js": "c6838b1423ba8b0a1bb2ebf848fa37394416d52369d0d7ee5e8508d67f87066b", + "packages/db/dist/esm/transactions.js": "901116611280b5cbd71c3d9c49b6bad061174d90fdd0d2ab29f8cb66d5fb12d1", + "packages/db/dist/esm/utils/array-utils.js": "e8b8006009a956a5520822c9bb1ac8a9b13750613b0be937c2c2cb8d14c5d4c0", + "packages/db/dist/esm/utils/browser-polyfills.js": "620828c9fcf0df2181fed5d866a5ad20f1ac01ad5a296dcb04fd8be7012f6d75", + "packages/db/dist/esm/utils/btree.js": "2db68fc77ef20eb576502fd284668da1c9e04b18befc88dbf3d6842b722e75a6", + "packages/db/dist/esm/utils/callbacks.js": "0e5c79079dcbd00609aaeea1d769ae56fd881f4245740dfce408419773c599da", + "packages/db/dist/esm/utils/comparison.js": "19d37b5b97b84c1a19d331e277f47042e87ac01696d90a74fd3f76030d45e7ae", + "packages/db/dist/esm/utils/cursor.js": "1051977f9f8acbb6fb6884faf467c5c379cb54fdf0b02d22c9c1eb14cc52ca86", + "packages/db/dist/esm/utils/error.js": "a19d18998688b8a2ebd796bf668c694fe5f0c18fd4a4f8434f91589a7e436e40", + "packages/db/dist/esm/utils/get-or-create.js": "b1b71fa6f0e123e44f4908ea606ad6002515afbe204dd5841f72789f8fdaf3d3", + "packages/db/dist/esm/utils/index-optimization.js": "24ffd884fb0c24a49f614c5cc7fa9b37d9942bf59ccb12ccba07c7a0a328ea1c", + "packages/db/dist/esm/utils/source-record.js": "dcb726a12fb21ca74fafa5eb78ab7b83f1d18c2176fee2af11f430322ada6f48", + "packages/db/dist/esm/utils/type-guards.js": "a0327ad9ec010fa5f75a73b01b302c19433258db3ae23ce563a97eff8d981fa6", + "packages/db/dist/esm/utils/uuid.js": "fae77bf0bc610f2fe2e2b7b3ac289f48b572b223973780c6b895bb878c68e8f4", + "packages/db/dist/esm/utils.js": "ab6496511234ca87fd1c59c51c7b8f2f8b8601ea5d7ed15057f3068a88210f4f", + "packages/db/dist/esm/virtual-props.js": "76a0a0e70ee0f959dc7f4c2dac49bdcd24d8a7427b1cd59fb708d7bf47a7fde0" + }, + "runtime_source_map_inputs_verified": 106, + "production_files_changed_this_round": [], + "scientific_models_changed_this_round": [], + "production_files_unchanged": 7, + "prior_interleaving_artifacts_verified": 143, + "prior_formal_artifacts_verified": 250, + "prior_loss_audit_artifacts_verified": 9, + "local_install_files_unchanged": 2, + "runtime_source_map_sha256": { + "packages/db/dist/esm/SortedMap.js.map": "9fa31bea314653a026871313192f228eb75af82e8cec748e8041d1d7bc3d6978", + "packages/db/dist/esm/client.js.map": "33a90696c3b077207b8b1ccd57b1e9299f505a9d468466746066dfe3936c9a32", + "packages/db/dist/esm/collection/change-events.js.map": "c53651fdb6c5b229a2280d45af00a4ec969d5edd48d11608e96c8e813e109ceb", + "packages/db/dist/esm/collection/changes.js.map": "66ba53f09b63f0e668f7522297736919261a64518ec2c43b16eec770e413dbe1", + "packages/db/dist/esm/collection/cleanup-queue.js.map": "84777c1baefeb9e15bfad6f2144decb14ad0236895f8eb59d6721221aacf25e4", + "packages/db/dist/esm/collection/config-errors.js.map": "6bb878aebe57ff145f43dd2afb85c80041cff4b39546ae5e6eb3c7c1b7ec57ad", + "packages/db/dist/esm/collection/events.js.map": "4c035f44d2a25221e333af7799d8dbb06d8b69a12584fe6668f517c71ea87b4c", + "packages/db/dist/esm/collection/index.js.map": "2e50904cadd394695e45c5e82bbdaf1f22f29d6787e7ece5e225250aa721f438", + "packages/db/dist/esm/collection/indexes.js.map": "415a842ba3570ad1a6f341edc761c80b7c37a41c1dcc568121a854d95fcd91f5", + "packages/db/dist/esm/collection/lifecycle.js.map": "12486a4c6d0a7dcaca4489596e2e6da0deb1284c63538c3547773cfd1a1af3c4", + "packages/db/dist/esm/collection/mutations.js.map": "57b450f3fe34ff7c279e349338b525140fe287754763aa4e335cbae08f4374b7", + "packages/db/dist/esm/collection/state.js.map": "f11a3431edc53bcffebcd0bbd6c27af38c2a8c38e5b36e2d2b66b934031b835b", + "packages/db/dist/esm/collection/subscription.js.map": "73c1f1ca5d23cae10c45175164f5e2c59aa10138f13c86ed6ee775f1655765bd", + "packages/db/dist/esm/collection/sync.js.map": "cccc3eeceefdfebde3c092cc3944e23a2aae67eb0c57ff7f3a0694fec98d1d76", + "packages/db/dist/esm/collection/validate-config.js.map": "fd8cea162de4fe8ef094df6522aa8705b83c12fbfe8f9433ee556b3aa76d069e", + "packages/db/dist/esm/collection-options-brand.js.map": "006f1ec5a86b56a67c81e1450d129a8c18c2ff40aeedbac730ff344cac539248", + "packages/db/dist/esm/collection-options.js.map": "5e0b50e274db3185ed9b0c7192920a562c2d9669e57bdb4317ff27a0c7bc0bf8", + "packages/db/dist/esm/deferred.js.map": "c87f06ed0c6b0f229700d59008329205ff3fe7fd5466306982bd932ae273bfcd", + "packages/db/dist/esm/error-message.js.map": "1ddef42c2961d5d65ffd8bafd3a639431a539cf90da41cd5ee794710f3f2884c", + "packages/db/dist/esm/errors.js.map": "6d4e905f2f6c38ea74cef7cbb5fa6f46ccef861488bb57eb2096d3fe4e8fdd9d", + "packages/db/dist/esm/event-emitter.js.map": "b0b3a8588c21ee62b7d3f23d2dffe07ae67f3a451f5151b6f7dbc528c7d009f4", + "packages/db/dist/esm/index.js.map": "f8108774caf02d99844c09563d578fbc3b23ff1a41815163eb053569fa2d1544", + "packages/db/dist/esm/indexed-db-wrapper.js.map": "0203fa669e4b0329df4575a721ca2a3a170180209aa1e4ef6083223f9339c6e8", + "packages/db/dist/esm/indexed-db.js.map": "c29f3c555810dfc2a4b0c75c9ea1f7ba76ebae042d9380cf1f8c69a5471a20c1", + "packages/db/dist/esm/indexes/auto-index.js.map": "511e7a724df721c7decc03223fc765a289f06274cdd379b7de89383a2673d2e6", + "packages/db/dist/esm/indexes/base-index.js.map": "fd5416f04da3991df1fdca5605cec332b7eda9e2517962bbfa437ad10028b286", + "packages/db/dist/esm/indexes/basic-index.js.map": "8a1d3483240e52f21e82f22424b4168b4e17d7f0127e74d6cd2fb1abcc491645", + "packages/db/dist/esm/indexes/btree-index.js.map": "24afa9523a33e8c7ae06c0543991f83b454c9ca67e667a125e363bd3665b4c87", + "packages/db/dist/esm/indexes/index-registry.js.map": "5c0bbfbf82671306e89a15c7ef3bbb8a4813b268b4f317d75be63f88eb41a75e", + "packages/db/dist/esm/indexes/reverse-index.js.map": "633cce9cc9774fbe8854d1d9a338e26b3158107d1edbd0f2161fe952eb4700fc", + "packages/db/dist/esm/live-query-adapter.js.map": "26c5f830609eff6e54e4520eb0ec269a3aad375c2fc857a3ae77140e0179e7f2", + "packages/db/dist/esm/live-query-observer.js.map": "5475b3532cd994257fd71961200597b079078521b81440265ce455fbba901986", + "packages/db/dist/esm/live-query-options.js.map": "ad90acb33887c695fdfa862cd8d5d49e68cd4db37198db903b56c6382e83a7b3", + "packages/db/dist/esm/live-query-primitives.js.map": "41afad71afdcd23aada590c459cd4533d2737a7c28c21ebb5eb23e7fcf7e6386", + "packages/db/dist/esm/live-query-window-binding.js.map": "6c1bd8174569741a154f5bba07fd3a09f18391e599e42d4b9b697089aa60154e", + "packages/db/dist/esm/live-query-window-controller.js.map": "298367ea2b702476de2c969a4226821c6ccfa033d3b90947b330c21060d146ce", + "packages/db/dist/esm/local-only.js.map": "c8eace5d10617bb0c5e178db897b6b5905a51b3b1e6c10e3b1018b14aa08c373", + "packages/db/dist/esm/local-storage.js.map": "95e266c329925a1a2ce6c984f4509f6366d40fb3ef02f50998532eca55538611", + "packages/db/dist/esm/optimistic-action.js.map": "1ff3eaf6f6ae7e2a08f1988eac3f9f80b307cfeb61b7031579f19cee774d2207", + "packages/db/dist/esm/paced-mutations.js.map": "209978b76decd4e48ccd0eca467a8fe1878a0b24145f4903df2a30aa66fea200", + "packages/db/dist/esm/persisted-readiness.js.map": "b36b855729c7fe5e9cf49a0eaebaf66f48ec69e87acf762e21079907062d7c78", + "packages/db/dist/esm/proxy.js.map": "7e93b8d55ec84bfa6435b535abf642a4ea817750ad0af18bfb4b0841af5052ab", + "packages/db/dist/esm/query/builder/clone-query.js.map": "a458b5f869bccb52d83f5ccc2a022e617198b07d71161599925d2b179b22ce8a", + "packages/db/dist/esm/query/builder/functions.js.map": "7209aeb6b70bc74fc1631dee124bdba42525c9de6c8e63257a9cb75c67225703", + "packages/db/dist/esm/query/builder/index.js.map": "57f18c1d77caee866a05de3343ab14417dce54b5b3d84e484a90c64f5b305212", + "packages/db/dist/esm/query/builder/query-ir.js.map": "404592f23d86e39fed95862bcd6c3a40ea9899bf64ce2b3e454b7a246d799096", + "packages/db/dist/esm/query/builder/ref-proxy-identity.js.map": "b0a54507e2de7678cc902067c315216eddde48cb9de7bee7ecebe9ca89c88e22", + "packages/db/dist/esm/query/builder/ref-proxy.js.map": "c5c6ae0637ae9be5ff1a2e3cf54d8788f57baaa8c1ff6629c962444dfe7851c2", + "packages/db/dist/esm/query/builder/wrapper-identity.js.map": "b7b11e683600fd94d16e869cb38dfa250ff56eb7b34b4e95d20ac35b497004b2", + "packages/db/dist/esm/query/compiler/evaluators.js.map": "d68150a8ca3406a2960168688fed45f5e5fe07573802691e4bb400499977e6b1", + "packages/db/dist/esm/query/compiler/expressions.js.map": "f41f8b46979dce48936373f86cc990357a82699f51585aa7ff34fb284430a704", + "packages/db/dist/esm/query/compiler/group-by.js.map": "c677452c235bbb7b08ed8c380953304c2fe462c5ac5a9de7a22098f00b65f11b", + "packages/db/dist/esm/query/compiler/index.js.map": "619130f951479e75e3a261b7cfff523dadd61d491ff5f4b66d0919a4b0509f1f", + "packages/db/dist/esm/query/compiler/joins.js.map": "c2c321244483a65edb382c6f2c55cd7f395bd383671f1d6b63e07b9b8df65104", + "packages/db/dist/esm/query/compiler/lazy-targets.js.map": "a95673b8a853f10dbd239bf3612f4f644e582bc5dc0a7f12873898a7ce33d7b6", + "packages/db/dist/esm/query/compiler/order-by.js.map": "898ad1b7bd1bdbd1c2c0a8edbb78e7ce14811bb4a9fed3e148befc1127a9509d", + "packages/db/dist/esm/query/compiler/parent-routes.js.map": "e18ec4a4c12e4c4f2e176e07c47eb3d58e36cc82eb5fa6327c92106552c1d523", + "packages/db/dist/esm/query/compiler/query-equivalence.js.map": "0e569092d8666ed94fb82c83ec29c5e9663f5d1ae1afadb45856fc4278b5b19a", + "packages/db/dist/esm/query/compiler/route-metadata.js.map": "3bf70196b5be0b9dcadfaee25e7c4764874d76a50ae88c15486f64fa6aef65a9", + "packages/db/dist/esm/query/compiler/select.js.map": "b400ce63f5ce3a505045241de9b5d2b9cb1dccb3e2fbf04475c7d1204717b779", + "packages/db/dist/esm/query/effect.js.map": "0d8f6b6535196a2bc2038830616750b99efbb47505ffab786c7a7c018c296835", + "packages/db/dist/esm/query/equality-conjunct.js.map": "1bc30e55bad05f535c5dde6ff2e427ed80dc73139563652e9169ff78ed6e52cc", + "packages/db/dist/esm/query/equality-value-identity.js.map": "53e543a540f24c9bf28b549388e7b47e4d0dc2fe0a32d2b4c2fd42496f016d09", + "packages/db/dist/esm/query/expression-helpers.js.map": "552f3f643aaf98d1fa50328892bbff6c3cf166a788f1a9bed22450ea835a3e3b", + "packages/db/dist/esm/query/ir-stable-identity.js.map": "54629136d3760ce211cf87b7795dbe629795a59789d26b6b4b4c33f00c50da6e", + "packages/db/dist/esm/query/ir.js.map": "ccb4d95ae5caa7bfc320e2bc66ef54f685dbe867301c463e4793c80397c581a3", + "packages/db/dist/esm/query/join-conditions.js.map": "39d95b4fd518dce35318bbdf38cf3b9f105a4e3b5db15d4ff4048ce261a8caeb", + "packages/db/dist/esm/query/live/bucket-facade-adapter.js.map": "5346a9f6853231e91b63e7303be2ff234560ec0653c748f257fd103ea0b5fd41", + "packages/db/dist/esm/query/live/collection-config-builder.js.map": "7fc086bb59ab0dd80007cc16b7164a7a4c4d1306fc29ba2357e8ac2df67f2b09", + "packages/db/dist/esm/query/live/collection-registry.js.map": "4f4be3b283e3daa91838fa9ce44dbc11d29b599328d7db419319ed6fd7d9c8f4", + "packages/db/dist/esm/query/live/collection-subscriber.js.map": "1b1bec3ab4af5b8f568be4f298d5984ca74170929574b41ac7490ddf47b0eb32", + "packages/db/dist/esm/query/live/graph-scheduler.js.map": "f9d01ec493d292f27a6136816a61082939d7ff9b2915e73b9f04a58f573d48ee", + "packages/db/dist/esm/query/live/internal.js.map": "8f3093c6b396f3b3b0ebdaf9d330f23f71cc6ff9ad6813c598b590c497a56382", + "packages/db/dist/esm/query/live/materialized-pipeline.js.map": "9924a8437663a9ff914fc8e733903449f322b9fb9786a873b72f63e10aefd5b0", + "packages/db/dist/esm/query/live/ordered-source-loader.js.map": "eaa8b13f6b4d995d35579816e23e53041c925dc1f027271ce6bb99e89b3f67fe", + "packages/db/dist/esm/query/live/subset-demand-controller.js.map": "3c0ef841c2f6ce3f6c8c4e1f5ef6fe2b885b52140977f198479440b62dadb065", + "packages/db/dist/esm/query/live/utils.js.map": "0d1b870b1c6b91edcbb7d553fb4bf12122eb1331a56ffeaafac7312581c1443c", + "packages/db/dist/esm/query/live-query-collection.js.map": "f1ce1849148cd27103b5f0dcf55cb97a738d26f17706fe7685b7262cfc86e5ff", + "packages/db/dist/esm/query/optimizer.js.map": "fe4f220fd03842d9d518b44df4ffac8a03d9dc4c40fe3b5a1456ca05cb7c5a47", + "packages/db/dist/esm/query/pooled-live-query.js.map": "82bf9a5f6131a287cc09c05cd35d0f12910189f86ce2009529ecdb91cebf6bb7", + "packages/db/dist/esm/query/query-once.js.map": "cea3bbe5c87666103f83c8d02a8feaaf9f718f318b821f76ff5c2a5d77dbfea9", + "packages/db/dist/esm/query/runtime-reference-identity.js.map": "d06fece2e78c5ea3ae21e74ce22fb24a7422cd57632bc28d5cbd7292dd049dab", + "packages/db/dist/esm/query/subset-dedupe.js.map": "d86e0085d0097a37f35375a94d52063829177fa6ec2aefce3cdce46c42133e82", + "packages/db/dist/esm/scheduler.js.map": "d0ca54ee8e793d30ba55632a0f59d8c141724698b4d1f82236c70c7ef36307b1", + "packages/db/dist/esm/strategies/commit-completion.js.map": "0ccb2df9d5ed0561721f8459a6f029a031f774250aaaaadb7f68a5556bd4729c", + "packages/db/dist/esm/strategies/debounceStrategy.js.map": "8dcdddd38e78f32c844279e92845359b7a95f4b91cb6add26474b680e32e5421", + "packages/db/dist/esm/strategies/queueStrategy.js.map": "e61023e3380ae9594484c9e673b078454955b85ef8d37fd57b9ecdef1bdf6da2", + "packages/db/dist/esm/strategies/serial-pacer.js.map": "4e86b6c2203e220ab7096e2e624463c92c2bf6c8cb1098eaebad2df56f5e4a69", + "packages/db/dist/esm/strategies/throttleStrategy.js.map": "8abc6c79e822c3f6de0496b6c42bd730680f8b48989016e3f4257e25ab01a25d", + "packages/db/dist/esm/sync-persistence.js.map": "a6e395d89daebfc769642fa8bd00a0b4f3c9bfb4090a200d97f6637363d89df2", + "packages/db/dist/esm/sync-receipt.js.map": "037f7d6167e90947a5468a36757d4bd32dfe0c937d8b5f1ab7a83bd28c23f756", + "packages/db/dist/esm/transaction-commit-work.js.map": "10ae9167e5a3a0b030a5a9cb34b347d4bb6f719e023a84db6a0a9da85f39accf", + "packages/db/dist/esm/transactions.js.map": "d071193784fd02d4365ba1db134d53c13932fdfbeceab2e300211335e6582873", + "packages/db/dist/esm/utils/array-utils.js.map": "543f01857219d2625c3dab6f7c8da41e45ffc29f34643681f8be3d42b61099b6", + "packages/db/dist/esm/utils/browser-polyfills.js.map": "259ca9a616f32a59ae240606903456aae505856a03b01d7f0c5133fe724316c5", + "packages/db/dist/esm/utils/btree.js.map": "3d96225772c3794f2e65962fbe29cad56738f45a382b7a4e40f1370dcf780830", + "packages/db/dist/esm/utils/callbacks.js.map": "61b12884fd3fb07d404d53734577a254af387dde4d178862901ed43bf154d713", + "packages/db/dist/esm/utils/comparison.js.map": "4c3232f26a3487b36546b34c0af682b0880282c63fa021da36b070ba0506ec19", + "packages/db/dist/esm/utils/cursor.js.map": "50e78e47699a68d3ac3828eb37760f3d8331e25ac2bd833b74796c2f121056f3", + "packages/db/dist/esm/utils/error.js.map": "302fbea3014e7d353c8e19456d5c7b930876a738e868945fd0eba88498886626", + "packages/db/dist/esm/utils/get-or-create.js.map": "049135923c3a435c8a6435c2330978b7272b1f954481a71ede0450453b3c7d1f", + "packages/db/dist/esm/utils/index-optimization.js.map": "36d3be1f0e57af2e910d9d61f01f4757fe68637e84e0f59a360718b197505d97", + "packages/db/dist/esm/utils/source-record.js.map": "e52ab96575ec8e4fb99084e788ceb8e2f06451369503a5d3792a32b27f7311fe", + "packages/db/dist/esm/utils/type-guards.js.map": "0b96a21f6d742d02da8e62350804eb0e494dc2c10d90f03f29750988c843befc", + "packages/db/dist/esm/utils/uuid.js.map": "439824094f7d0ab0ce52e065cc80e717bac86ba96717435f0befc616495b83aa", + "packages/db/dist/esm/utils.js.map": "a635c14883c73c14c69a03637c408429e951b99fc3055062915245a2359f9c04", + "packages/db/dist/esm/virtual-props.js.map": "73b6876085e95170f2a932adddcf3c333175b3ff80ba00340ebcc150afc6358a" + }, + "verified_runtime_source_sha256": { + "packages/db/src/SortedMap.ts": "dbbef7d2842d265cf5268f3ca8c512b0b79ea927135116d3fc4e08ea2799856a", + "packages/db/src/client.ts": "fdac0f151242b349c53e3a8bb0e9788cc432b35f306d2ae91d74bf832fc97f38", + "packages/db/src/collection/change-events.ts": "754f897ba843d004eaf5bfe3f047fc53aa24eb49a16ae1eae4073e32ec17ba49", + "packages/db/src/collection/changes.ts": "c6deb4b04891feec0f2c4368ffa9ea652d12593f4e7ddf4edbe392a706f314df", + "packages/db/src/collection/cleanup-queue.ts": "d6d24ba9216d9dfacf57623d14e61861b044a23cac1b8e475dbfe71795a0c84d", + "packages/db/src/collection/config-errors.ts": "ffe046ad75542c863e1781c192191479cdcb68e7dcfaf92c9ce180ee48522cc0", + "packages/db/src/collection/events.ts": "fd95280be3bdd9a1177d4d57b425f484b9daf06b4b257ed28d6b17e3ef1bd132", + "packages/db/src/collection/index.ts": "458e71b1478a6150ef9c1854fb7df483cb32466217cec050993f21befcb1dba0", + "packages/db/src/collection/indexes.ts": "1a0b6633e5f5f24c1b29fe8e544ee3c52df6e886d2a61c8fe88750f3f811520e", + "packages/db/src/collection/lifecycle.ts": "5bb9027353ff30b9c467c1c3c749e739aef741ce1762c770ccd0ac90502763b5", + "packages/db/src/collection/mutations.ts": "735bdd4872c97afbe6d7333d42402635bdb54c914e6b5cacf703444fbf933f31", + "packages/db/src/collection/state.ts": "3c6caf3829455db5e6c037bf56e06c7a3b0e6abebc35368da0dbf886b4729e76", + "packages/db/src/collection/subscription.ts": "c446932a8a1f8b2efd79d00846f1a110e65a6c6df75d2b24658053e6964b530b", + "packages/db/src/collection/sync.ts": "4ff2e29f8fb27af6e71a46f959ad764a0d9dbd390bfba66604ba1c1836676add", + "packages/db/src/collection/validate-config.ts": "10bc3f83c357cf8b835694c73df9adf413c6190f924256786f17e123a688489a", + "packages/db/src/collection-options-brand.ts": "93df3fc8d90531ca926c6e9ca48653dbda9259cfaaade9c23cbbf35131b2e1ed", + "packages/db/src/collection-options.ts": "0b0f9713312f9554f62c020dda3bae8291303dbeadb85adfee76515a28442feb", + "packages/db/src/deferred.ts": "9c99553d7ab92ac89d377cb3382a9dfbdf592635c87b86eac8aa1f18efc4da68", + "packages/db/src/error-message.ts": "9ab4a64436c3db3a61f3a84b5ccff11359ccdf5676c81b81dc3be9db9c1dff13", + "packages/db/src/errors.ts": "ee54c0fb19ef95630e833b62562a4a2310ebd79337746355b8f67676cce0e416", + "packages/db/src/event-emitter.ts": "1d8775f3c2dbc9c833f03d2b9bb0d55d1606419e43d661064cc95cd91eb187e8", + "packages/db/src/indexed-db-wrapper.ts": "216fcac76c01b1af8363cda8a93678f53cd2cd58c9df918cd3d56d2a1f25fd11", + "packages/db/src/indexed-db.ts": "4c9960e7020389ab784f3ea6ce3fa1684b36ad1f6aef28a100cc15517374832b", + "packages/db/src/indexes/auto-index.ts": "d226516a7514c40e83010ab211020adba17965af96bd6d3b96e88989c6f31b51", + "packages/db/src/indexes/base-index.ts": "63e35c6bf011ad361882509a521407acf29b9404ce4e810a8f4d54b50c0e6685", + "packages/db/src/indexes/basic-index.ts": "9a128909d6df2c6d5b9ef07295a55f9f587170721cb7c6a652dd9c43eb674633", + "packages/db/src/indexes/btree-index.ts": "b006f65cf1c2ab14004e7311f4ac4b89958e919fb8439cd9ca25090789e37186", + "packages/db/src/indexes/index-registry.ts": "d44e34c9e5d90db56dee7f91e402f2547d91647ae100cc4752bc13bcd49e2719", + "packages/db/src/indexes/reverse-index.ts": "ebf2d22eb0eec10acb4df154488862243ec20be117bc069fe307fc991853dd0b", + "packages/db/src/live-query-adapter.ts": "cc6e68e05afb876a46989357304a2e9315cac918d2d7623e4272c3a4ce3b470c", + "packages/db/src/live-query-observer.ts": "37f3bdd904af1ba51d6d8821d4711b4e29789e307363590972e52a50e77a4d0f", + "packages/db/src/live-query-options.ts": "8ba7980c64de78500e56b3c822e19e8e451d77ee50352beaaf89644f47c3f411", + "packages/db/src/live-query-primitives.ts": "16fa17f9cefbb2dad98fda23651d792f8c8cd5bb4b812bd1197de7ae8f1f99c1", + "packages/db/src/live-query-window-binding.ts": "7564bac73ec3aab26dbc4c4ed44bc972f108fc6241c158eb63c8b7ae22b67747", + "packages/db/src/live-query-window-controller.ts": "ea439e6b6f0ce1bdd03f259a39d6d912024845e8343d013b67a7b2251740c8fc", + "packages/db/src/local-only.ts": "06d516a29e6f72b87f08c83f1fad2454dbc1cd3a3d296562f74fa5eb5aaec408", + "packages/db/src/local-storage.ts": "62331d402b5a7b958617e45ddb9de90a358f2fb1645ac589c5d412deb079cb5f", + "packages/db/src/optimistic-action.ts": "3b3d98ec0561927c301c6979256bbcd98faf0ee41b2b6690abe1c189eeaf4fec", + "packages/db/src/paced-mutations.ts": "ea9239b269c2622d23cb2fee6df2895534d64b43fb245f8160e054587b9b5458", + "packages/db/src/persisted-readiness.ts": "3fdff536570ed25347928bfac3043482d87a1506ea8bfd4d2c4e7f4c532fb598", + "packages/db/src/proxy.ts": "97fd4841cd8532a8bd2f08197d0d755cf348a50e5df0b2f4705079895f2a42c1", + "packages/db/src/query/builder/clone-query.ts": "7265db09e09e441d598d5b5774759f4ece6dc1cf5d5723d49d1c342a36e3798d", + "packages/db/src/query/builder/functions.ts": "71ff875be3b94602be517fd03d2ffd0fc1866a189a4849403623d9865e35227e", + "packages/db/src/query/builder/index.ts": "c658662a2ff50d85a7a1d662b728873e16a568d58e38248bbc57d03f884b463e", + "packages/db/src/query/builder/query-ir.ts": "6aa35045e8a83c63a68e3af2de951879aaa2b91c132606db6d0bfd32247af108", + "packages/db/src/query/builder/ref-proxy-identity.ts": "909d40e4425647ae58850decd6891fa7f6aa4690cd76e82c3ced13b70dc93b10", + "packages/db/src/query/builder/ref-proxy.ts": "da99a9d725676d7c29b1bc2c10913465389c6eb442ca651d06ab658a2561d0c1", + "packages/db/src/query/builder/wrapper-identity.ts": "3053d30505c57f0eb16cc573e196291e617aff7479151508bbaa397fa3757f57", + "packages/db/src/query/compiler/evaluators.ts": "499cb252c6e2c166cf626bf88c6c08ced102020558bdccab46a6d12acd52ec92", + "packages/db/src/query/compiler/expressions.ts": "70925f2d29f57aec189d0e7f485d0531534e3482f0696a5e9a1141d8cf36c42d", + "packages/db/src/query/compiler/group-by.ts": "f30cb4ebd6940dd4a3888fde633e16f952225e4f9d35be4e450d6ad912a22bc7", + "packages/db/src/query/compiler/index.ts": "0d53aa7d27fba0e0f472af55404adbc437e69a537aa2f529710e81d08b16944e", + "packages/db/src/query/compiler/joins.ts": "8f0aacfc3e09bbc2672cc21af75ac8b27e1f957a47ee8e36ce3b451137beff08", + "packages/db/src/query/compiler/lazy-targets.ts": "6949ebfac1f0d76b64dc9a3c1cfee87490702a21de235c320fdd7aaa02b77587", + "packages/db/src/query/compiler/order-by.ts": "271b3195a4d24cc0faf9c19261a1e18cd91eea8794ab9e611257929c4e3a880b", + "packages/db/src/query/compiler/parent-routes.ts": "d59e9d64534c53bb58349f07c121158adc9b37cac7a0b5150493e6368442c18b", + "packages/db/src/query/compiler/query-equivalence.ts": "7171fa0f1aa960297ba1269193ed3527967d78ebf1c19ef03544604280aa4480", + "packages/db/src/query/compiler/route-metadata.ts": "53283291ce25656339492bff678b8742a7fdc7b60c49d3b7f7015569e5c06788", + "packages/db/src/query/compiler/select.ts": "a4b60959e37ea674bfac8f3cc4076c35facb9378891f72603c309e92c2882760", + "packages/db/src/query/effect.ts": "013d653074e139caf2e2089574c3a985c7f408c9434d2d70d7c3b2dd9cc82258", + "packages/db/src/query/equality-conjunct.ts": "02041d880376b46814ab072747a9b0493e24cd4bd72df8389726780e535cc584", + "packages/db/src/query/equality-value-identity.ts": "a7b65759c20ebec4d83c47ec744bbd382da9a86ffa4c9e59028827127081d016", + "packages/db/src/query/expression-helpers.ts": "15d6635f8b28dafee0ca0afe2e8598545f09995a5b992798916d789167383019", + "packages/db/src/query/ir-stable-identity.ts": "2369d6ad584c2546f4fdf2051055a3bfa9bedb0c53cd8ff3d86b2b2a86cfd80e", + "packages/db/src/query/ir.ts": "9b319bd3728640d2ab941b167ea16b09d721ccff112647f597c9761dd43b5ada", + "packages/db/src/query/join-conditions.ts": "c2659d14f7a15203343de19b66cb83195c7ab5c26f953b52bbf6f40bbe012802", + "packages/db/src/query/live/bucket-facade-adapter.ts": "cbd6ea77a9d103fea6253e20656e31ff41ac353d134cb0c49710c56ffdd4b3c3", + "packages/db/src/query/live/collection-config-builder.ts": "aa55faec9bc8db4597357989d9466e4ab030e743ee6a197691807b775f0f8440", + "packages/db/src/query/live/collection-registry.ts": "c24da0f80292e4fa22370711cdf66e52ec44989f8fda50430025aa5b25510e65", + "packages/db/src/query/live/collection-subscriber.ts": "74e97fe498b44446d5ee21912175afab6a3e58d2787e04e890a26771d5c8e1a6", + "packages/db/src/query/live/graph-scheduler.ts": "bdd37b3814814a2bf32fa55422a2fb039107d04cbf4fbd79df67056c5bc4654b", + "packages/db/src/query/live/internal.ts": "3aa788cfb3647e2758ff9befe18e4503bc68797fd97d55dc5c3905db9a7145fc", + "packages/db/src/query/live/materialized-pipeline.ts": "9e0ad27c197c9571e242c7a7ccb286739ecdd748b25f00f4f4f8aae4f0894407", + "packages/db/src/query/live/ordered-source-loader.ts": "95ea59a251cd540406d3dd2dac1ba85f41d1f6422c4ffd44f973d9ea4ca01e18", + "packages/db/src/query/live/subset-demand-controller.ts": "28e49884847544356975afd781bb2d485844c2f43f9f33097aef28d06ba2a1d0", + "packages/db/src/query/live/utils.ts": "00d2a0462ea2a997f50d104f641e9837cdf1341fc763116a0df773f6888f1646", + "packages/db/src/query/live-query-collection.ts": "d9fa71c30a1dbb69d58e2e11c2ba4acbeaa727f8d1ee70ce3d225be08ce7224f", + "packages/db/src/query/optimizer.ts": "384be668674194cb69a3b6165ffc97bb3b7edf066e107c444f36f6bbfa43571c", + "packages/db/src/query/pooled-live-query.ts": "d2b9d1c77cb7b2f9fa0c0ed6c546e0738390d20207ebde466636d0c95f459554", + "packages/db/src/query/query-once.ts": "1399a24809ce83173f0d952ab35620bc4430874cb96ab14229495fad3ef57478", + "packages/db/src/query/runtime-reference-identity.ts": "c1c695e80771ab545e58254381333bd24469813d69f448106df2dc5a5de1db48", + "packages/db/src/query/subset-dedupe.ts": "c535bb2a38db491d1c11024c88c71dc1664a59bae3c19f1f5bec35d37d5b99de", + "packages/db/src/scheduler.ts": "b21cbbdc088b3c1dc4840b7db857ee7f262e6958dc8bc300398ad8e93da4f01a", + "packages/db/src/strategies/commit-completion.ts": "242b52fc7a041f5f9d17172c356dc4e1ec06f2cbeaa85ba5995294356fba9b15", + "packages/db/src/strategies/debounceStrategy.ts": "8ab894803575f1cfab85939adbf67637342bf2e230e34d0db9bb642f205cdce5", + "packages/db/src/strategies/queueStrategy.ts": "c21794ea5707e75de082b87c80ee5a19fd8b1752fd7725adf7435b377a5e4196", + "packages/db/src/strategies/serial-pacer.ts": "63f4aa8f7aacd4bbe5cc3d56173fdb9231fedea68bb721bb2d420cf2303ec679", + "packages/db/src/strategies/throttleStrategy.ts": "028b5a000cc5a49a0176a42f0bb0c5cbb0a71849e543b523b080942947f40cb7", + "packages/db/src/sync-persistence.ts": "eb64401db52d9d2f269e5b6d92f85ebb5d4cfe6479dd1d4832918cd7b9dbad6b", + "packages/db/src/sync-receipt.ts": "5ab4074603d96c23decd4fae499ed55f3a6ee5077e379f370d9b76fd90565399", + "packages/db/src/transaction-commit-work.ts": "b3c341c01f7a8b1207683fb3388b66b1173481f43ea59160e1922778e64872fe", + "packages/db/src/transactions.ts": "60acaa21a7c158e2617bb382778bec1aa2b5fcaff7d527ab902e47b364d140d7", + "packages/db/src/utils/array-utils.ts": "a151c6b1779cf975e85e53a7b2328865889738df1f0d0fc3b8a8e466d15911f2", + "packages/db/src/utils/browser-polyfills.ts": "b3212d22af7254eb8c286d89393eb043324abd0b5a7ddbc8eaaa505e292b437b", + "packages/db/src/utils/btree.ts": "e2172d757bfdaf3e528fe94864876fac9de24a66302ed1930d108daa1f525b1e", + "packages/db/src/utils/callbacks.ts": "da4d6fc8d94bf93d07442eefbebd1ec0c183ef559fa7eebceb51b3652816708a", + "packages/db/src/utils/comparison.ts": "ed8c22f7315f0119025d47a0ea6432bab974c618665b167012c6c12da2287449", + "packages/db/src/utils/cursor.ts": "b728649d395a8e4e61cd6a63479be92ce2d4d4a08dc73beb549b92dafec5181f", + "packages/db/src/utils/error.ts": "133924e3809af4a69826c08774455f12f10aba675cbc92a4298525868bba4ece", + "packages/db/src/utils/get-or-create.ts": "eb56ba9e1a6eddecc8241062cb9a4ea0afca7437fbb50f0404de1276a611ee82", + "packages/db/src/utils/index-optimization.ts": "034310477e43f29bfe3493a56ee3b3524378ae3f941f4a452e6a15b9aec24425", + "packages/db/src/utils/source-record.ts": "48023571bd2e850170b78a34a3a9d14940d97742f510f96cad4baaf4a75694e3", + "packages/db/src/utils/type-guards.ts": "b7a63accd5abd4dd5c93bb451650fc962a2fdcc3981e70f0b7b373ebeb5f07db", + "packages/db/src/utils/uuid.ts": "0365c5c02b46eabac53562dac87b342656e2774b36385a094c16b3a9db3a5ca1", + "packages/db/src/utils.ts": "67e7143cfb324dc8f97c310679730682ecfb8088c2dc0d9e0efde6bc57ff9c76", + "packages/db/src/virtual-props.ts": "6add3fb9226251bf4dddadc7c5227469d4183a4df7799047f2fdc6b51ef4d9c0" + } +} diff --git a/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-backend-concurrency.md b/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-backend-concurrency.md new file mode 100644 index 0000000000..62a1e8c6b2 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-backend-concurrency.md @@ -0,0 +1,166 @@ +# Convex lazy concurrency at the real backend + +This record closes the real-backend receipt/version receiving boundary left in +the [interleaving follow-up](2026-10-27-convex-lazy-interleavings.md). +The scientific models are unchanged. A new receiver uses the actual isolated +Convex backend, both real SDK clients, real opaque server cursors, actual core +commit receipts and real optimistic transactions. Its source binding is the +[manifest](2026-10-27-convex-lazy-backend-concurrency-source.json). + +## Law and receiving path + +`LoadSubsetFn`, `LoadSubsetOptions.signal` and `SyncAppliedReceipt` in +`packages/db/src/types.ts` require successful loads and canceled uncancelable +accepted work to wait for their establishing writes to apply. A cached caller +can owe zero new receipts, and can still wait behind unrelated held work. The +fixed-query coherent SDK premise and atomic publication require a fully acquired +source to publish coherent authored relations in monotone revision order. +Stuttering and skipped intermediate revisions remain permitted. + +`e2e/lazy-concurrency-oracle.test.ts` receives these laws through the public +paginated factory and real Collection. `LazyReceiptModel` owns facts and exact +establishing obligations; `LazyVersionModel` owns authored relations and the +compatible monotone revision frontier. The new driver has no independent +expectation derived from production's page chain, sealing state or queue. + +The server fixture adds `tasks:revise`, an atomic full-table text update with +stable keys/ranks. The independent ledger authors the complete expected relation +before each mutation starts; the separate unpaginated query verifies the fixture. +Updating rows in separate transactions would permit intermediate server +relations and would not test this law. SDK wrappers only record genuine results +seen by the adapter. The source-provenance comparison receives that evidence +separately from the offered revision ledger. + +Each history first establishes a shallow source prefix, then starts two full +acquisitions. Required facts absent at acquisition have unique supplying native +batches, allowing one actual receipt to establish both callers. A real optimistic +transaction holds application. The shared held receipt is observed and both +callers' missing-fact sets must be empty before cancellation. The canceled +caller and successful peer remain pending until release. A cached shallow +caller owes no invented receipt; its early or delayed settlement is permitted, +and it must complete after the responsive drain. + +After full acquisition applies, every source change callback checks base rows +against the unchanged version/provenance models. Two actual version-bearing +batches are accepted during another optimistic hold before application is +released. A version witness proves some unique new fact reached acceptance; +it does not impose a commit partition or claim every authored fact was written +in that batch. Application comes from actual receipt fulfillment. FIFO work may +already have advanced to a newer revision before an earlier Promise callback +runs, so the driver does not require historical rows to remain present then. + +The reader then loses its real TCP connections while an independent writer +advances the server. Reconnect must eventually establish the latest authored +relation, allowing skipped intermediate revisions. The network gate forwards +uninterpreted TCP bytes and never synthesizes page data. TCP loss is a transport +fault; it does not promise a native provider-error callback. + +## Generated reach + +The matching fixed/random family varies 7–12 rows, page sizes 2–4, text, and +whether another revision supersedes one during disconnect. Every history runs +both real SDKs. Each lane has four top-level histories, giving sixteen complete +SDK/backend episodes across the two lanes. + +Final full-suite evidence observed all eight states in all four histories in +each lane: + +- `real-shared-held-receipt` +- `real-canceled-held` +- `real-cached-peer-drain` +- `real-two-revisions-held` +- `real-monotone-publications` +- `real-reconnect-latest` +- `javascript` +- `react` + +Actual callback snapshots must contain both the held-drain revision and the +reconnected latest relation. Manual comparison checkpoints cannot populate that +callback witness set. Callback violations are retained and surfaced at explicit +checkpoints without throwing through production. Environmental polling also +stops when a retained semantic violation makes its target unreachable. A timeout +alone is not classified as a semantic mutant rejection. Both SDK receivers run +even after one fails, preserving each independent failure. + +## RED, GREEN and hostile calibration + +Unchanged production passed the extended receiver. No production defect RED or +production repair is claimed in this round. Changes are the backend receiver, +atomic test-server mutation, lazy test configuration and coverage documentation. + +Two production edits were applied in isolation and restored in `finally`: + +| Mutant | Semantic rejection through both SDKs | +| --- | --- | +| Remove `if (visible !== true) await visible` from the native subset wrapper | `receipt law: a settled before batch-2 applied`. Provider completion cannot substitute for the actual application receipt. | +| Drop the first changed row's update from native keyed reconciliation | `lazy version: mixed revision or public rollback`. The first row retains `task 0` while the remaining rows expose revision 2. Provenance alone would permit those individually delivered facts; coherence rejects their combination. | + +Both shrink twice to seven rows, page size two, empty text and no supersession. +Both directly replay at seed `20261028`, path `0:0:0`, with exit 1 at the intended +comparison through each SDK. Restored production passes the same direct replay. +`calibration-v2` contains exact patches, commands, logs, before/mutant/restored +hashes and the final receiver hash. The final source was calibrated after the +review repairs. Earlier calibration logs remain preserved as stage-specific +evidence. The restored `options.ts` hash is +`ea2c2efc4d574080c5e03da8e121b79406640c667a1e260ead9eff22f6253d33`. + +Normal final command, from `packages/convex-db-collection`: + +```sh +CONVEX_TEST_BACKEND_VERSION=precompiled-2026-10-10-72ce13c \ + ../../node_modules/.bin/vitest run --config vitest.e2e.config.ts +``` + +Result: **21 tests pass across six files**, including both complete concurrency +campaigns. Targeted entry: `vitest.lazy.e2e.config.ts`; direct history replay adds +`CONVEX_BACKEND_CONCURRENCY_ORACLE_SEED=20261028` and +`CONVEX_BACKEND_CONCURRENCY_ORACLE_PATH=0:0:0` and selects +`e2e/lazy-concurrency-oracle.test.ts`. Correct production gives GREEN; the patches +in the evidence package are required to reproduce each hostile RED. + +TypeScript, changed-file ESLint, oracle lint and `git diff --check` pass. Oracle +lint's known missing-local-`main` diagnostic accompanies successful use of +`origin/main` and exit 0. The runner resolves `@tanstack/db` to its local ESM +build. All 107 JavaScript modules are fingerprinted; all 106 embedded source-map +inputs match current source bytes. This binds the actual receiving runtime +without rebuilding it. The scientific models and seven Convex production files +retain their pre-round bytes. + +## Review and preservation + +An independent authority/design review and a fresh receiver review inspected +the sources and evidence. Review repaired a real reach gap: manual observations +could have satisfied the publication count without proving callbacks reached +the monitor. The callback-only exact snapshot witnesses now enforce that +boundary. The source review also required the explicit all-missing-facts-accepted +cut before abort. Final rechecks have no unresolved supported findings within +the stated profile; each report binds its final source hash. + +A separate receiving-control calibration disconnects the actual change callback +while leaving manual observations intact and production correct. Both SDKs fail +`held revision never reached the publication callback`; the control shrinks and +directly replays at seed `20261028`, path `0:0:0`. The receiver is restored byte +for byte and the same replay passes. This is a test-recorder control, not a +production mutant or native provider failure. It confirms that the repaired +reach guard rejects the exact blind observation path identified in review. + +The task's `outputs/convex-lazy-backend-concurrency-evidence` preserves raw logs, +patches, reviews, runtime fingerprints and source snapshots. Prior frozen +formal/audit/interleaving artifacts and versioned records remain unchanged. +The existing local install override is unchanged and uncommitted: +`baseline-browser-mapping` 2.11.32 → 2.11.30. No install, commit or push occurred. + +## Limits + +The backend monitor covers stable keys/ranks and complete acquisition before +version monitoring. It does not add general query translation, arbitrary +partial-prefix retention, row membership movement, idempotent/superseding receipt +attribution, accepted cleanup abandonment, native application/provider failures, +or same-run authoritative truncate replay. The composed failure/terminal-graph +monitor retains its controlled receiver, and existing backend/window/framework +owners retain their independent boundaries. Both SDK client classes are used; +this is not a new React rendering or hook-concurrency claim. The new backend +production mutation calibrates mixed revisions, while transient rollback/final- +correct recorder calibrations remain in the earlier version owner. Four fixed +and four random histories are bounded receiving evidence, not exhaustive +scheduling or a temporal proof. No new TLC run is claimed. diff --git a/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-boundaries-source.json b/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-boundaries-source.json new file mode 100644 index 0000000000..163daf5e12 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-boundaries-source.json @@ -0,0 +1,406 @@ +{ + "starting_head": "8c24922026fb4a714cb54b992ac85faa2e876742", + "binding": "Current source SHA-256; this manifest and the unchanged local installation workaround excluded", + "files_sha256": { + ".changeset/convex-collection.md": "c7ad56ad983fe653f0054f0a82fcd47cad46c1d59087d62d79a21ffb077eb324", + ".github/workflows/e2e-tests.yml": "62f257aab4d6629d4227edbf29e659d3d9a4149ca103add59f5d218924ccb50f", + "docs/collections/convex-collection.md": "3901d5d040f8e101369f1a16a160d4e5d0df391ec48408f99d995191ad013105", + "docs/contributing/oracle-coverage.md": "0e2f93f152dbf074d4c91720f2d70969f50beeb8c8ae5571831c963f09c0ab2d", + "docs/contributing/oracle-reviews/2026-10-26-convex-lazy-paging-source.json": "a4af1a586c058d08644fead26679a2894209aa6cbe3803dcf1c8cbefab97bcb1", + "docs/contributing/oracle-reviews/2026-10-26-convex-lazy-paging.md": "1de8506081336953f4b1c53933375960c84af277c04cb3dc07b91b79df391810", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-backend-concurrency-source.json": "b859cc163b55b7b9076a0d2c94bad1b524fd0405de39a0689c503c142975ba62", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-backend-concurrency.md": "adea3ed30c6f5df32e5af261497afd84c5c5519ca1ef71130681e899e7eba450", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-boundaries.md": "dae5fd558a6fe1e7cc2adaff2f977496edfed349be1494994bd3f890c00c4845", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-interleavings-source.json": "0fa1e828f4fee657e9011770bef73da2ac02cb4ca068d386d3a662c81a3e7c8b", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-interleavings.md": "f105899875a4129b7d816cacc1d6f094c68ad6048cf70350e8d105bd0acd89e6", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-paging-source.json": "d25abd9b66a277ef20186bc507ecbcd082f8562a7acbfd2b6d691704434a110d", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-paging.md": "d057323e0dee9bab5cf8fbba85fa6d6c525ffc0be7843f1e351d7e48a6e42fff", + "packages/convex-db-collection/README.md": "2b92d0ce849e926acdc2d204e09fd9af3a662c8c8314ad8db5d223e74b77d12c", + "packages/convex-db-collection/e2e/backend/convex.json": "e7106add2ff21d61a430927bb9b1ba96b036b93fcecc31ff786149a6070ecd67", + "packages/convex-db-collection/e2e/backend/convex/schema.ts": "319611bbb3886d4b5c7bdbfbccab1f42792769304e2be8db009b73baf90aa92f", + "packages/convex-db-collection/e2e/backend/convex/tasks.ts": "9663005b0c35ec3b1a89fdc5644b2c16e16fa97ff0f77dbb641a3e05f8b6f64d", + "packages/convex-db-collection/e2e/backend/package.json": "8e2e33603aa201acb7c8d88a55130bc07275d8fb53693e0965539ae4bc4e3585", + "packages/convex-db-collection/e2e/lazy-boundary-oracle.test.ts": "c14b42dcfbd28e1c875204593c10d7f98ebc6323cdcba32736be8315147ddccb", + "packages/convex-db-collection/e2e/lazy-concurrency-oracle.test.ts": "69a02d34d0efd0d1881cfbd321f22aac2e820597efd2cd32fc3fa1dec745b625", + "packages/convex-db-collection/e2e/lazy-paging-oracle.test.ts": "7a5c1390879e4e01757d020e22bfa24f5852a2cf5bb4e18faa4174ce47a02e48", + "packages/convex-db-collection/e2e/local-backend.ts": "e38be0c546a885f55b004f23bc894ab86d3c3b5880bfd9dde8208866a42aea5d", + "packages/convex-db-collection/e2e/network-gate.ts": "73851db1ae46e83b7ee1bdb88faf01aaec27178644a73e1f7835f94681806dd6", + "packages/convex-db-collection/e2e/setup.ts": "d448bab619ac7243957578b2565cdb647a5e71a70df3c65af3c3dc378b5df6bb", + "packages/convex-db-collection/package.json": "0b1ec7960948a8d0f51b418881db6f962d7189a1d8421eb5f8f7e4131f6dc5cd", + "packages/convex-db-collection/src/index.ts": "4247e2bbd168a80791861f4553c10d0d878504a7cdc6a65d4bc648e15b1e51ba", + "packages/convex-db-collection/src/offline.ts": "55e70bdbbc310226a9f0175d0c0912218413ba67433b5b47ccc78d6e7dadd5c3", + "packages/convex-db-collection/src/options.ts": "ea2c2efc4d574080c5e03da8e121b79406640c667a1e260ead9eff22f6253d33", + "packages/convex-db-collection/src/paginated-options.ts": "de0e92849d363f2545823d96829964084b1a115b890f0f521f9bdc0958ea2b76", + "packages/convex-db-collection/src/pagination-demand.ts": "d2be74c1f3610c153c2b7c3c9411aa6ea216db7e76c9b0a7f32d63ef2bde57ce", + "packages/convex-db-collection/src/pagination.ts": "0308668d07db701d1f7f891d001a4aca2798973ea29bf1d4b00931eaa0e05f1d", + "packages/convex-db-collection/src/subscription.ts": "a0c312caf2af3667c18e96c565ceb159c20d65550426cdd44aca69a43a3543e5", + "packages/convex-db-collection/tests/helpers.ts": "11f2c79c6872373e8f77efae62d2fabfead0e5af6673c86920ae2ef4abdb7274", + "packages/convex-db-collection/tests/lazy-paging-campaign.ts": "bf5dc54dd05db268c88ac1b8b9b32aa5a2765245658608e52163089d4601d63c", + "packages/convex-db-collection/tests/lazy-paging-driver.ts": "9819960aa222777f9de3b4bbc3728d492fd9304865701dacbbe381591e88cea6", + "packages/convex-db-collection/tests/lazy-paging-failure-history-oracle.property.test.ts": "65ff6c00d61014e841684ec7783f3175b7a013183f4bf671bcbeedb45212f272", + "packages/convex-db-collection/tests/lazy-paging-history-oracle.property.test.ts": "5bc8808d6f71648f48e40acb05943132448f3e8f5bba31d97e261f1741674384", + "packages/convex-db-collection/tests/lazy-paging-interleaving-oracle.property.test.ts": "f3a0238452746233224bc0b21adf790b594cacef82d786a0ba419d19980bb773", + "packages/convex-db-collection/tests/lazy-paging-model-oracle.ts": "7a0ce99a3442b15d516f03f0494b5a5cb39cc7106452ce50e257af79a014fc7b", + "packages/convex-db-collection/tests/lazy-paging-native-receipt-oracle.property.test.ts": "5714e2c25507f1ea4aef5bb6eb9bed69bc9a9b079540667570227aa1014a0822", + "packages/convex-db-collection/tests/lazy-paging-oracle.property.test.ts": "3a7b5b6284076bc8dbdb59ac1d735f509c6bc19201ad355a0382e7a797a2d569", + "packages/convex-db-collection/tests/lazy-paging-receipt-model.ts": "b3714d83e0a90ceb9184a1701059f027907ff663ce829fa58895007147cf1453", + "packages/convex-db-collection/tests/lazy-paging-receipt-oracle.property.test.ts": "06b60b0a86926070dd57dd068f4abe572d48924e2cea29acb77f417bf8115d6a", + "packages/convex-db-collection/tests/lazy-paging-revalidation-oracle.property.test.ts": "130b4e11498b9983cf123d03dd116957f089ac14c18ed7cbbd4ed6a165bdc10a", + "packages/convex-db-collection/tests/lazy-paging-version-model-oracle.ts": "5aee0ba04f556d312e25f03c6077949d47daf6d0e4eae6cd3dad0a9fe9ce86a0", + "packages/convex-db-collection/tests/lazy-paging-version-oracle.property.test.ts": "8f4f115806a65dc71b3382dade7a89e326decc2b3c25505369783f30163a210e", + "packages/convex-db-collection/tests/sdk-cache-oracle.test.ts": "0e011a20ce991195742bdfa0cd570d831105604ad8e3e2cc4b8fab4b62daad58", + "packages/convex-db-collection/tests/types.test-d.ts": "3ff1a42d6caf95d14e5270b632340524a9f796b978ccd031835bd1f2d0dab6dd", + "packages/convex-db-collection/tsconfig.json": "623bc70a72fd718e7d209c90a08df12cced0e9045a3565058623e34e946a5710", + "packages/convex-db-collection/tsconfig.test.json": "5168aca44188a9be3b0bee215bfc2f222010dedb8f4e1fc08029c6873dcb6cf1", + "packages/convex-db-collection/vite.config.ts": "3f4d5abb1fb86d41ee205bf6364e6f38ffb8de98f5d42cb3d233e52e674902bf", + "packages/convex-db-collection/vitest.e2e.config.ts": "b43b7b797860455efa827774e9847975bd9850f81b9b284f43665c422e45b440", + "packages/convex-db-collection/vitest.lazy.e2e.config.ts": "17ccc7abc23cbe325433eae0487d36087994a15fade3b0c62a0ec2dc003b09e4", + "packages/db/package.json": "bb6df01ff9589c8b75437683c935b8457f65e9216d37173a4730e1a169e0ab43", + "packages/db/src/collection/subscription.ts": "c446932a8a1f8b2efd79d00846f1a110e65a6c6df75d2b24658053e6964b530b", + "packages/db/src/query/live/ARCHITECTURE.md": "aa6a41b651809819d414d364421140bab02f9b992e9f5dbe73cf634b43c46478", + "packages/db/src/types.ts": "2b4c5926922db049e7ccd58c3e3fc22503f6d8a9400f3fb01900e82da85c7a1e", + "packages/db/tests/query/load-subset-joint-replay-oracle.property.test.ts": "975585ef1d1bc921afcab2e5384318c5f661dd0a2577e642351b2aafed0ee98d", + "packages/react-db/tests/convex-lazy-paging-oracle.test.tsx": "0a33955d45816d766100b89e45dd63ce32125864cf3e2f0ea46533a582b0333b", + "scripts/commonjs-declarations.mjs": "3dae7c394287bd5d74467947267ab35207a6290b2fb5b12dcf7b2664115b9d66", + "tsconfig.json": "79e4282995ed94ce7e73583a6ef896db6dd8968b276f2a900de8d63493ace99b" + }, + "runtime_js_sha256": { + "packages/db/dist/esm/SortedMap.js": "ecbe71a4b4387932ed64817362c31eeb58c151d1fda5ba6824a491e4d6eeaf84", + "packages/db/dist/esm/client.js": "4191f1ff58a2b3eae418d2e8dc56b0fe16a290ff233129535b0945f41a34f0da", + "packages/db/dist/esm/collection/change-events.js": "cb5740b95d7c403e0ed7fd8b2aa831a921492782fc66c47bf1c91647070de0ac", + "packages/db/dist/esm/collection/changes.js": "084445baf434ffbfce6ceba34108c32dea5a58b5db68d6c068af6a8b762fff34", + "packages/db/dist/esm/collection/cleanup-queue.js": "3378b66f70261b2ce7eb14fdd982114e838ba6ce6d61ec18b0de976605cb9ba2", + "packages/db/dist/esm/collection/config-errors.js": "d6beb8dddab089d6dfb2516b5850b4b2a709b2bb2a9843bda854ad47723c35d1", + "packages/db/dist/esm/collection/events.js": "0cb3eadd7a9ce2e454e0c4a1a1b0023d2fb96724ad5a4dc806684f9ed4e47d17", + "packages/db/dist/esm/collection/index.js": "01e254fc0d024e7e716b4b09a10e7656130663ae587ad72314873f3b87f5fc2f", + "packages/db/dist/esm/collection/indexes.js": "9d1dbc166c3a44cd9348a62d96c7724335ef38c6f466575c0bbc2419e5647130", + "packages/db/dist/esm/collection/lifecycle.js": "2a26fe829e0183e77a33777f3003e1ab8699a4a55ec6fc309336b35c581ea03b", + "packages/db/dist/esm/collection/mutations.js": "42ed12785749b4098b422a538155ade6d002eaef3ad9cae227c0218f44c95e42", + "packages/db/dist/esm/collection/state.js": "d03955c898fc1672a1a708723d6e4506064665f8a24c57aea6be956f2c4dd0b4", + "packages/db/dist/esm/collection/subscription.js": "ac04fdd5bb56894afa45ecd9bef37abd4f96418c7912cd329f40e397370d27aa", + "packages/db/dist/esm/collection/sync.js": "82be78b67610f59fe60bad6e9f9776884287d38d6bc5641d1478875a559c6961", + "packages/db/dist/esm/collection/validate-config.js": "16a2b254fde0bc0d37c376107a5f394b17dab89165d99ff56a1897af9c234138", + "packages/db/dist/esm/collection-options-brand.js": "ae4fa09d0ac1ff1a9be961efa924d7dc769aad56a7e67cd1552c08b48fee3880", + "packages/db/dist/esm/collection-options.js": "1c6d225b2e8fd061f7e7616d67de43288583660e99a8a596b86d3f8c58c3a0de", + "packages/db/dist/esm/deferred.js": "d8ce1c72bd8f76a4e49aa17042e46a5432284730d1b2ceb4b5afbf927f881e2b", + "packages/db/dist/esm/error-message.js": "176a340e97f58cf9fbb21edc92cf43c6703ad2110310965b3da0f983e1cfab9f", + "packages/db/dist/esm/errors.js": "8c22d726d3878b029e2d9bf89c158ae2c25f915fdc9833711cf63fab3e48cd26", + "packages/db/dist/esm/event-emitter.js": "07add965808d5bd93b29d97e0e6d7db1c060856a1ea20cb7601a81785f2c67dd", + "packages/db/dist/esm/index.js": "fa5714b1da1264029dc75dd2f9e18edf145f128232feca5690392ff94dac6b8f", + "packages/db/dist/esm/indexed-db-wrapper.js": "5ca4ed531df467c317a8b302f218664a13ab579cc472eb4b9dda3b70854ec667", + "packages/db/dist/esm/indexed-db.js": "f7a7b78e057570562aa7eec0279e37a58ac7c4cfc05e820401f89728654dfbf7", + "packages/db/dist/esm/indexes/auto-index.js": "663f98c52c018571e87f398cfd4efe51bd27f48e2a4ac76b8e31327a725da8de", + "packages/db/dist/esm/indexes/base-index.js": "59652757da474e7119d759f784e954b56d9daecf586e385b2bda4ecd9219f465", + "packages/db/dist/esm/indexes/basic-index.js": "1ac365be033cb255d3b4005de57b7466b9ed4cd21daa08a77d8dfcfef0584626", + "packages/db/dist/esm/indexes/btree-index.js": "9ae7bf699fd9d44f9cc7dab823df85638efb2f43114eb33aaa7120efc367269b", + "packages/db/dist/esm/indexes/index-registry.js": "cd9448a61e290d32e23090cc57c8282aadb2d1736611a33509dbbe1f328d080b", + "packages/db/dist/esm/indexes/reverse-index.js": "94f0751672696624240f6e214a9b16517c9145269968e07ec7b2b8f439e3f25b", + "packages/db/dist/esm/live-query-adapter.js": "e787323960ffd3e848ad669ccfea1df3d21d80e084e75b6048740dfe23756e3c", + "packages/db/dist/esm/live-query-observer.js": "53eb064feaded0b54fb64e5a4741b2ed5cf94de00638a2bac0a95e573aab75d8", + "packages/db/dist/esm/live-query-options.js": "51ddfc4eb7ae745a75db197d67e23cd0b9b15f527b1fa706a759d90fa7920f2c", + "packages/db/dist/esm/live-query-primitives.js": "67f6abfb58ecba0abfa2e3c8094c98a86eadad39bd96afeb48912a77475bd76e", + "packages/db/dist/esm/live-query-window-binding.js": "3f239006fa71213f924008cccdb29b9ad57d97beff2947a30f0e0130370bac02", + "packages/db/dist/esm/live-query-window-controller.js": "bdb129aa7772774fe9fddb598d911bd7ae06cb8d581c05c41f47152b478f6805", + "packages/db/dist/esm/local-only.js": "0a4a5d855e4e3db629d20898eca261fa2dc02c3cb2d0c6feec32a052796930b4", + "packages/db/dist/esm/local-storage.js": "5a01856bf1eb5f1cb91aeb6536f3d685e8129aac6e9ea0c6c5feb62159aeb8ef", + "packages/db/dist/esm/optimistic-action.js": "ca9f92ae29a9710cd18e3e71c46a2cc0f204334525aab42174fdb4f53ec67b89", + "packages/db/dist/esm/paced-mutations.js": "01135f1c21fd5deefa89c92ecf7598bbe42d98aa284e646ac21c470c0d346369", + "packages/db/dist/esm/persisted-readiness.js": "160a5bdb6868f603e1bf1358ccabadd72cbe70c05d87f807972873bb03f9c850", + "packages/db/dist/esm/proxy.js": "33a224882c8dc0db103d6d0b92e50bb71021030e608937e7a800411ee9eb3fb6", + "packages/db/dist/esm/query/builder/clone-query.js": "9fdd9595f744fd286b55de26cd552bd630a358e4114dd605ad4d75a474ba70ec", + "packages/db/dist/esm/query/builder/functions.js": "d43c221d002d703965be1587f725255ceb58dd11258670b2882c3451340efe55", + "packages/db/dist/esm/query/builder/index.js": "c42986783a038350a694a9d8352f559017f62fc4b51197ca99f6ea1ce386a8fc", + "packages/db/dist/esm/query/builder/query-ir.js": "8d535e9be5fea36bbea302665f1d30a1b4a029fe4fdc95e6f674b6b0a3c5ad34", + "packages/db/dist/esm/query/builder/ref-proxy-identity.js": "fc756419ecb6b451640f729a44aa2e9cef4385d20ef1c68c049d36fde27d2b10", + "packages/db/dist/esm/query/builder/ref-proxy.js": "b71f544f804438a3c4a7ccec380fa0f4614ec7c859a983de4520d19390707993", + "packages/db/dist/esm/query/builder/wrapper-identity.js": "00c10f26051967ff8f284e550ab207552ac2632cbc08a7623617dad1c593bb06", + "packages/db/dist/esm/query/compiler/evaluators.js": "0ccf7503f360331210ffabf4a1003c96f904f831400ca2687f553f509a52f21b", + "packages/db/dist/esm/query/compiler/expressions.js": "eeb0c6dfca9871b4f7a1dd893c265e51e1b23d20400ac28579875446ac37ca87", + "packages/db/dist/esm/query/compiler/group-by.js": "ca935c6886fc9cb1574fc71e22efe688beceb36bd524a25a6aa29ae0bcf44dda", + "packages/db/dist/esm/query/compiler/index.js": "19667be77fa84da780beb102509c28d98abd365a330e8e7da4e9ac5a51154959", + "packages/db/dist/esm/query/compiler/joins.js": "af1bd496e41902c7e5b9759b4231d0487db2f67f8aa060b75fd326c7791c88b3", + "packages/db/dist/esm/query/compiler/lazy-targets.js": "04ed7aec7600a3ccf86ac28f06f8dd171770e56ca01a11b74d0b59fba6e083ea", + "packages/db/dist/esm/query/compiler/order-by.js": "e9844a5178d22908bf1ee6a723fe903d05110f6c333eefd8851a83165b39180d", + "packages/db/dist/esm/query/compiler/parent-routes.js": "bd0cf9a9781b321181ed3e2b6571d94dcbcd7bc784c53399b98051364c0fce80", + "packages/db/dist/esm/query/compiler/query-equivalence.js": "139c14fa078e5ba4c4a3d7490854c336870eea4c30f5960cd8f15980ceb00b57", + "packages/db/dist/esm/query/compiler/route-metadata.js": "6889f6b3dec56ef8fe5e6ee05e892dab1c22c0c0a13cbe09afa76774dae8a614", + "packages/db/dist/esm/query/compiler/select.js": "3f7d19d780395dd84c1b2b2c506e2b4f847a5a8a9442016bc5f72597f454af18", + "packages/db/dist/esm/query/effect.js": "59f37b190830c138368260801b09e7bc7a30e347fe36681b4796f6902c7e7c3f", + "packages/db/dist/esm/query/equality-conjunct.js": "ca0bee4e908fd4dbe52dc78feb172dca90492edca49cabe509c3cb4e4fd67fd7", + "packages/db/dist/esm/query/equality-value-identity.js": "5091c8c6fbb1e06127ef19db34258fd0d765b3c1b96c4a4f55b6fb553e37fcca", + "packages/db/dist/esm/query/expression-helpers.js": "5c957e7d0f23fb781aae8ead705404cf7bec3b1cd68db3fb46b356a1d25002ab", + "packages/db/dist/esm/query/ir-stable-identity.js": "a9cdba9efedc50caecaf7f8724f4146ab8d5f16d916f25707112a43cc36a0a8b", + "packages/db/dist/esm/query/ir.js": "9f2a67c11ecf08e1af74375eac7a603455e12ce56a75317376fd90b55193261a", + "packages/db/dist/esm/query/join-conditions.js": "0ede70428fc77b12d38c333b22767d629d50e88dca26af0c495c83c3308daa85", + "packages/db/dist/esm/query/live/bucket-facade-adapter.js": "daee317ea51844f450c893f7ce57e3383a0bf96c52e2bc4930d1aff951e5c7b3", + "packages/db/dist/esm/query/live/collection-config-builder.js": "93228d57a26e0e6dd5d51553c5fe10c9ed49f7803979ad21a76aa6805edec3b4", + "packages/db/dist/esm/query/live/collection-registry.js": "05e27e3d041d58aefc8820e523163b2e21ee53a3940680a0b468bb8e81e6d46d", + "packages/db/dist/esm/query/live/collection-subscriber.js": "11dc6621b866033008c2d6c9c2ba90ce36eb94d5f0f0ae3435ea55ecee736f3f", + "packages/db/dist/esm/query/live/graph-scheduler.js": "d3888f67b6e09ac29a3e45a120e3cbbceda49be7415eacaf0856cecf2cbc9e9a", + "packages/db/dist/esm/query/live/internal.js": "244a7f35268790408879ed8b1774fdd767b78829dc1a5ef6a0df4c2c372c0258", + "packages/db/dist/esm/query/live/materialized-pipeline.js": "6d98c2d4ee9412f5816c4151c14f73224b449738df183d0c82c37c08056c64c2", + "packages/db/dist/esm/query/live/ordered-source-loader.js": "63481aa7985a8e9de91f7e3499deb0e5d6f53d6abec6dd6614609ac1f1b5efda", + "packages/db/dist/esm/query/live/subset-demand-controller.js": "1163906179d8c68fbaf36507411c258ce664e8d4141f0a8c4b01f905f1cd1c27", + "packages/db/dist/esm/query/live/utils.js": "3df2bfd90ae95fda8c421e3131e0aaf90875e2cb1af514a09da42ec67e78f899", + "packages/db/dist/esm/query/live-query-collection.js": "f6024029a340a675df1e36ed180ab0d800ac0e697399a5b0ffdab392fdb87181", + "packages/db/dist/esm/query/optimizer.js": "2992bfaaf19a94828538613dcfffd71e4908952fd7c401d4fa01509c53776093", + "packages/db/dist/esm/query/pooled-live-query.js": "0c83e1139162d0314a0a6f1076911d4cf635877d6c9dbc89b8983d27cc90ac46", + "packages/db/dist/esm/query/query-once.js": "efa18058ba8d8abf2f201cf0a6418ab3f1c133d4d7464748f55c0ff8839150f8", + "packages/db/dist/esm/query/runtime-reference-identity.js": "3a5de8435888ede6b0edf799e5f0c192192aa1e0fad922450e9774ebceb4de18", + "packages/db/dist/esm/query/subset-dedupe.js": "06203c388ec4f1e525de8fe770775736222b62e273007e9a31bc37ba5fbab15a", + "packages/db/dist/esm/scheduler.js": "d9b4d34d6a4c656c0f38859426d39830ff62053ab47d22a14acc8b34bb6a8136", + "packages/db/dist/esm/strategies/commit-completion.js": "bd177de52002dceb9d28e02223da81b05c00ca5f72f5c06aa70699142904b263", + "packages/db/dist/esm/strategies/debounceStrategy.js": "88c73e9970201acbe24c3ea11632f2747f174cfb2840a53fdc4ba25224feffff", + "packages/db/dist/esm/strategies/queueStrategy.js": "abf15dc18ce9f481ef876478b0e2190188b960d76d7cedcf4f0c8b2a6818f9b8", + "packages/db/dist/esm/strategies/serial-pacer.js": "84c01c2f15ac57689b7f80a8ca0b152035cc2dedd8573e7e816b1a2bd0dfb08f", + "packages/db/dist/esm/strategies/throttleStrategy.js": "f646adf4f396ceddebca7580b398dc57edf1a22dfc2dd2e6c1eb607b412c0de0", + "packages/db/dist/esm/sync-persistence.js": "16292216a685c02ce1f3e1b2945a2409600e42d951c6acd19090dae99a38dacc", + "packages/db/dist/esm/sync-receipt.js": "c4365d1f36cff69c10194edef4903526fa85078cdf135172978075b5a3d56652", + "packages/db/dist/esm/transaction-commit-work.js": "c6838b1423ba8b0a1bb2ebf848fa37394416d52369d0d7ee5e8508d67f87066b", + "packages/db/dist/esm/transactions.js": "901116611280b5cbd71c3d9c49b6bad061174d90fdd0d2ab29f8cb66d5fb12d1", + "packages/db/dist/esm/utils/array-utils.js": "e8b8006009a956a5520822c9bb1ac8a9b13750613b0be937c2c2cb8d14c5d4c0", + "packages/db/dist/esm/utils/browser-polyfills.js": "620828c9fcf0df2181fed5d866a5ad20f1ac01ad5a296dcb04fd8be7012f6d75", + "packages/db/dist/esm/utils/btree.js": "2db68fc77ef20eb576502fd284668da1c9e04b18befc88dbf3d6842b722e75a6", + "packages/db/dist/esm/utils/callbacks.js": "0e5c79079dcbd00609aaeea1d769ae56fd881f4245740dfce408419773c599da", + "packages/db/dist/esm/utils/comparison.js": "19d37b5b97b84c1a19d331e277f47042e87ac01696d90a74fd3f76030d45e7ae", + "packages/db/dist/esm/utils/cursor.js": "1051977f9f8acbb6fb6884faf467c5c379cb54fdf0b02d22c9c1eb14cc52ca86", + "packages/db/dist/esm/utils/error.js": "a19d18998688b8a2ebd796bf668c694fe5f0c18fd4a4f8434f91589a7e436e40", + "packages/db/dist/esm/utils/get-or-create.js": "b1b71fa6f0e123e44f4908ea606ad6002515afbe204dd5841f72789f8fdaf3d3", + "packages/db/dist/esm/utils/index-optimization.js": "24ffd884fb0c24a49f614c5cc7fa9b37d9942bf59ccb12ccba07c7a0a328ea1c", + "packages/db/dist/esm/utils/source-record.js": "dcb726a12fb21ca74fafa5eb78ab7b83f1d18c2176fee2af11f430322ada6f48", + "packages/db/dist/esm/utils/type-guards.js": "a0327ad9ec010fa5f75a73b01b302c19433258db3ae23ce563a97eff8d981fa6", + "packages/db/dist/esm/utils/uuid.js": "fae77bf0bc610f2fe2e2b7b3ac289f48b572b223973780c6b895bb878c68e8f4", + "packages/db/dist/esm/utils.js": "ab6496511234ca87fd1c59c51c7b8f2f8b8601ea5d7ed15057f3068a88210f4f", + "packages/db/dist/esm/virtual-props.js": "76a0a0e70ee0f959dc7f4c2dac49bdcd24d8a7427b1cd59fb708d7bf47a7fde0" + }, + "runtime_source_map_sha256": { + "packages/db/dist/esm/SortedMap.js.map": "9fa31bea314653a026871313192f228eb75af82e8cec748e8041d1d7bc3d6978", + "packages/db/dist/esm/client.js.map": "33a90696c3b077207b8b1ccd57b1e9299f505a9d468466746066dfe3936c9a32", + "packages/db/dist/esm/collection/change-events.js.map": "c53651fdb6c5b229a2280d45af00a4ec969d5edd48d11608e96c8e813e109ceb", + "packages/db/dist/esm/collection/changes.js.map": "66ba53f09b63f0e668f7522297736919261a64518ec2c43b16eec770e413dbe1", + "packages/db/dist/esm/collection/cleanup-queue.js.map": "84777c1baefeb9e15bfad6f2144decb14ad0236895f8eb59d6721221aacf25e4", + "packages/db/dist/esm/collection/config-errors.js.map": "6bb878aebe57ff145f43dd2afb85c80041cff4b39546ae5e6eb3c7c1b7ec57ad", + "packages/db/dist/esm/collection/events.js.map": "4c035f44d2a25221e333af7799d8dbb06d8b69a12584fe6668f517c71ea87b4c", + "packages/db/dist/esm/collection/index.js.map": "2e50904cadd394695e45c5e82bbdaf1f22f29d6787e7ece5e225250aa721f438", + "packages/db/dist/esm/collection/indexes.js.map": "415a842ba3570ad1a6f341edc761c80b7c37a41c1dcc568121a854d95fcd91f5", + "packages/db/dist/esm/collection/lifecycle.js.map": "12486a4c6d0a7dcaca4489596e2e6da0deb1284c63538c3547773cfd1a1af3c4", + "packages/db/dist/esm/collection/mutations.js.map": "57b450f3fe34ff7c279e349338b525140fe287754763aa4e335cbae08f4374b7", + "packages/db/dist/esm/collection/state.js.map": "f11a3431edc53bcffebcd0bbd6c27af38c2a8c38e5b36e2d2b66b934031b835b", + "packages/db/dist/esm/collection/subscription.js.map": "73c1f1ca5d23cae10c45175164f5e2c59aa10138f13c86ed6ee775f1655765bd", + "packages/db/dist/esm/collection/sync.js.map": "cccc3eeceefdfebde3c092cc3944e23a2aae67eb0c57ff7f3a0694fec98d1d76", + "packages/db/dist/esm/collection/validate-config.js.map": "fd8cea162de4fe8ef094df6522aa8705b83c12fbfe8f9433ee556b3aa76d069e", + "packages/db/dist/esm/collection-options-brand.js.map": "006f1ec5a86b56a67c81e1450d129a8c18c2ff40aeedbac730ff344cac539248", + "packages/db/dist/esm/collection-options.js.map": "5e0b50e274db3185ed9b0c7192920a562c2d9669e57bdb4317ff27a0c7bc0bf8", + "packages/db/dist/esm/deferred.js.map": "c87f06ed0c6b0f229700d59008329205ff3fe7fd5466306982bd932ae273bfcd", + "packages/db/dist/esm/error-message.js.map": "1ddef42c2961d5d65ffd8bafd3a639431a539cf90da41cd5ee794710f3f2884c", + "packages/db/dist/esm/errors.js.map": "6d4e905f2f6c38ea74cef7cbb5fa6f46ccef861488bb57eb2096d3fe4e8fdd9d", + "packages/db/dist/esm/event-emitter.js.map": "b0b3a8588c21ee62b7d3f23d2dffe07ae67f3a451f5151b6f7dbc528c7d009f4", + "packages/db/dist/esm/index.js.map": "f8108774caf02d99844c09563d578fbc3b23ff1a41815163eb053569fa2d1544", + "packages/db/dist/esm/indexed-db-wrapper.js.map": "0203fa669e4b0329df4575a721ca2a3a170180209aa1e4ef6083223f9339c6e8", + "packages/db/dist/esm/indexed-db.js.map": "c29f3c555810dfc2a4b0c75c9ea1f7ba76ebae042d9380cf1f8c69a5471a20c1", + "packages/db/dist/esm/indexes/auto-index.js.map": "511e7a724df721c7decc03223fc765a289f06274cdd379b7de89383a2673d2e6", + "packages/db/dist/esm/indexes/base-index.js.map": "fd5416f04da3991df1fdca5605cec332b7eda9e2517962bbfa437ad10028b286", + "packages/db/dist/esm/indexes/basic-index.js.map": "8a1d3483240e52f21e82f22424b4168b4e17d7f0127e74d6cd2fb1abcc491645", + "packages/db/dist/esm/indexes/btree-index.js.map": "24afa9523a33e8c7ae06c0543991f83b454c9ca67e667a125e363bd3665b4c87", + "packages/db/dist/esm/indexes/index-registry.js.map": "5c0bbfbf82671306e89a15c7ef3bbb8a4813b268b4f317d75be63f88eb41a75e", + "packages/db/dist/esm/indexes/reverse-index.js.map": "633cce9cc9774fbe8854d1d9a338e26b3158107d1edbd0f2161fe952eb4700fc", + "packages/db/dist/esm/live-query-adapter.js.map": "26c5f830609eff6e54e4520eb0ec269a3aad375c2fc857a3ae77140e0179e7f2", + "packages/db/dist/esm/live-query-observer.js.map": "5475b3532cd994257fd71961200597b079078521b81440265ce455fbba901986", + "packages/db/dist/esm/live-query-options.js.map": "ad90acb33887c695fdfa862cd8d5d49e68cd4db37198db903b56c6382e83a7b3", + "packages/db/dist/esm/live-query-primitives.js.map": "41afad71afdcd23aada590c459cd4533d2737a7c28c21ebb5eb23e7fcf7e6386", + "packages/db/dist/esm/live-query-window-binding.js.map": "6c1bd8174569741a154f5bba07fd3a09f18391e599e42d4b9b697089aa60154e", + "packages/db/dist/esm/live-query-window-controller.js.map": "298367ea2b702476de2c969a4226821c6ccfa033d3b90947b330c21060d146ce", + "packages/db/dist/esm/local-only.js.map": "c8eace5d10617bb0c5e178db897b6b5905a51b3b1e6c10e3b1018b14aa08c373", + "packages/db/dist/esm/local-storage.js.map": "95e266c329925a1a2ce6c984f4509f6366d40fb3ef02f50998532eca55538611", + "packages/db/dist/esm/optimistic-action.js.map": "1ff3eaf6f6ae7e2a08f1988eac3f9f80b307cfeb61b7031579f19cee774d2207", + "packages/db/dist/esm/paced-mutations.js.map": "209978b76decd4e48ccd0eca467a8fe1878a0b24145f4903df2a30aa66fea200", + "packages/db/dist/esm/persisted-readiness.js.map": "b36b855729c7fe5e9cf49a0eaebaf66f48ec69e87acf762e21079907062d7c78", + "packages/db/dist/esm/proxy.js.map": "7e93b8d55ec84bfa6435b535abf642a4ea817750ad0af18bfb4b0841af5052ab", + "packages/db/dist/esm/query/builder/clone-query.js.map": "a458b5f869bccb52d83f5ccc2a022e617198b07d71161599925d2b179b22ce8a", + "packages/db/dist/esm/query/builder/functions.js.map": "7209aeb6b70bc74fc1631dee124bdba42525c9de6c8e63257a9cb75c67225703", + "packages/db/dist/esm/query/builder/index.js.map": "57f18c1d77caee866a05de3343ab14417dce54b5b3d84e484a90c64f5b305212", + "packages/db/dist/esm/query/builder/query-ir.js.map": "404592f23d86e39fed95862bcd6c3a40ea9899bf64ce2b3e454b7a246d799096", + "packages/db/dist/esm/query/builder/ref-proxy-identity.js.map": "b0a54507e2de7678cc902067c315216eddde48cb9de7bee7ecebe9ca89c88e22", + "packages/db/dist/esm/query/builder/ref-proxy.js.map": "c5c6ae0637ae9be5ff1a2e3cf54d8788f57baaa8c1ff6629c962444dfe7851c2", + "packages/db/dist/esm/query/builder/wrapper-identity.js.map": "b7b11e683600fd94d16e869cb38dfa250ff56eb7b34b4e95d20ac35b497004b2", + "packages/db/dist/esm/query/compiler/evaluators.js.map": "d68150a8ca3406a2960168688fed45f5e5fe07573802691e4bb400499977e6b1", + "packages/db/dist/esm/query/compiler/expressions.js.map": "f41f8b46979dce48936373f86cc990357a82699f51585aa7ff34fb284430a704", + "packages/db/dist/esm/query/compiler/group-by.js.map": "c677452c235bbb7b08ed8c380953304c2fe462c5ac5a9de7a22098f00b65f11b", + "packages/db/dist/esm/query/compiler/index.js.map": "619130f951479e75e3a261b7cfff523dadd61d491ff5f4b66d0919a4b0509f1f", + "packages/db/dist/esm/query/compiler/joins.js.map": "c2c321244483a65edb382c6f2c55cd7f395bd383671f1d6b63e07b9b8df65104", + "packages/db/dist/esm/query/compiler/lazy-targets.js.map": "a95673b8a853f10dbd239bf3612f4f644e582bc5dc0a7f12873898a7ce33d7b6", + "packages/db/dist/esm/query/compiler/order-by.js.map": "898ad1b7bd1bdbd1c2c0a8edbb78e7ce14811bb4a9fed3e148befc1127a9509d", + "packages/db/dist/esm/query/compiler/parent-routes.js.map": "e18ec4a4c12e4c4f2e176e07c47eb3d58e36cc82eb5fa6327c92106552c1d523", + "packages/db/dist/esm/query/compiler/query-equivalence.js.map": "0e569092d8666ed94fb82c83ec29c5e9663f5d1ae1afadb45856fc4278b5b19a", + "packages/db/dist/esm/query/compiler/route-metadata.js.map": "3bf70196b5be0b9dcadfaee25e7c4764874d76a50ae88c15486f64fa6aef65a9", + "packages/db/dist/esm/query/compiler/select.js.map": "b400ce63f5ce3a505045241de9b5d2b9cb1dccb3e2fbf04475c7d1204717b779", + "packages/db/dist/esm/query/effect.js.map": "0d8f6b6535196a2bc2038830616750b99efbb47505ffab786c7a7c018c296835", + "packages/db/dist/esm/query/equality-conjunct.js.map": "1bc30e55bad05f535c5dde6ff2e427ed80dc73139563652e9169ff78ed6e52cc", + "packages/db/dist/esm/query/equality-value-identity.js.map": "53e543a540f24c9bf28b549388e7b47e4d0dc2fe0a32d2b4c2fd42496f016d09", + "packages/db/dist/esm/query/expression-helpers.js.map": "552f3f643aaf98d1fa50328892bbff6c3cf166a788f1a9bed22450ea835a3e3b", + "packages/db/dist/esm/query/ir-stable-identity.js.map": "54629136d3760ce211cf87b7795dbe629795a59789d26b6b4b4c33f00c50da6e", + "packages/db/dist/esm/query/ir.js.map": "ccb4d95ae5caa7bfc320e2bc66ef54f685dbe867301c463e4793c80397c581a3", + "packages/db/dist/esm/query/join-conditions.js.map": "39d95b4fd518dce35318bbdf38cf3b9f105a4e3b5db15d4ff4048ce261a8caeb", + "packages/db/dist/esm/query/live/bucket-facade-adapter.js.map": "5346a9f6853231e91b63e7303be2ff234560ec0653c748f257fd103ea0b5fd41", + "packages/db/dist/esm/query/live/collection-config-builder.js.map": "7fc086bb59ab0dd80007cc16b7164a7a4c4d1306fc29ba2357e8ac2df67f2b09", + "packages/db/dist/esm/query/live/collection-registry.js.map": "4f4be3b283e3daa91838fa9ce44dbc11d29b599328d7db419319ed6fd7d9c8f4", + "packages/db/dist/esm/query/live/collection-subscriber.js.map": "1b1bec3ab4af5b8f568be4f298d5984ca74170929574b41ac7490ddf47b0eb32", + "packages/db/dist/esm/query/live/graph-scheduler.js.map": "f9d01ec493d292f27a6136816a61082939d7ff9b2915e73b9f04a58f573d48ee", + "packages/db/dist/esm/query/live/internal.js.map": "8f3093c6b396f3b3b0ebdaf9d330f23f71cc6ff9ad6813c598b590c497a56382", + "packages/db/dist/esm/query/live/materialized-pipeline.js.map": "9924a8437663a9ff914fc8e733903449f322b9fb9786a873b72f63e10aefd5b0", + "packages/db/dist/esm/query/live/ordered-source-loader.js.map": "eaa8b13f6b4d995d35579816e23e53041c925dc1f027271ce6bb99e89b3f67fe", + "packages/db/dist/esm/query/live/subset-demand-controller.js.map": "3c0ef841c2f6ce3f6c8c4e1f5ef6fe2b885b52140977f198479440b62dadb065", + "packages/db/dist/esm/query/live/utils.js.map": "0d1b870b1c6b91edcbb7d553fb4bf12122eb1331a56ffeaafac7312581c1443c", + "packages/db/dist/esm/query/live-query-collection.js.map": "f1ce1849148cd27103b5f0dcf55cb97a738d26f17706fe7685b7262cfc86e5ff", + "packages/db/dist/esm/query/optimizer.js.map": "fe4f220fd03842d9d518b44df4ffac8a03d9dc4c40fe3b5a1456ca05cb7c5a47", + "packages/db/dist/esm/query/pooled-live-query.js.map": "82bf9a5f6131a287cc09c05cd35d0f12910189f86ce2009529ecdb91cebf6bb7", + "packages/db/dist/esm/query/query-once.js.map": "cea3bbe5c87666103f83c8d02a8feaaf9f718f318b821f76ff5c2a5d77dbfea9", + "packages/db/dist/esm/query/runtime-reference-identity.js.map": "d06fece2e78c5ea3ae21e74ce22fb24a7422cd57632bc28d5cbd7292dd049dab", + "packages/db/dist/esm/query/subset-dedupe.js.map": "d86e0085d0097a37f35375a94d52063829177fa6ec2aefce3cdce46c42133e82", + "packages/db/dist/esm/scheduler.js.map": "d0ca54ee8e793d30ba55632a0f59d8c141724698b4d1f82236c70c7ef36307b1", + "packages/db/dist/esm/strategies/commit-completion.js.map": "0ccb2df9d5ed0561721f8459a6f029a031f774250aaaaadb7f68a5556bd4729c", + "packages/db/dist/esm/strategies/debounceStrategy.js.map": "8dcdddd38e78f32c844279e92845359b7a95f4b91cb6add26474b680e32e5421", + "packages/db/dist/esm/strategies/queueStrategy.js.map": "e61023e3380ae9594484c9e673b078454955b85ef8d37fd57b9ecdef1bdf6da2", + "packages/db/dist/esm/strategies/serial-pacer.js.map": "4e86b6c2203e220ab7096e2e624463c92c2bf6c8cb1098eaebad2df56f5e4a69", + "packages/db/dist/esm/strategies/throttleStrategy.js.map": "8abc6c79e822c3f6de0496b6c42bd730680f8b48989016e3f4257e25ab01a25d", + "packages/db/dist/esm/sync-persistence.js.map": "a6e395d89daebfc769642fa8bd00a0b4f3c9bfb4090a200d97f6637363d89df2", + "packages/db/dist/esm/sync-receipt.js.map": "037f7d6167e90947a5468a36757d4bd32dfe0c937d8b5f1ab7a83bd28c23f756", + "packages/db/dist/esm/transaction-commit-work.js.map": "10ae9167e5a3a0b030a5a9cb34b347d4bb6f719e023a84db6a0a9da85f39accf", + "packages/db/dist/esm/transactions.js.map": "d071193784fd02d4365ba1db134d53c13932fdfbeceab2e300211335e6582873", + "packages/db/dist/esm/utils/array-utils.js.map": "543f01857219d2625c3dab6f7c8da41e45ffc29f34643681f8be3d42b61099b6", + "packages/db/dist/esm/utils/browser-polyfills.js.map": "259ca9a616f32a59ae240606903456aae505856a03b01d7f0c5133fe724316c5", + "packages/db/dist/esm/utils/btree.js.map": "3d96225772c3794f2e65962fbe29cad56738f45a382b7a4e40f1370dcf780830", + "packages/db/dist/esm/utils/callbacks.js.map": "61b12884fd3fb07d404d53734577a254af387dde4d178862901ed43bf154d713", + "packages/db/dist/esm/utils/comparison.js.map": "4c3232f26a3487b36546b34c0af682b0880282c63fa021da36b070ba0506ec19", + "packages/db/dist/esm/utils/cursor.js.map": "50e78e47699a68d3ac3828eb37760f3d8331e25ac2bd833b74796c2f121056f3", + "packages/db/dist/esm/utils/error.js.map": "302fbea3014e7d353c8e19456d5c7b930876a738e868945fd0eba88498886626", + "packages/db/dist/esm/utils/get-or-create.js.map": "049135923c3a435c8a6435c2330978b7272b1f954481a71ede0450453b3c7d1f", + "packages/db/dist/esm/utils/index-optimization.js.map": "36d3be1f0e57af2e910d9d61f01f4757fe68637e84e0f59a360718b197505d97", + "packages/db/dist/esm/utils/source-record.js.map": "e52ab96575ec8e4fb99084e788ceb8e2f06451369503a5d3792a32b27f7311fe", + "packages/db/dist/esm/utils/type-guards.js.map": "0b96a21f6d742d02da8e62350804eb0e494dc2c10d90f03f29750988c843befc", + "packages/db/dist/esm/utils/uuid.js.map": "439824094f7d0ab0ce52e065cc80e717bac86ba96717435f0befc616495b83aa", + "packages/db/dist/esm/utils.js.map": "a635c14883c73c14c69a03637c408429e951b99fc3055062915245a2359f9c04", + "packages/db/dist/esm/virtual-props.js.map": "73b6876085e95170f2a932adddcf3c333175b3ff80ba00340ebcc150afc6358a" + }, + "verified_runtime_source_sha256": { + "packages/db/src/SortedMap.ts": "dbbef7d2842d265cf5268f3ca8c512b0b79ea927135116d3fc4e08ea2799856a", + "packages/db/src/client.ts": "fdac0f151242b349c53e3a8bb0e9788cc432b35f306d2ae91d74bf832fc97f38", + "packages/db/src/collection/change-events.ts": "754f897ba843d004eaf5bfe3f047fc53aa24eb49a16ae1eae4073e32ec17ba49", + "packages/db/src/collection/changes.ts": "c6deb4b04891feec0f2c4368ffa9ea652d12593f4e7ddf4edbe392a706f314df", + "packages/db/src/collection/cleanup-queue.ts": "d6d24ba9216d9dfacf57623d14e61861b044a23cac1b8e475dbfe71795a0c84d", + "packages/db/src/collection/config-errors.ts": "ffe046ad75542c863e1781c192191479cdcb68e7dcfaf92c9ce180ee48522cc0", + "packages/db/src/collection/events.ts": "fd95280be3bdd9a1177d4d57b425f484b9daf06b4b257ed28d6b17e3ef1bd132", + "packages/db/src/collection/index.ts": "458e71b1478a6150ef9c1854fb7df483cb32466217cec050993f21befcb1dba0", + "packages/db/src/collection/indexes.ts": "1a0b6633e5f5f24c1b29fe8e544ee3c52df6e886d2a61c8fe88750f3f811520e", + "packages/db/src/collection/lifecycle.ts": "5bb9027353ff30b9c467c1c3c749e739aef741ce1762c770ccd0ac90502763b5", + "packages/db/src/collection/mutations.ts": "735bdd4872c97afbe6d7333d42402635bdb54c914e6b5cacf703444fbf933f31", + "packages/db/src/collection/state.ts": "3c6caf3829455db5e6c037bf56e06c7a3b0e6abebc35368da0dbf886b4729e76", + "packages/db/src/collection/subscription.ts": "c446932a8a1f8b2efd79d00846f1a110e65a6c6df75d2b24658053e6964b530b", + "packages/db/src/collection/sync.ts": "4ff2e29f8fb27af6e71a46f959ad764a0d9dbd390bfba66604ba1c1836676add", + "packages/db/src/collection/validate-config.ts": "10bc3f83c357cf8b835694c73df9adf413c6190f924256786f17e123a688489a", + "packages/db/src/collection-options-brand.ts": "93df3fc8d90531ca926c6e9ca48653dbda9259cfaaade9c23cbbf35131b2e1ed", + "packages/db/src/collection-options.ts": "0b0f9713312f9554f62c020dda3bae8291303dbeadb85adfee76515a28442feb", + "packages/db/src/deferred.ts": "9c99553d7ab92ac89d377cb3382a9dfbdf592635c87b86eac8aa1f18efc4da68", + "packages/db/src/error-message.ts": "9ab4a64436c3db3a61f3a84b5ccff11359ccdf5676c81b81dc3be9db9c1dff13", + "packages/db/src/errors.ts": "ee54c0fb19ef95630e833b62562a4a2310ebd79337746355b8f67676cce0e416", + "packages/db/src/event-emitter.ts": "1d8775f3c2dbc9c833f03d2b9bb0d55d1606419e43d661064cc95cd91eb187e8", + "packages/db/src/indexed-db-wrapper.ts": "216fcac76c01b1af8363cda8a93678f53cd2cd58c9df918cd3d56d2a1f25fd11", + "packages/db/src/indexed-db.ts": "4c9960e7020389ab784f3ea6ce3fa1684b36ad1f6aef28a100cc15517374832b", + "packages/db/src/indexes/auto-index.ts": "d226516a7514c40e83010ab211020adba17965af96bd6d3b96e88989c6f31b51", + "packages/db/src/indexes/base-index.ts": "63e35c6bf011ad361882509a521407acf29b9404ce4e810a8f4d54b50c0e6685", + "packages/db/src/indexes/basic-index.ts": "9a128909d6df2c6d5b9ef07295a55f9f587170721cb7c6a652dd9c43eb674633", + "packages/db/src/indexes/btree-index.ts": "b006f65cf1c2ab14004e7311f4ac4b89958e919fb8439cd9ca25090789e37186", + "packages/db/src/indexes/index-registry.ts": "d44e34c9e5d90db56dee7f91e402f2547d91647ae100cc4752bc13bcd49e2719", + "packages/db/src/indexes/reverse-index.ts": "ebf2d22eb0eec10acb4df154488862243ec20be117bc069fe307fc991853dd0b", + "packages/db/src/live-query-adapter.ts": "cc6e68e05afb876a46989357304a2e9315cac918d2d7623e4272c3a4ce3b470c", + "packages/db/src/live-query-observer.ts": "37f3bdd904af1ba51d6d8821d4711b4e29789e307363590972e52a50e77a4d0f", + "packages/db/src/live-query-options.ts": "8ba7980c64de78500e56b3c822e19e8e451d77ee50352beaaf89644f47c3f411", + "packages/db/src/live-query-primitives.ts": "16fa17f9cefbb2dad98fda23651d792f8c8cd5bb4b812bd1197de7ae8f1f99c1", + "packages/db/src/live-query-window-binding.ts": "7564bac73ec3aab26dbc4c4ed44bc972f108fc6241c158eb63c8b7ae22b67747", + "packages/db/src/live-query-window-controller.ts": "ea439e6b6f0ce1bdd03f259a39d6d912024845e8343d013b67a7b2251740c8fc", + "packages/db/src/local-only.ts": "06d516a29e6f72b87f08c83f1fad2454dbc1cd3a3d296562f74fa5eb5aaec408", + "packages/db/src/local-storage.ts": "62331d402b5a7b958617e45ddb9de90a358f2fb1645ac589c5d412deb079cb5f", + "packages/db/src/optimistic-action.ts": "3b3d98ec0561927c301c6979256bbcd98faf0ee41b2b6690abe1c189eeaf4fec", + "packages/db/src/paced-mutations.ts": "ea9239b269c2622d23cb2fee6df2895534d64b43fb245f8160e054587b9b5458", + "packages/db/src/persisted-readiness.ts": "3fdff536570ed25347928bfac3043482d87a1506ea8bfd4d2c4e7f4c532fb598", + "packages/db/src/proxy.ts": "97fd4841cd8532a8bd2f08197d0d755cf348a50e5df0b2f4705079895f2a42c1", + "packages/db/src/query/builder/clone-query.ts": "7265db09e09e441d598d5b5774759f4ece6dc1cf5d5723d49d1c342a36e3798d", + "packages/db/src/query/builder/functions.ts": "71ff875be3b94602be517fd03d2ffd0fc1866a189a4849403623d9865e35227e", + "packages/db/src/query/builder/index.ts": "c658662a2ff50d85a7a1d662b728873e16a568d58e38248bbc57d03f884b463e", + "packages/db/src/query/builder/query-ir.ts": "6aa35045e8a83c63a68e3af2de951879aaa2b91c132606db6d0bfd32247af108", + "packages/db/src/query/builder/ref-proxy-identity.ts": "909d40e4425647ae58850decd6891fa7f6aa4690cd76e82c3ced13b70dc93b10", + "packages/db/src/query/builder/ref-proxy.ts": "da99a9d725676d7c29b1bc2c10913465389c6eb442ca651d06ab658a2561d0c1", + "packages/db/src/query/builder/wrapper-identity.ts": "3053d30505c57f0eb16cc573e196291e617aff7479151508bbaa397fa3757f57", + "packages/db/src/query/compiler/evaluators.ts": "499cb252c6e2c166cf626bf88c6c08ced102020558bdccab46a6d12acd52ec92", + "packages/db/src/query/compiler/expressions.ts": "70925f2d29f57aec189d0e7f485d0531534e3482f0696a5e9a1141d8cf36c42d", + "packages/db/src/query/compiler/group-by.ts": "f30cb4ebd6940dd4a3888fde633e16f952225e4f9d35be4e450d6ad912a22bc7", + "packages/db/src/query/compiler/index.ts": "0d53aa7d27fba0e0f472af55404adbc437e69a537aa2f529710e81d08b16944e", + "packages/db/src/query/compiler/joins.ts": "8f0aacfc3e09bbc2672cc21af75ac8b27e1f957a47ee8e36ce3b451137beff08", + "packages/db/src/query/compiler/lazy-targets.ts": "6949ebfac1f0d76b64dc9a3c1cfee87490702a21de235c320fdd7aaa02b77587", + "packages/db/src/query/compiler/order-by.ts": "271b3195a4d24cc0faf9c19261a1e18cd91eea8794ab9e611257929c4e3a880b", + "packages/db/src/query/compiler/parent-routes.ts": "d59e9d64534c53bb58349f07c121158adc9b37cac7a0b5150493e6368442c18b", + "packages/db/src/query/compiler/query-equivalence.ts": "7171fa0f1aa960297ba1269193ed3527967d78ebf1c19ef03544604280aa4480", + "packages/db/src/query/compiler/route-metadata.ts": "53283291ce25656339492bff678b8742a7fdc7b60c49d3b7f7015569e5c06788", + "packages/db/src/query/compiler/select.ts": "a4b60959e37ea674bfac8f3cc4076c35facb9378891f72603c309e92c2882760", + "packages/db/src/query/effect.ts": "013d653074e139caf2e2089574c3a985c7f408c9434d2d70d7c3b2dd9cc82258", + "packages/db/src/query/equality-conjunct.ts": "02041d880376b46814ab072747a9b0493e24cd4bd72df8389726780e535cc584", + "packages/db/src/query/equality-value-identity.ts": "a7b65759c20ebec4d83c47ec744bbd382da9a86ffa4c9e59028827127081d016", + "packages/db/src/query/expression-helpers.ts": "15d6635f8b28dafee0ca0afe2e8598545f09995a5b992798916d789167383019", + "packages/db/src/query/ir-stable-identity.ts": "2369d6ad584c2546f4fdf2051055a3bfa9bedb0c53cd8ff3d86b2b2a86cfd80e", + "packages/db/src/query/ir.ts": "9b319bd3728640d2ab941b167ea16b09d721ccff112647f597c9761dd43b5ada", + "packages/db/src/query/join-conditions.ts": "c2659d14f7a15203343de19b66cb83195c7ab5c26f953b52bbf6f40bbe012802", + "packages/db/src/query/live/bucket-facade-adapter.ts": "cbd6ea77a9d103fea6253e20656e31ff41ac353d134cb0c49710c56ffdd4b3c3", + "packages/db/src/query/live/collection-config-builder.ts": "aa55faec9bc8db4597357989d9466e4ab030e743ee6a197691807b775f0f8440", + "packages/db/src/query/live/collection-registry.ts": "c24da0f80292e4fa22370711cdf66e52ec44989f8fda50430025aa5b25510e65", + "packages/db/src/query/live/collection-subscriber.ts": "74e97fe498b44446d5ee21912175afab6a3e58d2787e04e890a26771d5c8e1a6", + "packages/db/src/query/live/graph-scheduler.ts": "bdd37b3814814a2bf32fa55422a2fb039107d04cbf4fbd79df67056c5bc4654b", + "packages/db/src/query/live/internal.ts": "3aa788cfb3647e2758ff9befe18e4503bc68797fd97d55dc5c3905db9a7145fc", + "packages/db/src/query/live/materialized-pipeline.ts": "9e0ad27c197c9571e242c7a7ccb286739ecdd748b25f00f4f4f8aae4f0894407", + "packages/db/src/query/live/ordered-source-loader.ts": "95ea59a251cd540406d3dd2dac1ba85f41d1f6422c4ffd44f973d9ea4ca01e18", + "packages/db/src/query/live/subset-demand-controller.ts": "28e49884847544356975afd781bb2d485844c2f43f9f33097aef28d06ba2a1d0", + "packages/db/src/query/live/utils.ts": "00d2a0462ea2a997f50d104f641e9837cdf1341fc763116a0df773f6888f1646", + "packages/db/src/query/live-query-collection.ts": "d9fa71c30a1dbb69d58e2e11c2ba4acbeaa727f8d1ee70ce3d225be08ce7224f", + "packages/db/src/query/optimizer.ts": "384be668674194cb69a3b6165ffc97bb3b7edf066e107c444f36f6bbfa43571c", + "packages/db/src/query/pooled-live-query.ts": "d2b9d1c77cb7b2f9fa0c0ed6c546e0738390d20207ebde466636d0c95f459554", + "packages/db/src/query/query-once.ts": "1399a24809ce83173f0d952ab35620bc4430874cb96ab14229495fad3ef57478", + "packages/db/src/query/runtime-reference-identity.ts": "c1c695e80771ab545e58254381333bd24469813d69f448106df2dc5a5de1db48", + "packages/db/src/query/subset-dedupe.ts": "c535bb2a38db491d1c11024c88c71dc1664a59bae3c19f1f5bec35d37d5b99de", + "packages/db/src/scheduler.ts": "b21cbbdc088b3c1dc4840b7db857ee7f262e6958dc8bc300398ad8e93da4f01a", + "packages/db/src/strategies/commit-completion.ts": "242b52fc7a041f5f9d17172c356dc4e1ec06f2cbeaa85ba5995294356fba9b15", + "packages/db/src/strategies/debounceStrategy.ts": "8ab894803575f1cfab85939adbf67637342bf2e230e34d0db9bb642f205cdce5", + "packages/db/src/strategies/queueStrategy.ts": "c21794ea5707e75de082b87c80ee5a19fd8b1752fd7725adf7435b377a5e4196", + "packages/db/src/strategies/serial-pacer.ts": "63f4aa8f7aacd4bbe5cc3d56173fdb9231fedea68bb721bb2d420cf2303ec679", + "packages/db/src/strategies/throttleStrategy.ts": "028b5a000cc5a49a0176a42f0bb0c5cbb0a71849e543b523b080942947f40cb7", + "packages/db/src/sync-persistence.ts": "eb64401db52d9d2f269e5b6d92f85ebb5d4cfe6479dd1d4832918cd7b9dbad6b", + "packages/db/src/sync-receipt.ts": "5ab4074603d96c23decd4fae499ed55f3a6ee5077e379f370d9b76fd90565399", + "packages/db/src/transaction-commit-work.ts": "b3c341c01f7a8b1207683fb3388b66b1173481f43ea59160e1922778e64872fe", + "packages/db/src/transactions.ts": "60acaa21a7c158e2617bb382778bec1aa2b5fcaff7d527ab902e47b364d140d7", + "packages/db/src/utils/array-utils.ts": "a151c6b1779cf975e85e53a7b2328865889738df1f0d0fc3b8a8e466d15911f2", + "packages/db/src/utils/browser-polyfills.ts": "b3212d22af7254eb8c286d89393eb043324abd0b5a7ddbc8eaaa505e292b437b", + "packages/db/src/utils/btree.ts": "e2172d757bfdaf3e528fe94864876fac9de24a66302ed1930d108daa1f525b1e", + "packages/db/src/utils/callbacks.ts": "da4d6fc8d94bf93d07442eefbebd1ec0c183ef559fa7eebceb51b3652816708a", + "packages/db/src/utils/comparison.ts": "ed8c22f7315f0119025d47a0ea6432bab974c618665b167012c6c12da2287449", + "packages/db/src/utils/cursor.ts": "b728649d395a8e4e61cd6a63479be92ce2d4d4a08dc73beb549b92dafec5181f", + "packages/db/src/utils/error.ts": "133924e3809af4a69826c08774455f12f10aba675cbc92a4298525868bba4ece", + "packages/db/src/utils/get-or-create.ts": "eb56ba9e1a6eddecc8241062cb9a4ea0afca7437fbb50f0404de1276a611ee82", + "packages/db/src/utils/index-optimization.ts": "034310477e43f29bfe3493a56ee3b3524378ae3f941f4a452e6a15b9aec24425", + "packages/db/src/utils/source-record.ts": "48023571bd2e850170b78a34a3a9d14940d97742f510f96cad4baaf4a75694e3", + "packages/db/src/utils/type-guards.ts": "b7a63accd5abd4dd5c93bb451650fc962a2fdcc3981e70f0b7b373ebeb5f07db", + "packages/db/src/utils/uuid.ts": "0365c5c02b46eabac53562dac87b342656e2774b36385a094c16b3a9db3a5ca1", + "packages/db/src/utils.ts": "67e7143cfb324dc8f97c310679730682ecfb8088c2dc0d9e0efde6bc57ff9c76", + "packages/db/src/virtual-props.ts": "6add3fb9226251bf4dddadc7c5227469d4183a4df7799047f2fdc6b51ef4d9c0" + }, + "production_files_changed_this_round": [], + "scientific_models_changed_this_round": [ + "packages/convex-db-collection/tests/lazy-paging-model-oracle.ts", + "packages/convex-db-collection/tests/lazy-paging-receipt-model.ts", + "packages/convex-db-collection/tests/lazy-paging-version-model-oracle.ts" + ], + "prior_frozen_artifacts_verified": { + "convex-lazy-backend-concurrency-evidence": 416, + "convex-lazy-interleavings-evidence": 143, + "convex-lazy-paging-strengthening-evidence": 74, + "convex-lazy-paging-loss-audit": 9, + "formal-design": 250 + } +} diff --git a/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-boundaries.md b/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-boundaries.md new file mode 100644 index 0000000000..2ca5d2034d --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-boundaries.md @@ -0,0 +1,223 @@ +# Convex lazy paging: receipt ambiguity and partial-window boundaries + +Starting head: `8c24922026fb4a714cb54b992ac85faa2e876742`. +This record supersedes the current-source bindings of the earlier receipt and +version records for the files in its [source manifest](2026-10-27-convex-lazy-boundaries-source.json). +Older records and frozen evidence are preserved. Changes remain local; no +commit, push or new TLC run is claimed. + +## Law and scientific account + +Authority is `LoadSubsetOptions.refetch/signal`, `LoadSubsetFn` and +`SyncAppliedReceipt` in `packages/db/src/types.ts`, the ordered-prefix contract, +and publication/cleanup laws 7, 9 and 14 in `query/live/ARCHITECTURE.md`. + +Successful or canceled accepted work owes its actual establishing application +receipts even when its row values equal an already applied relation. A later +revision may already be visible when an earlier receipt observer runs: the +actual receipt proves application, not historical equality at that later cut. +A conforming unchanged revalidation may accept another receipt or need no new +receipt. The model does not prescribe either batching choice. + +A partial source may retain delivered rows. Its entire retained base need not +match any complete server revision. The promised live ordered window must match +an independently authored scoped relation and advance monotonically through +compatible offered revisions. Old complete observations may stutter; revisions +may be skipped. Every applied base fact separately needs provider provenance. + +Cleanup permits pending actual receipts to reject as abandoned. Abort and +ordinary provider error do not grant that authority. Calling cleanup alone does +not discharge a pending receipt; the driver must observe actual rejection. +Cleanup makes an old dependent graph terminal with its last complete value. A +fresh sync run may serve a fresh graph without reviving the old one. + +The task-local `laws.md` records these predictions before model edits. In the old +receipt model, cleanup abandonment was unrepresentable. The extension retains +pending/applied/abandoned receipt state and cleanup authority. These distinguish +cancellation while work is pending, lawful cancellation after actual cleanup +abandonment, and success after application. Abandonment cannot provide applied +facts for success. This is an authority-based extension, not an expectation +changed to fit an observed implementation. + +`LazyVersionModel` now distinguishes relation observations from ordered sequence +observations. `expectedLazyRows` independently projects authored relations into +the promised window before offering those sequences. `LazyTerminalGraphModel` +is the existing last-value/cleanup judgment moved into the shared model owner; +both controlled and backend receivers use it. No cursor, page chain, queue, +sealing state, SDK identity or transport phase enters these scientific models. + +## Receiving paths and grammar + +`tests/lazy-paging-revalidation-oracle.property.test.ts` uses the native public +paginated factory, real Collection/core receipts, coherent controlled SDK caches +and real optimistic holds. Each isolated explicit revalidation attributes only +commits actually made inside its synchronous invocation. Equal-valued fresh SDK +objects witness an accepted idempotent batch even though every fact already +applied. A newer accepted revision supersedes it while held. One caller aborts; +its peer and a cached revalidation must also obey their actual obligations. +An additional unheld reactive revision exercises the ordinary callback path. +Actual callback snapshots must witness the held-drain and reactive latest +relations; manual checkpoints cannot supply those witnesses. + +The bounded grammar has 5–11 rows, page sizes 2–4, both order directions, short +text and either canceled caller. Every history runs both SDK shapes, even if one +fails. Its fixed/random campaigns each run 30 histories: 120 SDK episodes total. +Each of the eight declared states reaches 30 histories in each lane. With the +run multiplier at three, each lane reaches 90 histories, totaling 360 episodes. +The current implementation exhibits zero-new-receipt cached reuse in every +successful episode; this is measured separately, without requiring that choice. + +`e2e/lazy-boundary-oracle.test.ts` supplies real SDKs, opaque server cursors, +atomic server edits, actual query errors, actual application/abandonment receipts +and a real dependent graph. The ledger authors expected relations before server +mutations; an independent unpaginated query verifies the final server fixture. +Only genuine SDK reads enter the separate provenance recorder. + +Its grammar has 18–24 rows, page sizes 2–3, initial limits 2–3, a growth window +at offset one with limit 7–8, short text, either canceled caller, and two or three +accepted revisions during the growth hold. Each history: + +1. Proves an unread tail and a partial acquired source. +2. Holds application while one atomic transaction deletes, inserts and moves an + unread row into the promised window, then drains to its exact result. +3. Starts larger-window acquisition, proves its promise still waits on an actual + held receipt, and accepts later membership/order revisions before release. + The grown result must be exact while a separately witnessed tail stays unread. +4. Acquires full coverage for two callers, proves shared held establishment and + no missing required facts, aborts one caller, then causes an actual reactive + server query error via the test-only control document. +5. Requires the canceled caller to keep waiting until application or cleanup. + Cleanup authorizes actual receipt abandonment; the old graph stays terminal + while a fresh run and fresh graph recover. + +Every original-graph change/status event and every base publication reaches its relevant +monitor. Subscriber exceptions are retained and surfaced at explicit checkpoints. +Polling stops on a retained semantic failure before it can become a misleading +timeout. Four matching fixed/random histories per lane give sixteen SDK/backend +episodes; each of ten declared states reaches four histories in each lane. The +deeper campaign has twelve histories per lane, giving forty-eight episodes. + +Reconstruction: minimum-row/page/window cases are the captured shrinks below; +order reversal, caller choice and revision count reconstruct adjacent schedules. +Ablating fresh equal-valued acceptance loses content-independent receipt debt; +removing the unheld callback loses the transient-publication challenge; removing +held demand growth loses mutation-during-acquisition reach; removing query error +or cleanup loses their authority distinction. Larger source bounds retain a +truly unread suffix after growth. Nearby excluded histories include abandonment +under ordinary abort, source acceptance after cleanup in the ended model, and +success using an abandoned receipt. The executable model controls reject them. +The backend family excludes ties and edits during an unresolved prior edit; +other owners retain those independent profiles. + +## Calibration and replay + +All production sources remain unchanged from the start of this round. Correct +production passes the extended receivers; no product-defect RED or repair is +claimed. In isolated local Git clones overlaid with the current source, two +production decisions and one separate publication perturbation are challenged: + +| Calibration | Intended comparison | Seed/path | +| --- | --- | --- | +| Clear pending visibility when an unchanged cached result arrives | `receipt law: equal settled before receipt-2 applied` | `20261029` / `0:0` | +| Iterate the new keys instead of the previous keys when emitting deletions | `lazy version: mixed revision or public rollback`; deleted rows contaminate the grown public window | `20261029` / `0:0:0:0:0:0` | +| Deliberately preserve and replay the previous native publication between new and corrected publications | Same version law; both SDKs report `finalMatchesLatest:true` while retaining the earlier violation | `20261029` / `0:0` | + +All three fail semantically through both SDKs, shrink, directly replay with exit +one, and give GREEN after byte-for-byte restoration. The third is a native +publication perturbation, not a minimal production decision mutant or provider +failure. It exercises actual core writes/publication rather than injecting a +wrong answer into the scientific model. `calibration-final-v5` contains the final +patches, commands, hashes, source bindings, REDs, replays and restored GREENs. + +An earlier candidate replayed the current result cache. It survived on the +exercised paths because explicit refetch and published-range callbacks invalidate +that cache before a changed relation publishes. No global equivalence is claimed. +The retained-publication perturbation supplies the distinguishing wrong output +without weakening that invariant. Intermediate calibration directories are not +final-source kill evidence. Port contention, a wrong command working directory, +and shared coverage output races are preserved as setup/harness failures, never +semantic rejections. + +Comparator controls additionally reject abandonment without cleanup, canceled +settlement while a receipt is still pending, success using an abandoned receipt, +wrong window order and rollback after a newer observation. Positive controls +accept zero new receipts, additional actual receipts, coherent skipped revisions, +old complete stuttering and lawful retained base facts. Existing independently +varied response/commit partitions continue through the same receipt model. + +Commands below run from `packages/convex-db-collection`: + +```sh +pnpm test:oracles +CONVEX_TEST_BACKEND_VERSION=precompiled-2026-10-10-72ce13c pnpm test:e2e +CONVEX_LAZY_ORACLE_RUNS_MULTIPLIER=3 pnpm exec vitest run tests/lazy-paging-revalidation-oracle.property.test.ts +CONVEX_TEST_BACKEND_VERSION=precompiled-2026-10-10-72ce13c CONVEX_LAZY_ORACLE_RUNS_MULTIPLIER=3 pnpm exec vitest run --config vitest.lazy.e2e.config.ts e2e/lazy-boundary-oracle.test.ts +``` + +Direct replay selects the native file with `CONVEX_REVALIDATION_ORACLE_SEED=20261029` +and `CONVEX_REVALIDATION_ORACLE_PATH=0:0`, or the backend file with +`CONVEX_BOUNDARY_ORACLE_SEED=20261029` and +`CONVEX_BOUNDARY_ORACLE_PATH=0:0:0:0:0:0`. Correct production is GREEN; apply the +matching saved perturbation/patch to reproduce RED. The scripts discover the +native family, and the existing browser CI job already runs the backend family +via `test:e2e`; no new scheduler or background automation is introduced. Run +backend commands serially because the local CLI checks its default port across +checkouts. Concurrent native runs need separate coverage directories. + +The full package command passes 108 runner cases across eighteen files, including +four type assertions; the complete backend suite passes 22 cases across seven +files. These are runner counts, not counts of independent oracles. The new oracle +reach and receiving episode counts above are the semantic evidence. TypeScript, +changed-file ESLint, oracle lint and diff checks pass. The dedicated test tsconfig +fixes Vitest incremental source-root errors without disabling typechecking or +changing production build configuration. + +## Oracle-review self-audit + +This round uses the oracle-review skill for self-review; no fresh independent +review is claimed. The raw evidence and this record bind the reviewed source. + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | API/architecture authority and bounded receiving limits stated above. | +| ORC-002 | Scientific facts, receipt debt, cleanup authority and authored windows are independent of pager mechanics. | +| ORC-003 | Opening contract, shared models, grammar, real drivers and event comparisons are directly named in each executable owner. | +| ORC-004 | Bounds, reconstruction, ablations and excluded authority states accounted for above. | +| ORC-005 | Actual factory/core/SDK paths, held-growth promise, genuine query error, actual abandonment and callback witnesses are reached. | +| ORC-006 | Two decision mutants, one publication perturbation and separate model controls reject at intended comparisons. | +| ORC-007 | Matching fixed/random budgets and checked shrunk seed/path replays. | +| ORC-008 | Cleanup authority and three receipt states change legal abandonment/settlement; sequence versus relation equality changes allowed order. | +| ORC-009 | Shared glossary vocabulary; isolated-call attribution and projected application fields are driver abstractions, not pager states. | +| ORC-010 | Retained callback failures, original nested causes, both SDKs after a failure, and cleanup aggregation preserve the law failure. | +| ORC-011 | Row equality as a proposed substitute for receipt application is independently challenged with actual core receipts; scoped array recomputation and real server order challenge stale/deleted windows. No new second pager is used. | +| ORC-012 | This versioned record, source manifest and frozen evidence distinguish final calibration, intermediates and open cells. No broad bug-class closure is claimed. | +| ORC-013 | Equal already-applied facts versus a newly pending actual receipt, pending versus observed-abandoned cleanup, and partial versus whole-source judgments have distinguishing witnesses. | +| ORC-014 | Actual server supplies partial mutation, query-error and cleanup premises; equal-valued SDK emission remains explicitly fixture-scoped. | +| ORC-015 | Required eight native and ten backend reach states fail on underreach and are recorded per campaign. | + +Self-review repaired two overconstraints before final validation: requiring +cached reuse to create zero receipts, and requiring a graph to stay `ready` +during ordinary provider failure. Neither is promised by the contract. It also +strengthened growth from a potentially cached window to an actually pending held +acquisition, and added unheld reactive receiving. None of these repairs weaken +receipt application, coherent publication or cleanup terminality. + +## Limits and preservation + +Native idempotent attribution is limited to isolated synchronous explicit +revalidation against coherent controlled SDK caches. It does not establish a +real backend must emit equal-valued query updates, general asynchronous receipt +attribution, or reentrant commit listener ordering. The backend family uses +ascending unique ranks and sequenced atomic edits, with mutation overlapping +held growth. Arbitrary nonquiescent schedules, error before acceptance, stale +real-SDK callback injection, authoritative truncate replay, rank-tie changes and +React hook/render concurrency retain their existing owners. The controlled +failure grammar remains broader than the backend receiving subset. No finite +campaign or deeper run is an exhaustive temporal proof. + +Seven Convex production source files, the two existing install-workaround files, +and all prior frozen formal/audit/interleaving/backend evidence remain unchanged. +The pre-existing uncommitted local override remains +`baseline-browser-mapping` 2.11.32 → 2.11.30. No dependency install occurred. +Task-local raw evidence and source/runtime snapshots are frozen separately in +`outputs/convex-lazy-boundary-strengthening-evidence`. diff --git a/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-interleavings-source.json b/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-interleavings-source.json new file mode 100644 index 0000000000..be89fdcbb9 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-interleavings-source.json @@ -0,0 +1,50 @@ +{ + "starting_head": "8c24922026fb4a714cb54b992ac85faa2e876742", + "binding": "Current working-tree SHA-256 contents; excludes this manifest and unchanged local install workaround", + "files_sha256": { + ".changeset/convex-collection.md": "c7ad56ad983fe653f0054f0a82fcd47cad46c1d59087d62d79a21ffb077eb324", + "docs/collections/convex-collection.md": "3901d5d040f8e101369f1a16a160d4e5d0df391ec48408f99d995191ad013105", + "docs/contributing/oracle-coverage.md": "4a6b2059732a7d25aa7c3e585e7ff4ee9c93b54f33ccaa4f5a08964f3c6c6151", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-interleavings.md": "f105899875a4129b7d816cacc1d6f094c68ad6048cf70350e8d105bd0acd89e6", + "packages/convex-db-collection/README.md": "53b984ef9156a52cea7cb92a81d34d6543508d2dbf048d8f2aeb361ae996d259", + "packages/convex-db-collection/e2e/lazy-paging-oracle.test.ts": "7a5c1390879e4e01757d020e22bfa24f5852a2cf5bb4e18faa4174ce47a02e48", + "packages/convex-db-collection/e2e/local-backend.ts": "e38be0c546a885f55b004f23bc894ab86d3c3b5880bfd9dde8208866a42aea5d", + "packages/convex-db-collection/package.json": "2f7708d14a108253b257b1771f85060642817c6896ba240c7a0d1b853428b799", + "packages/convex-db-collection/src/index.ts": "4247e2bbd168a80791861f4553c10d0d878504a7cdc6a65d4bc648e15b1e51ba", + "packages/convex-db-collection/src/offline.ts": "55e70bdbbc310226a9f0175d0c0912218413ba67433b5b47ccc78d6e7dadd5c3", + "packages/convex-db-collection/src/options.ts": "ea2c2efc4d574080c5e03da8e121b79406640c667a1e260ead9eff22f6253d33", + "packages/convex-db-collection/src/paginated-options.ts": "de0e92849d363f2545823d96829964084b1a115b890f0f521f9bdc0958ea2b76", + "packages/convex-db-collection/src/pagination-demand.ts": "d2be74c1f3610c153c2b7c3c9411aa6ea216db7e76c9b0a7f32d63ef2bde57ce", + "packages/convex-db-collection/src/pagination.ts": "0308668d07db701d1f7f891d001a4aca2798973ea29bf1d4b00931eaa0e05f1d", + "packages/convex-db-collection/src/subscription.ts": "a0c312caf2af3667c18e96c565ceb159c20d65550426cdd44aca69a43a3543e5", + "packages/convex-db-collection/tests/helpers.ts": "11f2c79c6872373e8f77efae62d2fabfead0e5af6673c86920ae2ef4abdb7274", + "packages/convex-db-collection/tests/lazy-paging-driver.ts": "9819960aa222777f9de3b4bbc3728d492fd9304865701dacbbe381591e88cea6", + "packages/convex-db-collection/tests/lazy-paging-failure-history-oracle.property.test.ts": "b96de9cf49b58cdbc07e364312692e677363c90a83a9c1d94b84cb2b6c4dbdbb", + "packages/convex-db-collection/tests/lazy-paging-history-oracle.property.test.ts": "5bc8808d6f71648f48e40acb05943132448f3e8f5bba31d97e261f1741674384", + "packages/convex-db-collection/tests/lazy-paging-interleaving-oracle.property.test.ts": "f3a0238452746233224bc0b21adf790b594cacef82d786a0ba419d19980bb773", + "packages/convex-db-collection/tests/lazy-paging-model-oracle.ts": "5c25e5765413eb6ad3d7e26bd10e7cd20c75a9c094cdca0e142b87da56d5dfc5", + "packages/convex-db-collection/tests/lazy-paging-native-receipt-oracle.property.test.ts": "5714e2c25507f1ea4aef5bb6eb9bed69bc9a9b079540667570227aa1014a0822", + "packages/convex-db-collection/tests/lazy-paging-oracle.property.test.ts": "3a7b5b6284076bc8dbdb59ac1d735f509c6bc19201ad355a0382e7a797a2d569", + "packages/convex-db-collection/tests/lazy-paging-receipt-model.ts": "ce9d4e5bcf6265536498462c218deb5d8c42a4f1c2f792dd98042ed7b077902d", + "packages/convex-db-collection/tests/lazy-paging-receipt-oracle.property.test.ts": "06b60b0a86926070dd57dd068f4abe572d48924e2cea29acb77f417bf8115d6a", + "packages/convex-db-collection/tests/lazy-paging-version-model-oracle.ts": "23f2b3624ecf0e086f2d9b04eb3f940ead4f9a6fe320dc7b100fc04ef82099e1", + "packages/convex-db-collection/tests/lazy-paging-version-oracle.property.test.ts": "8f4f115806a65dc71b3382dade7a89e326decc2b3c25505369783f30163a210e", + "packages/convex-db-collection/tests/sdk-cache-oracle.test.ts": "0e011a20ce991195742bdfa0cd570d831105604ad8e3e2cc4b8fab4b62daad58", + "packages/convex-db-collection/tests/types.test-d.ts": "3ff1a42d6caf95d14e5270b632340524a9f796b978ccd031835bd1f2d0dab6dd", + "packages/convex-db-collection/tsconfig.json": "623bc70a72fd718e7d209c90a08df12cced0e9045a3565058623e34e946a5710", + "packages/convex-db-collection/vitest.lazy.e2e.config.ts": "e377ee3a835edd1215c48654db863d9ed27406ca67ae3b34c846e4ed253a030c", + "packages/db/package.json": "bb6df01ff9589c8b75437683c935b8457f65e9216d37173a4730e1a169e0ab43", + "packages/db/src/collection/subscription.ts": "c446932a8a1f8b2efd79d00846f1a110e65a6c6df75d2b24658053e6964b530b", + "packages/db/src/query/live/ARCHITECTURE.md": "aa6a41b651809819d414d364421140bab02f9b992e9f5dbe73cf634b43c46478", + "packages/db/src/types.ts": "2b4c5926922db049e7ccd58c3e3fc22503f6d8a9400f3fb01900e82da85c7a1e", + "packages/db/tests/query/load-subset-joint-replay-oracle.property.test.ts": "975585ef1d1bc921afcab2e5384318c5f661dd0a2577e642351b2aafed0ee98d", + "packages/react-db/tests/convex-lazy-paging-oracle.test.tsx": "0a33955d45816d766100b89e45dd63ce32125864cf3e2f0ea46533a582b0333b" + }, + "production_files_changed_this_round": [], + "production_files_unchanged": 7, + "core_subscription_restored_sha256": "c446932a8a1f8b2efd79d00846f1a110e65a6c6df75d2b24658053e6964b530b", + "prior_analytical_freeze_verified": 250, + "loss_audit_artifacts_unchanged": 9, + "prior_original_records_unchanged": 2, + "local_install_files_unchanged": 2 +} diff --git a/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-interleavings.md b/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-interleavings.md new file mode 100644 index 0000000000..bf3cd5f2d4 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-interleavings.md @@ -0,0 +1,146 @@ +# Convex lazy paging: receipt interleavings and composed publication + +This follow-up strengthens the four open products from the prior +[loss-audit/strengthening record](2026-10-27-convex-lazy-paging.md). +The scientific models own authored facts, in-flight obligations, receipt debt, +coherent versions and permitted public snapshots. Provider ranges, cursor +encodings, page chains, sealing and transport callbacks remain driver facts. + +Source binding: [manifest](2026-10-27-convex-lazy-interleavings-source.json). +The source began at branch commit `8c24922026fb4a714cb54b992ac85faa2e876742` +with the previous round's uncommitted implementation. No production fix was +needed in this round. All seven Convex production files match its recorded +pre-round hashes; core `collection/subscription.ts` was restored byte for byte +after calibration. Earlier records and frozen evidence remain historical. + +## Laws and receiving boundaries + +- **Exact applied settlement:** `LoadSubsetFn`, `LoadSubsetOptions.signal` and + `SyncAppliedReceipt` in `packages/db/src/types.ts` require establishing + receipts and permit an uncancelable in-flight baseline. The existing + `LazyReceiptModel` now records that independently authored obligation with + `initiate`/`finish`. Abort can precede acceptance of already initiated work. + Cancellation cannot settle before that work concludes and its assigned + receipts apply. Core can fulfill after abort once all required facts and + receipts apply; native/controlled receivers also assert their cancellation + outcome policy. Ordinary rejection has no new universal receipt-wait rule. +- **Coherent version publication:** after full lazy acquisition, each observed + source base must be one offered authored relation. Its compatible revision + frontier cannot move backward. Stuttering and skipped intermediate revisions + remain legal. Source provenance is checked separately, so offering facts + does not stand in for delivering them. +- **Replay authority:** architecture sections on replay participants/failure + and normative laws 7/9 require each graph's participating uncancelable work + to finish before a replacement publishes. Failed authoritative replacements + stay private through ordinary writes until a successful authoritative retry. +- **Cleanup authority:** architecture law 14 and `CleanupFn` terminate old + dependent graphs. Every later value/status event must preserve their terminal + state; a fresh dependent can receive a restarted source. + +Native Convex emits coherent keyed diffs during ordinary reactive repair. It +does not issue authoritative same-run truncate replacement. Native and core +receivers therefore compose the same relevant laws through distinct boundaries; +an incompatible external truncate wrapper is not introduced as native behavior. + +## Generated reach and comparison + +All families run fixed seed `20261027` and a fresh random campaign with separate +reach ledgers. Explicit `*_SEED`/`*_PATH` replays bypass campaign minima. + +| Family | Histories per campaign | Observed distinguishing states | +| --- | ---: | --- | +| `CONVEX_LAZY_INTERLEAVING_ORACLE` | 60 | Acceptance before final response; partial application before another acceptance; abort with held receipt; later peer acceptance; uncancelable acceptance after abort; abort with only unfinished in-flight work. All six: 60 in each lane. | +| `CONVEX_LAZY_NATIVE_RECEIPT_ORACLE` | 45 | Shared held native receipt, cached zero debt, cached shallow caller behind deeper held peer, new-version revalidation, canceled and successful held callers. All six: 45 in each lane; both SDK shapes observed. | +| `CONVEX_LAZY_VERSION_ORACLE` | 50 | Fully acquired multi-revision source, two accepted held revisions, later/skipped revision delivery, both SDK shapes. See raw reach log for each count. | +| `CONVEX_LAZY_FAILURE_HISTORY_ORACLE` | 40 | Failure before acceptance, shared held batch, error after acceptance with surviving owner, accepted writes surviving retirement/error, held cleanup after recovery, fresh graph with old graphs terminal. All six: 40 in each lane. | +| `JOINT_REPLAY_ORACLE` | 35 | Overlap with held receipt, newest done while older remains pending, failed replacement plus ordinary writes, retiring peer/retry, held cleanup/restart. All five: 35 in each lane. | + +Response and accepted-batch counts vary independently in the interleaving +receiver. A legal-action scheduler consumes authored delivery, acceptance, +application and cancellation actions. It has no production call budget. +Constructive prefixes preserve the distinguishing cuts; generated dimensions +vary cardinalities and legal suffix orders. + +The native receipt receiver observes actual sync writes and actual core +application receipts. Each required row/version absent at acquisition has a +unique supplying batch in its stable episode; that causal witness can assign +one receipt to two callers. Cached unchanged facts acquire no invented edge. +A cached caller may complete early or wait behind unrelated held work; only +post-drain completion is required. New-version revalidation installs the +authored coherent SDK caches before acquisition while callbacks remain queued. +Merely changing an unobserved remote source is not a promise of fresh load data. + +Version comparisons run on every source publication after full acquisition. +Terminal graph comparisons retain every value/status event after their cleanup +monitor is armed, plus explicit drained checkpoints. They preserve transient +violations even when later state becomes correct. Core replay compares every +public snapshot event and each drained action, with explicit newest-settled and +older-pending predicates before crediting that reach. + +## Calibration and production status + +Unchanged production passed the extended families. There is no production-bug +RED or repair claimed in this round. Initial failed test drafts exposed fixture +assumptions (Map iteration order, an uninstalled source revision, and an +overstrong ordinary-failure expectation), not product defects. + +Hostile checks distinguish the following wrong behavior: + +| Control | Evidence and classification | +| --- | --- | +| Borrow a peer's applied receipt / wait only the first receipt | Faulty controlled adapters fail the actual caller settlement comparison. | +| Settle canceled while an uncancelable baseline is still in flight | Faulty controlled adapter fails `settled before in-flight work finished`; shrinks and directly replays at seed `20261027`, path `0:0:0:0:0`. All earlier accepted receipts have applied at this cut. | +| Return startup's applied receipt for a native held commit | Faulty native receiver preserves actual core receipts for observation but lies to the adapter. It fails `a settled before receipt-2 applied`, shrinks/replays at seed `20261027`, path `0:0:0:0:0`. Correct receiver passes. | +| Transient rollback / mixed versions followed by correct final data | Recorder/model controls fail the version comparison after retaining the bad event; both shrink/replay at seed `20261027`, path `0:0:0:0:0:0:0`. These are not production-pager mutation results. | +| Publish when only newest replay finished | Core production mutant fails the exact public-row comparison during held-receipt release; older A still participates. Shrink/direct replay: seed `20261027`, path `0:0:0:0:0`. | +| Ordinary writes reopen failed replacement | Core production mutant exposes revision 5 after failure and fails the public-row comparison. Same shrink/direct replay coordinates. | +| Revive/change an old terminal graph | Scientific comparator controls reject the observations. Native event recorders are independently exercised in generated cleanup/restart histories. | + +The former future-peer receipt edge injection was removed. Waiting longer +behind a peer is permitted; an invalid model attribution is not evidence that a +production waiting strategy violates the contract. A positive model calibration +also accepts fully applied fulfillment after abort, while negative cuts reject +unfinished in-flight work and unapplied receipts. + +## Independent review and validation + +Three fresh `oracle-review` passes reviewed receipts/interleavings, core replay, +and version/failure histories; the latter also compared Query/Electric receiving +boundaries. Review found two repaired issues: a promise wait that could hide a +stranded caller behind test timeout, and the old model's blanket post-abort +acceptance prohibition. Native post-drain outcomes now fail before any unbounded +result wait. A suggested core reach predicate was also made explicit. Final +rechecks report no unresolved supported findings in their bounded profiles. + +Fresh final validation: + +- Full Convex package: **100 tests / 16 files** pass. +- Core replay/transaction/lifecycle receiving suite: **459 tests / 6 files** pass. +- Convex and core TypeScript checks, changed-file ESLint and oracle lint pass. + Oracle lint emits the known missing-local-`main` diagnostic, finds `origin/main`, + and exits 0. `git diff --check` passes. +- Semantic hostile controls shrink and directly replay as described above; + correct receivers and restored core production pass. + +Raw commands, logs, mutation restoration hashes and source-bound reviews are +packaged in `outputs/convex-lazy-interleavings-evidence` in the task workspace. +The existing local installation workaround is unchanged: +`baseline-browser-mapping` 2.11.32 → 2.11.30, confined to the preexisting +uncommitted workspace/lockfile override. No install, commit or push was performed. + +## Limits + +These finite histories do not prove arbitrary nonquiescent temporal progress +or every TLA+ interleaving. The native causal receipt bridge covers unique new +facts and nonreentrant commits; idempotent/superseding attribution and cleanup +abandonment retain their separate controlled/receiving owners. Receipt batch +listeners fire when commit returns and do not impose FIFO listener order on +nested commits. Version histories keep source keys/order stable after full +acquisition; arbitrary partial-prefix retention and membership edits retain +their earlier owners. Core joint replay uses one source and two stable-key +filtered owners; joined/includes graphs and exact event-payload replica equality +retain existing coverage. Ordinary native errors may recover and are distinct +from core authoritative replacement failure. New concurrency monitors use +controlled SDKs; receiving them at a real backend remains open under the Convex +integration owner. Backend and React tests from the prior round were not rerun +or relabeled as fresh results here; production bytes are unchanged this round. diff --git a/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-paging-source.json b/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-paging-source.json new file mode 100644 index 0000000000..76a97cd08a --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-paging-source.json @@ -0,0 +1,39 @@ +{ + "starting_head": "8c24922026fb4a714cb54b992ac85faa2e876742", + "binding": "current working-tree SHA-256 contents; excludes local install workaround and self/record", + "files_sha256": { + ".changeset/convex-collection.md": "c7ad56ad983fe653f0054f0a82fcd47cad46c1d59087d62d79a21ffb077eb324", + "docs/collections/convex-collection.md": "3901d5d040f8e101369f1a16a160d4e5d0df391ec48408f99d995191ad013105", + "docs/contributing/oracle-coverage.md": "a1f250ff60d734264475f69d293773d01259d7c9ded1ceb5e624f99d7bd06258", + "packages/convex-db-collection/README.md": "53b984ef9156a52cea7cb92a81d34d6543508d2dbf048d8f2aeb361ae996d259", + "packages/convex-db-collection/e2e/lazy-paging-oracle.test.ts": "7a5c1390879e4e01757d020e22bfa24f5852a2cf5bb4e18faa4174ce47a02e48", + "packages/convex-db-collection/e2e/local-backend.ts": "e38be0c546a885f55b004f23bc894ab86d3c3b5880bfd9dde8208866a42aea5d", + "packages/convex-db-collection/package.json": "8f6f781a3f005920033d44d237e86598b0e81157df73ff8955b4c44d36abb63b", + "packages/convex-db-collection/src/index.ts": "4247e2bbd168a80791861f4553c10d0d878504a7cdc6a65d4bc648e15b1e51ba", + "packages/convex-db-collection/src/offline.ts": "55e70bdbbc310226a9f0175d0c0912218413ba67433b5b47ccc78d6e7dadd5c3", + "packages/convex-db-collection/src/options.ts": "ea2c2efc4d574080c5e03da8e121b79406640c667a1e260ead9eff22f6253d33", + "packages/convex-db-collection/src/paginated-options.ts": "de0e92849d363f2545823d96829964084b1a115b890f0f521f9bdc0958ea2b76", + "packages/convex-db-collection/src/pagination-demand.ts": "d2be74c1f3610c153c2b7c3c9411aa6ea216db7e76c9b0a7f32d63ef2bde57ce", + "packages/convex-db-collection/src/pagination.ts": "0308668d07db701d1f7f891d001a4aca2798973ea29bf1d4b00931eaa0e05f1d", + "packages/convex-db-collection/src/subscription.ts": "a0c312caf2af3667c18e96c565ceb159c20d65550426cdd44aca69a43a3543e5", + "packages/convex-db-collection/tests/helpers.ts": "11f2c79c6872373e8f77efae62d2fabfead0e5af6673c86920ae2ef4abdb7274", + "packages/convex-db-collection/tests/lazy-paging-driver.ts": "b6cdef3d9f929be6cd0104d531d4177a0dff72c0d2e8ff61260659acffa3dffe", + "packages/convex-db-collection/tests/lazy-paging-history-oracle.property.test.ts": "5bc8808d6f71648f48e40acb05943132448f3e8f5bba31d97e261f1741674384", + "packages/convex-db-collection/tests/lazy-paging-model-oracle.ts": "5c25e5765413eb6ad3d7e26bd10e7cd20c75a9c094cdca0e142b87da56d5dfc5", + "packages/convex-db-collection/tests/lazy-paging-oracle.property.test.ts": "3a7b5b6284076bc8dbdb59ac1d735f509c6bc19201ad355a0382e7a797a2d569", + "packages/convex-db-collection/tests/lazy-paging-receipt-model.ts": "4f0387612c6e18caae83a24af1183586c426ddad65a6b3ad8d7cfb5b3de1067a", + "packages/convex-db-collection/tests/lazy-paging-receipt-oracle.property.test.ts": "06b60b0a86926070dd57dd068f4abe572d48924e2cea29acb77f417bf8115d6a", + "packages/convex-db-collection/tests/sdk-cache-oracle.test.ts": "0e011a20ce991195742bdfa0cd570d831105604ad8e3e2cc4b8fab4b62daad58", + "packages/convex-db-collection/tests/types.test-d.ts": "3ff1a42d6caf95d14e5270b632340524a9f796b978ccd031835bd1f2d0dab6dd", + "packages/convex-db-collection/tsconfig.json": "623bc70a72fd718e7d209c90a08df12cced0e9045a3565058623e34e946a5710", + "packages/convex-db-collection/vitest.lazy.e2e.config.ts": "e377ee3a835edd1215c48654db863d9ed27406ca67ae3b34c846e4ed253a030c", + "packages/react-db/tests/convex-lazy-paging-oracle.test.tsx": "0a33955d45816d766100b89e45dd63ce32125864cf3e2f0ea46533a582b0333b" + }, + "production_files_changed_this_round": [ + "packages/convex-db-collection/src/subscription.ts" + ], + "prior_analytical_freeze_verified": 250, + "loss_audit_artifacts_unchanged": 9, + "prior_original_records_unchanged": 2, + "local_install_files_unchanged": true +} diff --git a/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-paging.md b/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-paging.md new file mode 100644 index 0000000000..bfd15aa930 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-27-convex-lazy-paging.md @@ -0,0 +1,159 @@ +# Convex lazy paging: scientific oracle strengthening — 2026-10-27 + +This follow-up implements the maintainer's direction that the oracle owns the +scientific model and pager details remain in its driver. It starts from the +local implementation recorded on October 26, at repository HEAD +`8c24922026fb4a714cb54b992ac85faa2e876742`. All changes remain local and +uncommitted. The [source manifest](2026-10-27-convex-lazy-paging-source.json) +binds the current working tree. The prior implementation record, TLA+ package +and loss audit remain historical and unchanged. + +## Law + +Authority: `LoadSubsetOptions.signal`, `LoadSubsetFn`, `SyncAppliedReceipt` and +`UnloadSubsetFn` in `packages/db/src/types.ts`, the approved fixed-query profile +in the [collection guide](../../collections/convex-collection.md), and ordered +publication in `packages/db/src/query/live/ARCHITECTURE.md`. + +Success requires exact scoped coverage and every actual establishing receipt. +Receipt establishment is many-to-many: one batch may establish several attempts; +one attempt may owe zero, one or multiple batches. Already-visible row content +cannot discharge a newly accepted receipt. Abort removes permission for new work +but cannot withdraw accepted writes; uncancelable baseline writes become visible +before the canceled caller settles. An unaccepted cancellation may settle promptly. +Retirement does not retroactively forbid an already-started response. Retired +subscriptions have no row, readiness or error authority over their replacement. + +The source model sees authored row/version facts, scopes, owners, acceptance, +application and caller outcomes. It imports no production parser, comparator, +page chain, cursor encoding, sealing phase or commit partition. The native driver +alone translates opaque provider ranges. The applied-base provenance comparison +excludes optimistic overlays. Historical delivered facts remain allowed while a +new revision is waiting for application; settled coverage separately requires +current authored facts. + +## Reach and observations + +All campaigns run 60 fixed and 60 random histories, with distinct seed/path +prefixes and enforced reach minima. New campaigns use fixed seed `20261027`: + +- `CONVEX_LAZY_HISTORY_ORACLE`: one ownership/action history composes acquisition, + retirement, abort, chosen callback delivery, source changes, real optimistic + application holds, cleanup and restart. Its constructive prefix reaches + wider-authorizer retirement/abort while a successor is pending, delivery after + retirement, stale success/error fencing, accepted-held-receipt cleanup/restart, + and a changed source after restart. A 1–32 action suffix explores legal orders. + Source size is 16–24, page size 2–5, both directions and both SDK shapes. + Every source publication is checked using its contemporaneous provider-fact + evidence. Branch reaches are separate; other declared states reach all 60 + histories per campaign. +- `CONVEX_LAZY_SUFFIX_ORACLE`: three six-row profiles, page size two, both + directions and generated text. Two profiles have identical first-delivery + evidence and actual discovery trace but different hidden suffixes. A third + places three rows in the leading tie. Pending discovery and final exact + coverage are checked through production. All three reaches occur 60 times per + campaign. Separate comparator controls accept retained and reacquired + overshoot while rejecting a lost row; they do not claim native strategy choice. +- `CONVEX_LAZY_EXACT_RECEIPT_ORACLE`: an independent receipt model receives actual + core commit receipts, with independently varied 1–3 provider-response groups + and 2–3 establishing partitions, both partition orders, text and two abort + choices per generated case. Actual held response gates affect completion. + Shared attempts owe the same batches; a peer with applied coverage succeeds + while another waits. A held idempotent batch proves that row equality cannot + stand in for receipt application. Cached success, true with zero/one/multiple + synchronous receipts, ordinary Promise rejection and synchronous throw are + checked. Quiescent comparison rejects stranding. Each declared reach occurs + in all 60 histories per campaign. + +The existing native lifecycle campaign now records every source and dependent +row publication and status transition during stale injections at cleanup start, +held cleanup and after restart, together with source error observations. It +checks a fresh dependent's ordered result after restart while the old dependent +remains terminal. Its ten declared reaches occur in all 60 fixed/random histories. +The SDK cache receiver, local backend receiver and React public receiver retain +separate roles. + +## RED and production repair + +Before production edits, all seven production-source hashes were saved in +`work/lazy-strengthening/production-before.json`, with raw byte copies. The +extended ownership property failed on unchanged production: + +```sh +cd packages/convex-db-collection +../../node_modules/.bin/vitest run tests/lazy-paging-history-oracle.property.test.ts \ + -t 'generates ownership' --coverage.enabled=false --typecheck.enabled=false +``` + +`native-red.log`: seed `20261027`, path `0:0:0`, smallest source size 16/page size +2, at `retired callback reported an error`. The retained error callback of a +retired/replaced page subscription reached the current pager's error path. + +The only production edit in this strengthening round is `src/subscription.ts`: +unsubscribe revokes callback authority before invoking the SDK unsubscribe; +JavaScript success/error callbacks check that authority, and React watch callbacks +check it before reading a possibly throwing obsolete cache. The pager, public +API and scientific expectations did not change to make this witness pass. + +Early paired-suffix, cancellation-classification and fresh-dependent failures +were driver/observation mistakes: descending tie IDs changed the supposed equal +prefix; a DOMException failed an Error realm check; Collection values were used +instead of the ordered live result. They are recorded in intermediate logs and +are not production RED evidence. Review also repaired inert response grouping, +content-inferred receipt application, an ambiguous replay prefix, and aborted +requests retaining work permission. + +## GREEN and hostile controls + +Fresh logs are packaged under `outputs/convex-lazy-paging-strengthening-evidence`. +They include package/runtime, core receiving owners, React, real local backend, +TypeScript, ESLint and oracle lint checks. `package-final.log` is the final full +package run (87 tests); the core receiving run passes 465 checks and the React +receiver passes four. Direct minified public-index bundling with package +dependencies external measures +142 gzip / +113 brotli bytes for this round +(+420 minified bytes); this is a same-method comparison against the saved +pre-strengthening source, not a replacement for the prior packaged-size record. +The full existing backend suite passes 20 tests, including both lazy SDK +receivers; the targeted lazy run also passes separately. The local backend uses cached build +`precompiled-2026-10-10-72ce13c` and both installed SDKs. + +Seven existing production mutants were rerun, shrunk, directly replayed, +restored byte-for-byte and given repaired GREEN replays: eager startup, +unbounded split child, missing leading tie, skipped applied receipt, older +receipt clearing a newer receipt, stranded duplicate request, and quiescent +stall. New callback controls restore the original unfenced subscription and +separately remove only React's before-read guard. Both fail semantically and +have repaired GREEN direct replays. JSON results retain seed/path/exit/hash data. + +Six faulty controlled adapters fail the independent receipt comparison: +borrowing a peer's receipts, awaiting only the first receipt, skipping provider +completion, equating visible facts with an applied idempotent receipt, promptly +rejecting an accepted-held canceled caller, and stranding a quiescent cached +caller. These are actual faulty adapters through real Collection controls, not +TLA+ negative controls credited as implementation execution. + +Two independent reviewers used the repository oracle-review skill, one comparing +Electric/Query receiving practice. Their findings drove the repairs above. +Reviewed source bindings and later narrow re-review are retained with the new +evidence. No review claim is transferred silently across source edits. + +## Limits + +This strengthens the runtime oracle without claiming equivalence to all four +formal projections. Exact receipt-edge calibration uses a controlled core +adapter; native Convex exercises its actual batching separately. The controlled +receiver supplies all provider responses before establishing commits, so +arbitrary response/commit interleavings are not covered. Its fact domain is +stable within a history; native histories separately vary source revisions. + +Generated native cleanup retires direct owners first. The constructive native +lifecycle witness covers a surviving terminal dependent and held cleanup, but +not every surviving-owner/receipt/replay/failure product. Core replay/failure +owners remain primary and were rerun. No test here invents a total-call cap or +uses pager sealing state as scientific progress. Finite responsive quiescent +stalls fail semantically; the continuously nonquiescent TLA+ lasso remains +analytical evidence, and the fixture's turn guard remains diagnostic. + +The existing local-only `baseline-browser-mapping` override (2.11.32 → 2.11.30) +and lockfile are preserved unchanged and excluded from review/commit content. +No install, commit, push or PR mutation was performed in this round. diff --git a/docs/contributing/oracle-reviews/2026-10-27-convex-main-replay-merge.md b/docs/contributing/oracle-reviews/2026-10-27-convex-main-replay-merge.md new file mode 100644 index 0000000000..def5159b2b --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-27-convex-main-replay-merge.md @@ -0,0 +1,22 @@ +# Convex integration with main's replay helpers + +This record accompanies the merge of main `7e46f81adb3734ba77f209858ffdd8951731a937` into PR head `8be0438c3982fccf5b32a2f1945a359a9bc87c31`. + +The two conflicts were in the shared offline test configuration and serializer test imports. Existing serializer properties keep main's registered property selectors. The new opt-in codec property keeps its separate fixed/random reach campaigns and direct `OFFLINE_CODEC_ORACLE_SEED`/`PATH` replay. It stands aside during another property's shared path replay. + +No product law, reference model, generated history, production driver, or value comparison changed. Convex campaign budgets now call `oracleRuns`, as main's new lint rule requires. The codec budget uses the same multiplier. The campaign helper carries its validated shrink path as `counterexamplePath`, identifying the recorded history for the lint rule; direct replay still runs once. + +## Validation + +- All 64 adapter checks, 302 offline tests, and 18 isolated local backend tests passed. +- Both packages passed typecheck. Changed-file lint, targeted ESLint, the oracle run-count checker, and frozen offline lockfile installation passed. +- Seven campaign/reach helper tests passed, including missing random reach rejection and one-history direct replay. +- With `TANSTACK_DB_ORACLE_RUNS_MULTIPLIER=2`, ownership's fixed and random campaigns each reached all 15 declared states in 120 histories, compared with 60 at the default budget. +- Shared serializer replay with property `offline.serializer.mutation-wire`, seed `20260914`, path `0` passed. The fixed campaign and unrelated property campaigns stood aside. +- Direct codec replay with seed `20261022`, path `0` passed. Its reach record reported one replay history without campaign minima. + +Commands use `pnpm --filter @tanstack/convex-db-collection test`, `test:e2e`, and `typecheck`; `pnpm --filter @tanstack/offline-transactions test --run` and `typecheck`; and `pnpm --filter @tanstack/db exec vitest run tests/oracle-campaign.test.ts tests/oracle-reach.test.ts`. + +## Limits + +This is test-runner integration, not a new behavioral repair or expanded product claim. Existing hostile-mutant and behavioral evidence remains in the October 26 review record. The codec and Convex properties retain their named direct replay interfaces; this merge does not claim they are registered in main's shared replay manifest. Backend evidence uses pinned application functions and isolated local backends, not cloud infrastructure. CI must be checked on the resulting merge commit before declaring readiness. diff --git a/docs/contributing/oracle-reviews/2026-10-27-convex-push-integration-source.json b/docs/contributing/oracle-reviews/2026-10-27-convex-push-integration-source.json new file mode 100644 index 0000000000..f092ff4e20 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-27-convex-push-integration-source.json @@ -0,0 +1,462 @@ +{ + "local_parent": "929e3533e358ba70d79941fa8c195b4bb6cf1748", + "incoming_parent": "26bc43454ddfbdfa65d32afb2a72d06b19c64baf", + "binding": "Final merge working-tree SHA-256; self and local pnpm installation workaround excluded", + "files_sha256": { + ".agents/skills/oracle-authoring/SKILL.md": "a7fecfa338e18777210948328e589e304b5e7716cf709ba2cd99aaa7e3851d22", + ".agents/skills/oracle-review/SKILL.md": "6ce535ca468de3acc60a8936871edd3d9638a498160f1c8705d709b13080e43f", + ".changeset/convex-collection.md": "c7ad56ad983fe653f0054f0a82fcd47cad46c1d59087d62d79a21ffb077eb324", + ".github/workflows/e2e-tests.yml": "052adfda2a265d2453e398c9343e3f0eff7efcd44c6c826b6ff6e79298d85b49", + "AGENTS.md": "fd3c51d14419f993ddb4165dc78c0234b00d43e1eb8b8912fcee86b08fee9084", + "docs/collections/convex-collection.md": "b8768d0e08a1ff728540fe5e8adfa931dcb511e4a8715f8c9daf10f5575ae6d6", + "docs/contributing/glossary.md": "b3c62ff85a1ac26d050ea18ef1b64eb0318f8143079ee19ae0e322a4d9dfde21", + "docs/contributing/oracle-coverage.md": "0ba3f53b74086bc0e302ecb9dc153f4c0311ff4d08436811e42704f74db3ca51", + "docs/contributing/oracle-reviews/2026-10-26-convex-lazy-paging-source.json": "a4af1a586c058d08644fead26679a2894209aa6cbe3803dcf1c8cbefab97bcb1", + "docs/contributing/oracle-reviews/2026-10-26-convex-lazy-paging.md": "1de8506081336953f4b1c53933375960c84af277c04cb3dc07b91b79df391810", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-app-abort-source.json": "c732893331bd325faf4ad6fb1f534019af050d43da4b7d39d481c20aa817449c", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-app-abort.md": "12f0d22c630c1461b32b91fb56092421a58776c45b72fec4de6f0a7ca7d2296a", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-backend-concurrency-source.json": "b859cc163b55b7b9076a0d2c94bad1b524fd0405de39a0689c503c142975ba62", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-backend-concurrency.md": "adea3ed30c6f5df32e5af261497afd84c5c5519ca1ef71130681e899e7eba450", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-boundaries-source.json": "8b6780ecc7cfa6bab39524f20cba9999425507a9860ded3ed20b47e7a2485760", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-boundaries.md": "dae5fd558a6fe1e7cc2adaff2f977496edfed349be1494994bd3f890c00c4845", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-interleavings-source.json": "0fa1e828f4fee657e9011770bef73da2ac02cb4ca068d386d3a662c81a3e7c8b", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-interleavings.md": "f105899875a4129b7d816cacc1d6f094c68ad6048cf70350e8d105bd0acd89e6", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-paging-source.json": "d25abd9b66a277ef20186bc507ecbcd082f8562a7acbfd2b6d691704434a110d", + "docs/contributing/oracle-reviews/2026-10-27-convex-lazy-paging.md": "d057323e0dee9bab5cf8fbba85fa6d6c525ffc0be7843f1e351d7e48a6e42fff", + "docs/contributing/oracle-reviews/2026-10-27-convex-push-integration.md": "87375f22e6bbda03a48e746ac078cfa137b36c430ba0c1a850d9f3be902e2a32", + "docs/contributing/oracle-rubric.md": "ff0c438cb866d3359f339f5654b67551df28910355b1aef4e28cdc5f3c0ed176", + "packages/convex-db-collection/README.md": "2b92d0ce849e926acdc2d204e09fd9af3a662c8c8314ad8db5d223e74b77d12c", + "packages/convex-db-collection/e2e/backend/convex.json": "e7106add2ff21d61a430927bb9b1ba96b036b93fcecc31ff786149a6070ecd67", + "packages/convex-db-collection/e2e/backend/convex/schema.ts": "319611bbb3886d4b5c7bdbfbccab1f42792769304e2be8db009b73baf90aa92f", + "packages/convex-db-collection/e2e/backend/convex/tasks.ts": "b2e1d75b0c225d950df2070840955bf1b26b6a827d21a88a2b1e08108ada680e", + "packages/convex-db-collection/e2e/backend/package.json": "8e2e33603aa201acb7c8d88a55130bc07275d8fb53693e0965539ae4bc4e3585", + "packages/convex-db-collection/e2e/lazy-app-write-oracle.test.ts": "ad265bbbf78b617e1adf1c5df77991c4a2ce789ce8e78423ff56249dc353700b", + "packages/convex-db-collection/e2e/lazy-boundary-oracle.test.ts": "c14b42dcfbd28e1c875204593c10d7f98ebc6323cdcba32736be8315147ddccb", + "packages/convex-db-collection/e2e/lazy-concurrency-oracle.test.ts": "950b7c363b6146d39d24e0a2b91fccd6b3828beaaab9358ced3c9e3f75c07b4d", + "packages/convex-db-collection/e2e/lazy-paging-oracle.test.ts": "7a5c1390879e4e01757d020e22bfa24f5852a2cf5bb4e18faa4174ce47a02e48", + "packages/convex-db-collection/e2e/local-backend.ts": "e38be0c546a885f55b004f23bc894ab86d3c3b5880bfd9dde8208866a42aea5d", + "packages/convex-db-collection/e2e/network-gate.ts": "73851db1ae46e83b7ee1bdb88faf01aaec27178644a73e1f7835f94681806dd6", + "packages/convex-db-collection/e2e/setup.ts": "d448bab619ac7243957578b2565cdb647a5e71a70df3c65af3c3dc378b5df6bb", + "packages/convex-db-collection/package.json": "0d1420d6d318ab205560bb09b8be2fac7398998c963ce670f612c73611107851", + "packages/convex-db-collection/src/index.ts": "4247e2bbd168a80791861f4553c10d0d878504a7cdc6a65d4bc648e15b1e51ba", + "packages/convex-db-collection/src/offline.ts": "55e70bdbbc310226a9f0175d0c0912218413ba67433b5b47ccc78d6e7dadd5c3", + "packages/convex-db-collection/src/options.ts": "ea2c2efc4d574080c5e03da8e121b79406640c667a1e260ead9eff22f6253d33", + "packages/convex-db-collection/src/paginated-options.ts": "de0e92849d363f2545823d96829964084b1a115b890f0f521f9bdc0958ea2b76", + "packages/convex-db-collection/src/pagination-demand.ts": "d2be74c1f3610c153c2b7c3c9411aa6ea216db7e76c9b0a7f32d63ef2bde57ce", + "packages/convex-db-collection/src/pagination.ts": "0308668d07db701d1f7f891d001a4aca2798973ea29bf1d4b00931eaa0e05f1d", + "packages/convex-db-collection/src/subscription.ts": "a0c312caf2af3667c18e96c565ceb159c20d65550426cdd44aca69a43a3543e5", + "packages/convex-db-collection/tests/helpers.ts": "11f2c79c6872373e8f77efae62d2fabfead0e5af6673c86920ae2ef4abdb7274", + "packages/convex-db-collection/tests/lazy-paging-abort-replay-oracle.property.test.ts": "ba40916ea9c3d813d5f9df2c86ee1c6b849fd1decede97f3477f25cddeb432c3", + "packages/convex-db-collection/tests/lazy-paging-campaign.ts": "2ad323803e9d24e035f1600607c23da22f50368e0ca1d51a388f8412c2c0d701", + "packages/convex-db-collection/tests/lazy-paging-driver.ts": "f8d04cdd895a06fedcae907b2416ca550191ad5fadf8215ad29953cd1a739a6f", + "packages/convex-db-collection/tests/lazy-paging-failure-history-oracle.property.test.ts": "6b7b68aed6daf4fa408efaa8cacd0702f5ddf69b4b82a697cf4063b2c93cd529", + "packages/convex-db-collection/tests/lazy-paging-history-oracle.property.test.ts": "00c2b28f4d53e37cf7a689e767278a1fb401b872928239adcd78f27687f2cf0e", + "packages/convex-db-collection/tests/lazy-paging-interleaving-oracle.property.test.ts": "0dbd750ac8cf1460a7e525c2ae43a6ee59219935bb2590b51dc6f3c033d801b7", + "packages/convex-db-collection/tests/lazy-paging-load-outcome-oracle.property.test.ts": "bdccff66dcc336a2ebd687e31d5bddac0f0eedb096b15e503a40b53ed714c521", + "packages/convex-db-collection/tests/lazy-paging-model-oracle.ts": "7a0ce99a3442b15d516f03f0494b5a5cb39cc7106452ce50e257af79a014fc7b", + "packages/convex-db-collection/tests/lazy-paging-native-receipt-oracle.property.test.ts": "37080a808ab5689749a79bd377d41321461bd3589a6bfe7f561783062d51a8ac", + "packages/convex-db-collection/tests/lazy-paging-oracle.property.test.ts": "d1813fe874a528eeedb4e46b375836a9d621f4294fb04117eb1e913209820803", + "packages/convex-db-collection/tests/lazy-paging-receipt-model.ts": "b3714d83e0a90ceb9184a1701059f027907ff663ce829fa58895007147cf1453", + "packages/convex-db-collection/tests/lazy-paging-receipt-oracle.property.test.ts": "93461e28c72766f36f5533b8543dd93d59214ea76cdb2be2d873bec51f84d2d1", + "packages/convex-db-collection/tests/lazy-paging-revalidation-oracle.property.test.ts": "130b4e11498b9983cf123d03dd116957f089ac14c18ed7cbbd4ed6a165bdc10a", + "packages/convex-db-collection/tests/lazy-paging-version-model-oracle.ts": "5aee0ba04f556d312e25f03c6077949d47daf6d0e4eae6cd3dad0a9fe9ce86a0", + "packages/convex-db-collection/tests/lazy-paging-version-oracle.property.test.ts": "d0c41046b5d7fe98736f5f0bdcbe198e2f0fdfbeeb05dd3740b98949e8896063", + "packages/convex-db-collection/tests/lazy-paging-write-model-oracle.test.ts": "134d1a0cad9e8397657df4fba055ecbaf1587244dfd0b68c3c481025cd56b163", + "packages/convex-db-collection/tests/lazy-paging-write-model-oracle.ts": "7220c5df1f39b1ee12159d63c7272afec157221e91bec8d7b22e51be7e09cc93", + "packages/convex-db-collection/tests/sdk-cache-oracle.test.ts": "0e011a20ce991195742bdfa0cd570d831105604ad8e3e2cc4b8fab4b62daad58", + "packages/convex-db-collection/tests/types.test-d.ts": "3ff1a42d6caf95d14e5270b632340524a9f796b978ccd031835bd1f2d0dab6dd", + "packages/convex-db-collection/tsconfig.json": "623bc70a72fd718e7d209c90a08df12cced0e9045a3565058623e34e946a5710", + "packages/convex-db-collection/tsconfig.test.json": "5168aca44188a9be3b0bee215bfc2f222010dedb8f4e1fc08029c6873dcb6cf1", + "packages/convex-db-collection/vite.config.ts": "d2256b10b021b1eecb466f5b3a614c42f6b7bfb1a504b676e5359ac37fc2f141", + "packages/convex-db-collection/vitest.e2e.config.ts": "b43b7b797860455efa827774e9847975bd9850f81b9b284f43665c422e45b440", + "packages/convex-db-collection/vitest.lazy.e2e.config.ts": "f70fe4252fa4814f8a48c0a264de8384868c9f47abe7166bd2e3363c211f4176", + "packages/db/package.json": "c6b34cdb4cb21da9d1961e5cc5e75f8bb2311c80163264cd3fe26e6b86b1f44d", + "packages/db/src/collection/subscription.ts": "e20dee08fe0a9c952f4b0b0e20cc1d95890559e5e980665926afd3f23148a900", + "packages/db/src/query/live/ARCHITECTURE.md": "7e9c03399d78ce2c50951923352737b4d8bc6317f274654f702961ccdb1b1ae6", + "packages/db/src/types.ts": "3cfeea62080865a55e4d65a1a2b4859a9be96d6ea87fd1200f18e21ff6ee1035", + "packages/db/tests/collection-subscription-lifecycle-grammar-oracle.ts": "b6e5752c5f78312971667dd9c0e6ea7af7fd5d9a9db18b29a20be1b68c75325a", + "packages/db/tests/optimistic-history-oracle.ts": "ccfbfd1e62f7692a49cc9fbe4bf7c8440e9631b4fe4d1ed369f60c2ee4a9da10", + "packages/db/tests/query/load-subset-joint-replay-oracle.property.test.ts": "29bb5b03ee25b1ce98f64f9b3dc7fb9b99229358f1ac442790d8aad867065d35", + "packages/react-db/tests/convex-lazy-paging-oracle.test.tsx": "0a33955d45816d766100b89e45dd63ce32125864cf3e2f0ea46533a582b0333b", + "scripts/agent-lint-feedback.mjs": "b8b7c06c194a3794e0797dcd1cd671d8c3ae5e8fc7e2dbdd0114fcb19ab41a54", + "scripts/check-doc-snippets.mjs": "cf120cc982a87e4da28b43b0a29f4b47c1626af2c52af16a54e6e7bbf42fbfa8", + "scripts/commonjs-declarations.mjs": "3dae7c394287bd5d74467947267ab35207a6290b2fb5b12dcf7b2664115b9d66", + "tsconfig.json": "79e4282995ed94ce7e73583a6ef896db6dd8968b276f2a900de8d63493ace99b" + }, + "db_runtime_js_sha256": { + "packages/db/dist/esm/SortedMap.js": "114cbd8c0c42be9c58f3f72601fff8008efbae965e50acfea9b61b57ada9a21d", + "packages/db/dist/esm/client.js": "69f08b0ba80951bd4c54ef8b41ddb3dd3c738bf390a0e50185efebdaca968ab6", + "packages/db/dist/esm/collection/change-events.js": "d8934957814b2d760e8fb7dbfa60a928ee5233558f00ded7c5e61b949fd8cb86", + "packages/db/dist/esm/collection/changes.js": "f0eff8baf85109b92a07481c5a4fb46f4e396586cdbbe65f2cda6ce7f3fcabf9", + "packages/db/dist/esm/collection/cleanup-queue.js": "fcdfc1ceb93bc2c69f3e5abb0f95a7f5a3150228edf43181ebb695fc57af6564", + "packages/db/dist/esm/collection/config-errors.js": "1c966f75f9508a4ae40d88bfb297b85135d8c3923f5fd8fcdc0ec43e8ee9d6ab", + "packages/db/dist/esm/collection/events.js": "5f0ddff6c512df83effbd1310767c3f5e40cafd1dd67e77aeaea22ca6739aa3b", + "packages/db/dist/esm/collection/index.js": "c4c96f0c0460eb0939ad4dbfb40ff25d5246b2480a168e10819d1c2e16db8ceb", + "packages/db/dist/esm/collection/indexes.js": "24587a6928e9e54fed6659adbdddaec52c5879a8f7896922b7e79e54ac4c3c7f", + "packages/db/dist/esm/collection/lifecycle.js": "00f5b710f6527dd1bf8471c8f1aa6be3a909d8cca21624426c578f1381a4e9e9", + "packages/db/dist/esm/collection/mutations.js": "541a99ba6232660ed6250f9069bf18fe4cac24deaa26f698ccb6a86796d56c8c", + "packages/db/dist/esm/collection/state.js": "132b47655a6c04514124b45e2528085989984e884d9a7ef033f09173ac67769c", + "packages/db/dist/esm/collection/subscription.js": "29e90846c4a6f05d1acb3c74e36ed980816acf9728295e5996459e53c2c90908", + "packages/db/dist/esm/collection/sync.js": "4336f62d8890ae0523a3dd44ffeae62478b0763eab00fd9facc2213a11b3de08", + "packages/db/dist/esm/collection/validate-config.js": "0246af5091f8b2d62562ac7dbd3cb9c5259dde580d71797ea49a2f441a3d2643", + "packages/db/dist/esm/collection-options-brand.js": "682938793947f1370a55084ebecbb847c3ed7415b7bf8bc34b98ac19166f88d3", + "packages/db/dist/esm/collection-options.js": "379eb66796aec3b8c22758a8dede29733d5da6aa2cbd7fed6ab75c7b2e8eae0c", + "packages/db/dist/esm/deferred.js": "65e42033976c0bfc57831ca55a5050572f3293fbff16c54c92ecd8be11b71905", + "packages/db/dist/esm/error-message.js": "a2c19aff999e24b9717b792f9b199bafb6c8b82becb1850d3029afd4d8c38fda", + "packages/db/dist/esm/errors.js": "b7256b0827abe0ba0a47ee0559b9f09d1164aa238e277fc2ea8219e50d93b201", + "packages/db/dist/esm/event-emitter.js": "f63dc12896a83bd8a79efebb4db4c657cd2bec28cd969a1f060e6f884c8df944", + "packages/db/dist/esm/index.js": "d042d97460ecb662605dde69de335a84852a315e85d27f22bf1fd7ccc093ddd3", + "packages/db/dist/esm/indexed-db-wrapper.js": "44c376b27a67a7b504f1b309a2797746e41418af1c4d79dcd616a5aacdb7de86", + "packages/db/dist/esm/indexed-db.js": "7fdf7c0f77e5799e770d7c6d36e811b3281a8147ac10438cd301fb23861c31d2", + "packages/db/dist/esm/indexes/auto-index.js": "adba8f538312f4a8637ff6bc8b8cfbc7ac61774f2f65bde3ea13f59677d69b27", + "packages/db/dist/esm/indexes/base-index.js": "e673683296171fcca1ab0f58913134813b91dd9301534e69673149d8978fa790", + "packages/db/dist/esm/indexes/basic-index.js": "7c07a215269b1c12df6f08e6df8c8d238176b1ad995b0b0a7a884faef5049936", + "packages/db/dist/esm/indexes/btree-index.js": "103ba0e2bd6c04719de499bc278043be8df834326de58da5a6f5bf6553814283", + "packages/db/dist/esm/indexes/index-registry.js": "3835139e3dadaf269606d47f05ec3d21d031c7843fcf03fa3056f824c1711f0f", + "packages/db/dist/esm/indexes/reverse-index.js": "447aca26b4072e3e1aa58fbda4fe073cac8d6d7e96d065e4c9c8888769477985", + "packages/db/dist/esm/live-query-adapter.js": "917414b7eccc0271e4f0fd79e8a6711c6605fb21bccaec6b1271a7e7c355b409", + "packages/db/dist/esm/live-query-observer-client.js": "a040850ee9177b7a4445575b9a518e43836154f1531a9fe170bd850e4deb8919", + "packages/db/dist/esm/live-query-observer.js": "cdcfa5bbbdbaca3d925d1d07dfcd2a3eb44116185bd15dc658a512dcb4191d7b", + "packages/db/dist/esm/live-query-options.js": "9adbf71df03d107df28d1c88a928129b71c50ffea0d594e345cd9754ea96e06c", + "packages/db/dist/esm/live-query-primitives.js": "5ce3be1270689f28f332450abdf7a4722124ed76ba40af3778500c9c9c5fd40c", + "packages/db/dist/esm/live-query-window-binding.js": "51a547d894e1696213b59d3218949bd800f51784419a0dc1352f369b0de88054", + "packages/db/dist/esm/live-query-window-controller.js": "8f5d65d70daf5eb38f4f89bbd429bd0a1058d1e00b6db42a39568ea891fda158", + "packages/db/dist/esm/local-only.js": "613f6eed57f86b5e64b60220fbe7209b5e2f81b7b89b3516d44b5705b8f73332", + "packages/db/dist/esm/local-storage.js": "f169199cf86334574605f0b149d2df45c9d20ddde329c0da1e464a0ed4e0b8aa", + "packages/db/dist/esm/optimistic-action.js": "738ea6a8f8af3da16f805d595f4ddbb1660c20c95b03cd4297b3802736ecf806", + "packages/db/dist/esm/paced-mutations.js": "941bc7b34256a05b05e176bef07d1699767353b70b4e517626b7a662eefde927", + "packages/db/dist/esm/persisted-readiness.js": "076cbce6c4031681837d0fb778431228d2f2463830e7c9b0ae494edc98dfc500", + "packages/db/dist/esm/proxy.js": "c5901987cc13cf13c4b1e372ea51eabe76e7a5f4a61a0ecb876afe46bb7e15b1", + "packages/db/dist/esm/query/builder/clone-query.js": "1d7bdc998d87d08bd2c004e33169e501b195e796fdfd3d2a2576fe39a6b03321", + "packages/db/dist/esm/query/builder/functions.js": "d5af6fee8e3a2d191cd52331654c6b62ae1c95c5981c06e8c21d1a4fb5a938fc", + "packages/db/dist/esm/query/builder/group-keys.js": "367181d73963a2d31c6f7fa34bcad77439769c4d29734fc458a68ca05f76149b", + "packages/db/dist/esm/query/builder/include-functions.js": "c841cdb708a89a0004f298685c886bb4b0971930a68da3bf8b6d3898fa152174", + "packages/db/dist/esm/query/builder/index.js": "a358cb7121e198ba726109862770141ee3a5af72548bcbc79449ef794cfe6ec5", + "packages/db/dist/esm/query/builder/query-ir.js": "7ebc4ac8a97afcd03780f6fbe863da5d786677bc91bc4611214b812cfe52a49a", + "packages/db/dist/esm/query/builder/ref-proxy-identity.js": "b772b7fa119c7ef900fa13e59e72df65ced05ea764deb3fcb113fbf813d76d4a", + "packages/db/dist/esm/query/builder/ref-proxy.js": "d3f72b9ba3eab7c5b8bb4b7193b4e57baf09344d6ab36b3850f69a82c4ac3a78", + "packages/db/dist/esm/query/builder/wrapper-identity.js": "7dac5387f14bea8ac2588f5a8b9c45686c2686d5e55277f12e492a12a169b47c", + "packages/db/dist/esm/query/compiler/evaluators.js": "5fd06d68aafd1f76f5b11e49ed453c6cadd459e9dbfacf561f8dee60dbfb6a12", + "packages/db/dist/esm/query/compiler/expressions.js": "e27ddbed2ed8dcd95083f9e5f95416fe986df400542df4bed3991df7a181a2c7", + "packages/db/dist/esm/query/compiler/find-aggregate.js": "c7536fd0420b07a0653836de2274139df55a695d5a5f45a8c52eb36ae3f480e2", + "packages/db/dist/esm/query/compiler/group-by.js": "3809d2e88a6c421dbe8c89d86c0dc3d1a0c192fd8b8606250d24dfea68833ed7", + "packages/db/dist/esm/query/compiler/grouping.js": "9d3301d5b2ab97c1e88088c615215b33f6679ae33026629cae2de2bca04f7bb4", + "packages/db/dist/esm/query/compiler/index.js": "a137c6e48a59976ff3ad5be24e9ad11f91eee23c3c6c07b4ff95d130b6d14aff", + "packages/db/dist/esm/query/compiler/joins.js": "990b370ac38e98e79640868f07efb1b035ba01d8504897eb497c3d1e55a49d57", + "packages/db/dist/esm/query/compiler/lazy-targets.js": "68fad8034420d55dcfb0aa1b06f383c2ea6aa20e20ac32a95a096e02568a1e0b", + "packages/db/dist/esm/query/compiler/order-by.js": "b571e0a1e7d6c924a726e0847b3e727b88fab79dc027ceb424b1db8f3aaa3384", + "packages/db/dist/esm/query/compiler/parent-routes.js": "e84b09e29c2940a95df8dbf61a92a6d3d6ee9a445ddab062d5a6b84fa3c57455", + "packages/db/dist/esm/query/compiler/query-equivalence.js": "5bff3df82702c70a617d6db0bb676f259948a39d117d7c3cf494a135748fa8bf", + "packages/db/dist/esm/query/compiler/route-metadata.js": "e3bc4510982d7254492b6bb4cacaf7f7d4d401ac731a2eb10acd7de81c5f9931", + "packages/db/dist/esm/query/compiler/select.js": "33146439db6a1d73bc054cd12ff9ef753e6857a01363f8f2766062ef3f51c277", + "packages/db/dist/esm/query/effect.js": "86e4ef1f0b6169f6792b13daeb14f9f262801c3757ab874807184f31720e4f98", + "packages/db/dist/esm/query/equality-conjunct.js": "520651d46e62352b3ddd9f60a5021194427ec63429edf30d5c57cd33e1eceb23", + "packages/db/dist/esm/query/equality-value-identity.js": "99a000ef0fb1bee4c2c7920f539f4c809746a6dcf0e7bcd6e3db16ac7a113897", + "packages/db/dist/esm/query/expression-helpers.js": "a522a46c99164d937aa1e71c81a1dfaf9920f01826b47569ab3287cf89b5c5ce", + "packages/db/dist/esm/query/ir-stable-identity.js": "84e4dc9eeb601c08b59ac69807c7f8c07e93b022652045b396e1dd8f5ccebdcf", + "packages/db/dist/esm/query/ir.js": "568a8146120377d396fe6fc89aeeff6936f54d045584f89dce75ac7a3fef3869", + "packages/db/dist/esm/query/join-conditions.js": "99c179dd0e3dd9083c01f92c426dce6ea09963246b4ae63c27d98ddcbb276a0f", + "packages/db/dist/esm/query/live/bucket-facade-adapter.js": "32d19589ea210eb935d616f48d726a12235d817cc41d63d3290bda4cf1f457f8", + "packages/db/dist/esm/query/live/collection-config-builder.js": "55500b2675284f0e8fcdede1b6a21a6db90aaf79fd3fe91ba73bafd6eecd5d2c", + "packages/db/dist/esm/query/live/collection-registry.js": "fbd84835282008b81f54ce1a1be1cbf96412ae76870db59e1f00b3c5fc4f8922", + "packages/db/dist/esm/query/live/collection-subscriber.js": "7a0d2a93206dc64bad60e2f947a66b2e24eb2140cf0fca8a238ffff8265e6d71", + "packages/db/dist/esm/query/live/graph-scheduler.js": "7809a0e43fa4aeea35866887116c9116682f5ca070722951c180aca60257f17d", + "packages/db/dist/esm/query/live/includes-runtime.js": "6616db3335bbb07af493b0c88e02bfde441dd2b399feb4ccc5f1e02538c7c658", + "packages/db/dist/esm/query/live/internal.js": "abc2abf591111e4e4b4c1d9581c1f55b17116a8d626e913aee8bb937dbe810e7", + "packages/db/dist/esm/query/live/materialized-pipeline.js": "9ff58b1d83229d280f178b5762233b5421e23199ca8731093e06c492415811c8", + "packages/db/dist/esm/query/live/ordered-source-loader.js": "d88c88548b9e230d97541254bd745cdf08a430d68c6924af5a71d511fc962754", + "packages/db/dist/esm/query/live/public-key-reduction.js": "6bf3f1a74f1b8c2484b0eece22f7055b0d7cd891851cc38522f58e0942000fb6", + "packages/db/dist/esm/query/live/subset-demand-controller.js": "21c08fc99ac33a6c4bfc570e7a97536ec4bbf4c4c03a543cf185f033598b54f2", + "packages/db/dist/esm/query/live/utils.js": "6f0f15e78ef7f2d960ba25d3cf4965c109450dfb6a85e7c89f277d3e477e1302", + "packages/db/dist/esm/query/live-query-collection.js": "a8dd982618f18ab4e4949cb3760ef8ae8069fa352f183de320e6f09fdd5a0e91", + "packages/db/dist/esm/query/optimizer.js": "7b4f368744ebe93b197beeeaa43bdfaf929d3dd374171aa3489ee63a57326b68", + "packages/db/dist/esm/query/pooled-live-query.js": "0f1f6f882abde2d7a8e63f289ce2c0cf9a9cbe91ddcfdc1992c878467d279efa", + "packages/db/dist/esm/query/pooled-source.js": "94db711141ea9b844a6711e64d202ae9cc187545671bcbe8b859b0ab81824beb", + "packages/db/dist/esm/query/query-once.js": "c329a07ca5ad58f459caccc7059c1e003022b5c47393c918f0ddd020db6aa12c", + "packages/db/dist/esm/query/runtime-reference-identity.js": "e3d48564f972f3e0dde2d9d366a0b6d671e9373ec6d13f88e2a0d407ac9d006a", + "packages/db/dist/esm/query/subset-dedupe.js": "7d702db33f8cc84b46780f4c519c05d9fc372b68976d12376edbf21b1ef180d7", + "packages/db/dist/esm/scheduler.js": "f596b23b0792bc9487a36b37ca22f00580519cb1daa5b5743da0d8f5a20bdb3d", + "packages/db/dist/esm/strategies/commit-completion.js": "d5d96793061ade36ce404c87119e33e5f6b4f49f70cc168ea6072f629671135b", + "packages/db/dist/esm/strategies/debounceStrategy.js": "385c3775413e5dee637945132b97d58b2cc94292da6a78503f891fe91ea6debf", + "packages/db/dist/esm/strategies/queueStrategy.js": "8077fcac64ce8e6565b7e395d0a56a6471ddd203ff98dc63f7f1079e7f5b3706", + "packages/db/dist/esm/strategies/serial-pacer.js": "1fb57cde3e0f146bf514939e4bdf83140bf689569b3f178abb42f9271bdb9fa8", + "packages/db/dist/esm/strategies/throttleStrategy.js": "953366dc4658077d8e15cd0f25c7c18e96c8b307bef231a629110af166df68e3", + "packages/db/dist/esm/sync-persistence.js": "1b43703ebb007a406e21f1bc6bfb7efbb1c8b10cb2ba00843dd62f5ae4279ebc", + "packages/db/dist/esm/sync-receipt.js": "c7e735641bf5364fa2bc6ed733f27c46bc5123e78276a88eacb350af737f5538", + "packages/db/dist/esm/transaction-commit-work.js": "f2aa1e573beb60d1b155b56ca298e04a46ccedfd3993fbf3168fc8ba7282e439", + "packages/db/dist/esm/transactions.js": "86593c4c3906dbc29225c2678bc3fd11a56f539c01c858a39e447260fc881bc1", + "packages/db/dist/esm/utils/array-utils.js": "4504720ba742f751ef068638cfd66baa24519982a2bf3b61a7581134fb6eddb6", + "packages/db/dist/esm/utils/browser-polyfills.js": "2e57e7166db3fe3a4bdfd580d7fe74423f574ee101e20ac5a64b3ea7814847e7", + "packages/db/dist/esm/utils/btree.js": "38b6350f3aa55b2a6ac22213ed73854c3d2c93d3b00b2a432e04faf9ac531e74", + "packages/db/dist/esm/utils/callbacks.js": "a6c62f9336d7016ae3beefa01b1f11053a09d96f0de5fc311c455d75004405bc", + "packages/db/dist/esm/utils/comparison.js": "296ea8becf270e1684298b1abf373b13ebb96be745bda67c5f5ef695859f7c49", + "packages/db/dist/esm/utils/cursor.js": "9afa197792fdb3b481bbbf274915819b05fbb435132e727dd8f6f0acdf880e48", + "packages/db/dist/esm/utils/error.js": "69c8e2692531bb1389bafe4da6d34d86a0ba8bd38185a90337c14c1ad7ba9573", + "packages/db/dist/esm/utils/get-or-create.js": "8a23cd1e36935738f2b026d21119397a5739c225023ffcafb2b79b44df6372ed", + "packages/db/dist/esm/utils/index-optimization.js": "7c7fc3646b46852b97bbd5546acbb706114adca56bb575991d6df337d6e1319f", + "packages/db/dist/esm/utils/source-record.js": "eb817a9d26ab38615e2ea046b400cda1ca8757bfa7011bb06835bee7220c48f4", + "packages/db/dist/esm/utils/type-guards.js": "8ccb27d2ed1959f398eefa945e1c1717fa6dfad5ad03336e23b7be6c5a3ff9fd", + "packages/db/dist/esm/utils/uuid.js": "de1aa871943cab7ce570a670c16b62b9dc79c59c911f6aa2b421dc5097725e88", + "packages/db/dist/esm/utils.js": "6a615b9d94d98b5ff4be6db4c20d72b3499b4948d82127a368ddb06ea7d97a20", + "packages/db/dist/esm/virtual-props.js": "be99bfabbd2fe18da15e37cb50d300ccbce0fd7944fe7523e8e42591ee8be2da" + }, + "db_runtime_maps_sha256": { + "packages/db/dist/esm/SortedMap.js.map": "4f03ebb6c16473f4ad77c19bfab43ae4cc73bd890cea5442945825a5ebb4922d", + "packages/db/dist/esm/client.js.map": "f969cdc4e84283f01d681d4712fad55236561d3bae80ff13385f7a355e366343", + "packages/db/dist/esm/collection/change-events.js.map": "8e6ce82e42af099d955aa356b42ad2bd9a931f27ca439e43a65de1178342414b", + "packages/db/dist/esm/collection/changes.js.map": "44fdc47bdb4b473c8b0125d4eb6d387368c7060bd7765d1b79954e97b3c0627e", + "packages/db/dist/esm/collection/cleanup-queue.js.map": "29c5bd7607e27d92765b434b3004d729f705f65694d18c344879e76d8da38f3d", + "packages/db/dist/esm/collection/config-errors.js.map": "29b69a9d30583cdc7f57692fb08864454744ed7d0fcecdf25c035dbbb60dbcbf", + "packages/db/dist/esm/collection/events.js.map": "d1434a99d85b79e9c65d0e50eab2320894dd14b0f05acbab7445e09cdef46874", + "packages/db/dist/esm/collection/index.js.map": "feb508640106744872cc69d33729d4bb69f02cd51f0fe4051047ab95add04454", + "packages/db/dist/esm/collection/indexes.js.map": "2d0477bf4e6d06a015db0fa438c02ad9ca8102a5a06a1b3996137bc747f25d5b", + "packages/db/dist/esm/collection/lifecycle.js.map": "284075609f9711623d6ac03af57e369a16b794a2ca68e0635121a3c9dafc7176", + "packages/db/dist/esm/collection/mutations.js.map": "612e464a239b2891e3963411ca455caf2d67424d7a3e74dcbedc078f0486d3ad", + "packages/db/dist/esm/collection/state.js.map": "9f248e17b09b5b4a2e53b71feca994d82a353f7f0e406e1e9e649507b89e3324", + "packages/db/dist/esm/collection/subscription.js.map": "d9069c6b45fe3d8d953b7751c892d30c444b464c1dd181011d20bb5e5555cd9b", + "packages/db/dist/esm/collection/sync.js.map": "8243d146494e84ef6c8654f9bba4024503334fe23b03700ace19e71eeea97dea", + "packages/db/dist/esm/collection/validate-config.js.map": "26e77168627f586d22b77699ce56eb2f46e45b06c846ee0544bced990c52ee08", + "packages/db/dist/esm/collection-options-brand.js.map": "c8723746f918aba63828ddb5dc8ff70584f1017915935f272bf7df4d0885174d", + "packages/db/dist/esm/collection-options.js.map": "33b85760e3063743aff92447ca650fb4e4c789326b466df896275ec210e40e6d", + "packages/db/dist/esm/deferred.js.map": "0791be3a861f5e777de882ffc2060663073f5f151c4a97b783903f03452f3b4e", + "packages/db/dist/esm/error-message.js.map": "faedcbec5ab018314000d9d9de36573e19f16f8f1d7198dce5635da1fd2f2942", + "packages/db/dist/esm/errors.js.map": "a697be4f15634fb03662ae252726125c2ead40e100dbb64a6177a5521e0a8d21", + "packages/db/dist/esm/event-emitter.js.map": "407968f7641b8c5101937616b947791179103669611a824213c93f9cc0c14140", + "packages/db/dist/esm/index.js.map": "cb9639c947e215d07f601483c8a29e8ef52690d0fdc275822695130b870fdbe3", + "packages/db/dist/esm/indexed-db-wrapper.js.map": "48739f44869ea52975cc25855d6312cd0dccc19a74aa332ff69e08554c89268a", + "packages/db/dist/esm/indexed-db.js.map": "e9f10817d779a2927e85ee4e9eadb6378a1c43a84a4d55f1f28e007ac29f5173", + "packages/db/dist/esm/indexes/auto-index.js.map": "69b4efd038a172db3c9ae301dd66fa31b9da33eb4ed17544e10e4eb13a35c80a", + "packages/db/dist/esm/indexes/base-index.js.map": "d3394b4ba4d5303e54db685c623f1b08c4cc6d26850e19b302ca9bc287844155", + "packages/db/dist/esm/indexes/basic-index.js.map": "6be2b341f20c58759cfb1dde3a7455e168e8ab90879d8ceeace92005250fe9fd", + "packages/db/dist/esm/indexes/btree-index.js.map": "0533f61bf9980dc8d9a5b310f6cef8ca5fb3018fec192c4bf3b5459884a887e6", + "packages/db/dist/esm/indexes/index-registry.js.map": "add104924744a151439b3e97b754fd99d52094ff6f7504ac7d34169c3c46b703", + "packages/db/dist/esm/indexes/reverse-index.js.map": "1e0127ebd0b405d9bf32ec6dab106495a7cc35ff58e4973be59cd4e07cedb948", + "packages/db/dist/esm/live-query-adapter.js.map": "21201b2397903260ba975cd00c5e4b0e8e9944f23b20e8df1036111e4bbb031b", + "packages/db/dist/esm/live-query-observer-client.js.map": "f857fff2a7242a411dac344ee91fe1fe5f2371efa73b17a57ec9d2afcbf326b7", + "packages/db/dist/esm/live-query-observer.js.map": "749059dd7ac040deed97e3bdc1abfb5e8c4608897b9f4e79786e8eece4e8e75d", + "packages/db/dist/esm/live-query-options.js.map": "27024de874f7c8cec09b072d9b801e3c2c0636bf2bb5b017b6384ca93cae3704", + "packages/db/dist/esm/live-query-primitives.js.map": "9cd95a2d8a015c3ff9873b840363e08df1bf3f4312852c3bc7f09b973212a103", + "packages/db/dist/esm/live-query-window-binding.js.map": "5e4c932bdcff1306e9d1a4b56c0c37b64f5ab99774140683d47b001a4cd6c734", + "packages/db/dist/esm/live-query-window-controller.js.map": "7c2ba91edad1870d72928490e4ef0942030237efe0323a2aaf28f9ec1fc21cfc", + "packages/db/dist/esm/local-only.js.map": "d15b695749f35b3ff64d1c7690bab46b840784b429f7d59637a0c85551fd3161", + "packages/db/dist/esm/local-storage.js.map": "a771a6c06e135a5c8147df4d15846313365a89958f68a5ecb925464c14f729b8", + "packages/db/dist/esm/optimistic-action.js.map": "0d3885b2b0a267aa6a63e824b3b701e04d95fee8ed34d66191a1c615fee8c6e2", + "packages/db/dist/esm/paced-mutations.js.map": "fc9311c17757b3a46283623366d7a74543fc23f4029a072a0135c75df3a1f5b4", + "packages/db/dist/esm/persisted-readiness.js.map": "90f4efef28060eb85e985a170189161e0fb02a1860e00decc4808f6063ca4e25", + "packages/db/dist/esm/proxy.js.map": "5ace60cf05b00f4acaa46bc61172ada9aec365c696a4b3a1d6c3e8d72389c5f0", + "packages/db/dist/esm/query/builder/clone-query.js.map": "ecc15e5f00ad74ee3d93f6e62b78e545b5a96fc468e77275fe10d2723edb186a", + "packages/db/dist/esm/query/builder/functions.js.map": "cfdf0dddf52b91858343129259de07d62ed21040731fc79eef800c47a733eee4", + "packages/db/dist/esm/query/builder/group-keys.js.map": "6c416e087c8e6fd91f6ddeea31a5119b562ccb0330dc8258765f724da3e5bca1", + "packages/db/dist/esm/query/builder/include-functions.js.map": "5b20283d8ed157c7cbbd0ccbed7ea1f0447e1b4cf946a27503784e80d0a54202", + "packages/db/dist/esm/query/builder/index.js.map": "3e0088ec49e8d043099024961b0f6053fb5512f21226989864621f9313cea014", + "packages/db/dist/esm/query/builder/query-ir.js.map": "cd6baf25e4c43c89ee94b0e8c7ceeb885a5dc08eb03bdc4dde3442ce5323706e", + "packages/db/dist/esm/query/builder/ref-proxy-identity.js.map": "387137eac97d5d205a66e4d2003174cc540e3678fa96eca00fe4608cb704de38", + "packages/db/dist/esm/query/builder/ref-proxy.js.map": "8aa4f68cbe12301a765d9b510b360c32476a438d37e59833cfae19610e854a4a", + "packages/db/dist/esm/query/builder/wrapper-identity.js.map": "97f6f6189b39844bbf8a6d617c85bbcec6de7707b4f5b873ac333b085877ec6c", + "packages/db/dist/esm/query/compiler/evaluators.js.map": "d8fc2d460f804201ae77aadebd2c5de118929f542f8ac9c77080de40e9809ab7", + "packages/db/dist/esm/query/compiler/expressions.js.map": "1093c3423249da10ea9d7e40961d4353667307f24d0452323ae1601824dfeb1d", + "packages/db/dist/esm/query/compiler/find-aggregate.js.map": "743b7b076f5ac3b6377c1bd8f5ee3288cd23084a63a1540c5008aa94ef7c89c9", + "packages/db/dist/esm/query/compiler/group-by.js.map": "c1cf61d4c9c736612c7d31330df33f15b4747da56dfa837b3fbb008de81b89dd", + "packages/db/dist/esm/query/compiler/grouping.js.map": "7b142363772fd57a6c662ae2d790745c7be284fbddfefad2a68a74fb480e8a6a", + "packages/db/dist/esm/query/compiler/index.js.map": "092b838a6b18d2e77e16909724dfce384e59e88905b9330e5a2aac9c429b8162", + "packages/db/dist/esm/query/compiler/joins.js.map": "c1c0e2fe2ddf85c2a6b74f0e46fee0e12abf7402127dc275066f0e8e62450916", + "packages/db/dist/esm/query/compiler/lazy-targets.js.map": "00173769d55ecbd3f2b941961194304cf782dcd2ec70d171e3a6aa927282eb30", + "packages/db/dist/esm/query/compiler/order-by.js.map": "61aceb4097bb0f6860e1b30a1830294f28761278a989a2b0cc80e15d21ed7848", + "packages/db/dist/esm/query/compiler/parent-routes.js.map": "64dfd6cb99685701b22338688d1d57b6895d060baadbbcd541df303cc39e7177", + "packages/db/dist/esm/query/compiler/query-equivalence.js.map": "0abbdb782c918ba871af1e1e01e039cfac12345a27e26631c960ef80b7786740", + "packages/db/dist/esm/query/compiler/route-metadata.js.map": "093255d3925a85b2edcd290d8bc23c4bd6cb3ce93ad91ace2c16fe349182b820", + "packages/db/dist/esm/query/compiler/select.js.map": "e8f39203b4d159bc1faf1cdfc01c5fcd7688c6d71cdaada0cdd15a8a8b100d3f", + "packages/db/dist/esm/query/effect.js.map": "b2de99f39c9a1ea00a4cc020c6fcd62350cd702af959fd9cc03dc23919124ab1", + "packages/db/dist/esm/query/equality-conjunct.js.map": "918d8b00bd828b24f392b598c065266ab3cf3c918a1b42521ec5407362b5713a", + "packages/db/dist/esm/query/equality-value-identity.js.map": "c025bcefb4c2aaf2e5574c0e2cd1e0d5b814c9d6588f7b492d257167bc482fe8", + "packages/db/dist/esm/query/expression-helpers.js.map": "2ea6acca4b405d74ea970f5561e3fcdb048b3da4b5114fd9a868a8be9928ec77", + "packages/db/dist/esm/query/ir-stable-identity.js.map": "07d1318824b95ca48cb4b542012ab84777a2fcaea89ef99d648bac21a02d63e7", + "packages/db/dist/esm/query/ir.js.map": "a330b8970ebf352f078723b806ec15e96164c10a193333eba30384626e03504f", + "packages/db/dist/esm/query/join-conditions.js.map": "d1871669563d3dbd3927b37fd10646f8acb2b81d9b2b486d4ba6fa7bd8d83b73", + "packages/db/dist/esm/query/live/bucket-facade-adapter.js.map": "4f03b45e0c54064758dc4401b46d0188f795c4e4db26742cd30c882480926e8f", + "packages/db/dist/esm/query/live/collection-config-builder.js.map": "768af22fc58e696b33474d03c064c810a3d1f8d73f74a49dfc55fe435aa6c24c", + "packages/db/dist/esm/query/live/collection-registry.js.map": "70bfde0019d311b42dbd9269ea76d2d8c21f19258056b8deffa6297ad25e65ea", + "packages/db/dist/esm/query/live/collection-subscriber.js.map": "20647ee4ec617c45e5e8cef377b2c384dec9f7d2a53f16813fbcf6f26697cee1", + "packages/db/dist/esm/query/live/graph-scheduler.js.map": "a6818879e40a71b7e4f89377bbec7810d46e20a89e2dc494d13d52e3604de32d", + "packages/db/dist/esm/query/live/includes-runtime.js.map": "12d2e32cfce137e1b20dfac0d9d23336443aee7b52b795f7e6f2d02d6cab70fa", + "packages/db/dist/esm/query/live/internal.js.map": "329ffe47728a21fd36f7b462afb488732ab1ea1c8fe12792360b2b7b3dfd252f", + "packages/db/dist/esm/query/live/materialized-pipeline.js.map": "93a46ee5748428f64054c63c1750f28f045987e09b592b8d5b0d8429ee299d0b", + "packages/db/dist/esm/query/live/ordered-source-loader.js.map": "3086e87cb07e8bdb6ce22423a2c998aa01926d6fcbfeb0c4fcf764ed2db6656c", + "packages/db/dist/esm/query/live/public-key-reduction.js.map": "097b906f1a515aae626c4732ea65c10417b8a96afa28d13e257bf7c5adb3d6fe", + "packages/db/dist/esm/query/live/subset-demand-controller.js.map": "f66817f6e62c873d95a43b53f60065f6fe82a0b54db6115f24234c0e99b5cb1e", + "packages/db/dist/esm/query/live/utils.js.map": "3e7b43177b0c6c9c68213e32b3b8dc3d0062f1784037c28d4354bfae7631f98c", + "packages/db/dist/esm/query/live-query-collection.js.map": "eee723aed4951b426d58a19002dea89528208a8c68ecffbbcb74648f284769a1", + "packages/db/dist/esm/query/optimizer.js.map": "065ec88d32017ebd2deb05bbfe7230636fd5de00b0e360360467d84fc1213106", + "packages/db/dist/esm/query/pooled-live-query.js.map": "c4abf6b9a8488b258d4ac3d7339aa3ebfc89eefbed0b8167e9273c90d0df7fd7", + "packages/db/dist/esm/query/pooled-source.js.map": "79fa12499eb410d3f859aed92e5ea6e3e7ceddb2d3993d56867252105b603b0f", + "packages/db/dist/esm/query/query-once.js.map": "72fe61d93b79696bf7d84558d9d4b9af69ba2b34bf8642fda7e25175351efb7d", + "packages/db/dist/esm/query/runtime-reference-identity.js.map": "92c3053a4f4739bebff5c4989e6c258219f92899b7a4bfbf4edd6cf6e5338f83", + "packages/db/dist/esm/query/subset-dedupe.js.map": "a56b40a5f8b85182316f8d0d51075472a730ad42e6a9332c8bf5a774cba7fc18", + "packages/db/dist/esm/scheduler.js.map": "704ac7b4044dc7ba6f59b39467e5c1684b5ac02281fd8bf11b3792a8bfaa0307", + "packages/db/dist/esm/strategies/commit-completion.js.map": "8d1048752a5a8e051f303a236469c4661c68d970612c9640f2b01baa59afed42", + "packages/db/dist/esm/strategies/debounceStrategy.js.map": "75d4c42027d869f84ded192893de75150f261a4dfa2461b9df7220e17463aafc", + "packages/db/dist/esm/strategies/queueStrategy.js.map": "e78d4f6b324e212ab2d180b1fd1f02b2b969482b1c616423998dbad9c273c942", + "packages/db/dist/esm/strategies/serial-pacer.js.map": "f86b0bacf9c837a0d17bd70b4f5044e063027503b9dfb637947d310f20e1044d", + "packages/db/dist/esm/strategies/throttleStrategy.js.map": "962666a3c956f340f4f08b8dc5ebc53dce3691dbadbc50d11f5ab9fb963573b5", + "packages/db/dist/esm/sync-persistence.js.map": "3af0edddba572b52c3ae71e64bc042f0d46d8d87e46840669fabead9848ebce8", + "packages/db/dist/esm/sync-receipt.js.map": "34e2a99f3cb52d23c9e87dae2dfc2e77921f635061dda42e9581764a5fa88726", + "packages/db/dist/esm/transaction-commit-work.js.map": "569efa5a2295d0f2968a4013686b35b910e89858d2354c455481dafb0d36639a", + "packages/db/dist/esm/transactions.js.map": "8418d2e3c17a2094be0664c22609968f662e692eb068a73bf879deccf5c3b5d5", + "packages/db/dist/esm/utils/array-utils.js.map": "06c319e02e1e6476f8b50ea87639d0fc51fd91582dde17013f83cc84763b3844", + "packages/db/dist/esm/utils/browser-polyfills.js.map": "d7314595460632c65e30e7b370f8e9f5cf8d08541cbbac9506fe33866f5e1b03", + "packages/db/dist/esm/utils/btree.js.map": "dc0de437d55323947333f2d2480958ef7c208d1827669ca3021813425db4a872", + "packages/db/dist/esm/utils/callbacks.js.map": "09c7aaed837dd544f92772e3b14051fcdb33a5522341c491da3cf71cd4e77213", + "packages/db/dist/esm/utils/comparison.js.map": "53cf8a6f05d0e3199b2a0c9c99c498af2a0a770cab887d13cf68cef97b123497", + "packages/db/dist/esm/utils/cursor.js.map": "909aaa708bf9b055e95061fb51d40367b6e908a6e0146060111ac9f79d667603", + "packages/db/dist/esm/utils/error.js.map": "5364f271a878e3b7a2b01b5afa7e0f9860c878065b53801242167a699b479289", + "packages/db/dist/esm/utils/get-or-create.js.map": "4bee1a8523d7f5c01458722ff15643e3657ebfbc5e810ad55b1e01f7cd251d4e", + "packages/db/dist/esm/utils/index-optimization.js.map": "a133141c6f04f0e46c7b7e49e7cc827299dbe4eff14793f9f7a1535891415be0", + "packages/db/dist/esm/utils/source-record.js.map": "a1a8bb928209a96051f0fc38bf67d100622d2d401ce44cef39ef39d3d3da3c10", + "packages/db/dist/esm/utils/type-guards.js.map": "d1a8931d606566ca49febfddbfcf05e8c33fc28cd62a79c7979fe11dabf1d5f1", + "packages/db/dist/esm/utils/uuid.js.map": "112e3685f8f47147aa30ee826a405ac86204ebdbe7d49ced0c04b2e0dfc15050", + "packages/db/dist/esm/utils.js.map": "cc7353c8e0c5637d12ed0e00f983e54805356682e984915a3dcd86ec8e3f5239", + "packages/db/dist/esm/virtual-props.js.map": "2e992a2180a22683e58e885ae43646ab5b9739f1e088f64799ea9c0029f1491c" + }, + "db_source_sha256": { + "packages/db/src/SortedMap.ts": "dbbef7d2842d265cf5268f3ca8c512b0b79ea927135116d3fc4e08ea2799856a", + "packages/db/src/client.ts": "b8209ae4a18372271a7a6d94e270043adf8aa8499e6c66f3a8ff171d5337c0f8", + "packages/db/src/collection/change-events.ts": "754f897ba843d004eaf5bfe3f047fc53aa24eb49a16ae1eae4073e32ec17ba49", + "packages/db/src/collection/changes.ts": "0c07c750b94a97773bd707975a7ec7a41959ca7271112c4c0efeb79763a52b70", + "packages/db/src/collection/cleanup-queue.ts": "869a5fdff4680354a9b3c68d3e5228ffe701507968dd1a5ae2123ed856fe517e", + "packages/db/src/collection/config-errors.ts": "ffe046ad75542c863e1781c192191479cdcb68e7dcfaf92c9ce180ee48522cc0", + "packages/db/src/collection/events.ts": "fd95280be3bdd9a1177d4d57b425f484b9daf06b4b257ed28d6b17e3ef1bd132", + "packages/db/src/collection/index.ts": "29bf79f5b702a1bfea91040c86d08b439e99688c5bff10a5b9983f6bd652e11c", + "packages/db/src/collection/indexes.ts": "1a0b6633e5f5f24c1b29fe8e544ee3c52df6e886d2a61c8fe88750f3f811520e", + "packages/db/src/collection/lifecycle.ts": "dca3224bc3dca98dedca96be385826720a475ff781d30483a301d8cbd7438bfe", + "packages/db/src/collection/mutations.ts": "735bdd4872c97afbe6d7333d42402635bdb54c914e6b5cacf703444fbf933f31", + "packages/db/src/collection/state.ts": "6ee794b9026bc8805e5a516c004b97e6d7f170178809cbb89e8d42e567c29b3d", + "packages/db/src/collection/subscription.ts": "e20dee08fe0a9c952f4b0b0e20cc1d95890559e5e980665926afd3f23148a900", + "packages/db/src/collection/sync.ts": "4ff2e29f8fb27af6e71a46f959ad764a0d9dbd390bfba66604ba1c1836676add", + "packages/db/src/collection/validate-config.ts": "10bc3f83c357cf8b835694c73df9adf413c6190f924256786f17e123a688489a", + "packages/db/src/collection-options-brand.ts": "93df3fc8d90531ca926c6e9ca48653dbda9259cfaaade9c23cbbf35131b2e1ed", + "packages/db/src/collection-options.ts": "0b0f9713312f9554f62c020dda3bae8291303dbeadb85adfee76515a28442feb", + "packages/db/src/deferred.ts": "9c99553d7ab92ac89d377cb3382a9dfbdf592635c87b86eac8aa1f18efc4da68", + "packages/db/src/duplicate-instance-check.ts": "7ea3110958ff28f9befe760c60fc6fcd695bfba76aba76d654220358e6e8f8f5", + "packages/db/src/error-message.ts": "9ab4a64436c3db3a61f3a84b5ccff11359ccdf5676c81b81dc3be9db9c1dff13", + "packages/db/src/errors.ts": "88f5b50bbbd4ab5b50e7b50d8f86f2c50ea878f7011280901d27ec7c661afee6", + "packages/db/src/event-emitter.ts": "1d8775f3c2dbc9c833f03d2b9bb0d55d1606419e43d661064cc95cd91eb187e8", + "packages/db/src/index.ts": "e993a0b1bce473bfc84970a1ccdc56a9d3c1e65dd5fbb2eb34b7ba85fd2df14b", + "packages/db/src/indexed-db-wrapper.ts": "216fcac76c01b1af8363cda8a93678f53cd2cd58c9df918cd3d56d2a1f25fd11", + "packages/db/src/indexed-db.ts": "4c9960e7020389ab784f3ea6ce3fa1684b36ad1f6aef28a100cc15517374832b", + "packages/db/src/indexes/auto-index.ts": "d226516a7514c40e83010ab211020adba17965af96bd6d3b96e88989c6f31b51", + "packages/db/src/indexes/base-index.ts": "63e35c6bf011ad361882509a521407acf29b9404ce4e810a8f4d54b50c0e6685", + "packages/db/src/indexes/basic-index.ts": "9a128909d6df2c6d5b9ef07295a55f9f587170721cb7c6a652dd9c43eb674633", + "packages/db/src/indexes/btree-index.ts": "b006f65cf1c2ab14004e7311f4ac4b89958e919fb8439cd9ca25090789e37186", + "packages/db/src/indexes/index-options.ts": "5a43b0b1fead58395fae8cbf9df6087091e5d60ff6aca471621d41b52b8446d4", + "packages/db/src/indexes/index-registry.ts": "d44e34c9e5d90db56dee7f91e402f2547d91647ae100cc4752bc13bcd49e2719", + "packages/db/src/indexes/reverse-index.ts": "ebf2d22eb0eec10acb4df154488862243ec20be117bc069fe307fc991853dd0b", + "packages/db/src/live-query-adapter.ts": "d9cdb5d94192061ca0b1d61d288998b34ae34294226f5219213cb639a0bfc2d7", + "packages/db/src/live-query-observer-client.ts": "de6a592194f8c6a468b76f2bcae335cde6a466c52a5d7e4b282ffab604f95232", + "packages/db/src/live-query-observer.ts": "0a29dbc376ced35924c1793be506c087c57ac0b3d61798fc60337f1f9d87d016", + "packages/db/src/live-query-options.ts": "b07ce133d5a315e36eed7612a2c28444e5647b56987243f4a5a801c157a04238", + "packages/db/src/live-query-primitives.ts": "16fa17f9cefbb2dad98fda23651d792f8c8cd5bb4b812bd1197de7ae8f1f99c1", + "packages/db/src/live-query-window-binding.ts": "90e2750f95d89ed63af7faae4c2ea75ed20befef220f04e029503074c7ba7aa7", + "packages/db/src/live-query-window-controller.ts": "ea908b1aebd6d2af046c2e2e3a7722afcdcdbc8c956950aeeb695f2f06cd0264", + "packages/db/src/local-only.ts": "06d516a29e6f72b87f08c83f1fad2454dbc1cd3a3d296562f74fa5eb5aaec408", + "packages/db/src/local-storage.ts": "62331d402b5a7b958617e45ddb9de90a358f2fb1645ac589c5d412deb079cb5f", + "packages/db/src/optimistic-action.ts": "3b3d98ec0561927c301c6979256bbcd98faf0ee41b2b6690abe1c189eeaf4fec", + "packages/db/src/paced-mutations.ts": "ea9239b269c2622d23cb2fee6df2895534d64b43fb245f8160e054587b9b5458", + "packages/db/src/persisted-readiness.ts": "3fdff536570ed25347928bfac3043482d87a1506ea8bfd4d2c4e7f4c532fb598", + "packages/db/src/proxy.ts": "97fd4841cd8532a8bd2f08197d0d755cf348a50e5df0b2f4705079895f2a42c1", + "packages/db/src/query/builder/clone-query.ts": "d149bec44c8a26da59519dc9bb2e7ca4013cc04028ac46830bafe4cca439c752", + "packages/db/src/query/builder/functions.ts": "25eebaaa6ab6abfc0dfc035f96fbb5119e4127c15db063a6e1164d06f3effba2", + "packages/db/src/query/builder/group-keys.ts": "333fc756bff61aea0e249e5ad75ddc0a5343410f1c02270b25ad673a48ea9403", + "packages/db/src/query/builder/include-functions.ts": "78612562bb571f37f95416eea52e1991a154ccfe13f42e50d4ffb921d4604786", + "packages/db/src/query/builder/index.ts": "8a5c3397e84601025e0589fa5e5303d5054773cb45d083bfb56ac2ff28646f26", + "packages/db/src/query/builder/query-ir.ts": "6aa35045e8a83c63a68e3af2de951879aaa2b91c132606db6d0bfd32247af108", + "packages/db/src/query/builder/ref-proxy-identity.ts": "909d40e4425647ae58850decd6891fa7f6aa4690cd76e82c3ced13b70dc93b10", + "packages/db/src/query/builder/ref-proxy.ts": "da99a9d725676d7c29b1bc2c10913465389c6eb442ca651d06ab658a2561d0c1", + "packages/db/src/query/builder/types.ts": "a5d4068a38865a8ec17fbaac6f610e5c7736b95ff7122570fd780116ad1f57fb", + "packages/db/src/query/builder/wrapper-identity.ts": "3053d30505c57f0eb16cc573e196291e617aff7479151508bbaa397fa3757f57", + "packages/db/src/query/compiler/evaluators.ts": "499cb252c6e2c166cf626bf88c6c08ced102020558bdccab46a6d12acd52ec92", + "packages/db/src/query/compiler/expressions.ts": "70925f2d29f57aec189d0e7f485d0531534e3482f0696a5e9a1141d8cf36c42d", + "packages/db/src/query/compiler/find-aggregate.ts": "8714ef7f93008a2cec4b6afd3369b93bc691b6f1ca9ec977a3e9530ff285a43c", + "packages/db/src/query/compiler/group-by.ts": "5bc98c00574a624b4e60c9fd5012bfc2fcc5a0a0b8a3274db82ee930ac900f29", + "packages/db/src/query/compiler/grouping.ts": "60bf53ca65138c45894858d222187e3ba94c831c0a3223bd3234d60ac832405f", + "packages/db/src/query/compiler/index.ts": "41fc49756c2af715d5f2c89678636694692fe80fae2a229dbefcc04799c07413", + "packages/db/src/query/compiler/joins.ts": "8f0aacfc3e09bbc2672cc21af75ac8b27e1f957a47ee8e36ce3b451137beff08", + "packages/db/src/query/compiler/lazy-targets.ts": "6949ebfac1f0d76b64dc9a3c1cfee87490702a21de235c320fdd7aaa02b77587", + "packages/db/src/query/compiler/order-by.ts": "45c15469bb52e11b74a5f6e95b31c659f15e34d33e4bd2fb45bc5caf017d190c", + "packages/db/src/query/compiler/parent-routes.ts": "d59e9d64534c53bb58349f07c121158adc9b37cac7a0b5150493e6368442c18b", + "packages/db/src/query/compiler/query-equivalence.ts": "7171fa0f1aa960297ba1269193ed3527967d78ebf1c19ef03544604280aa4480", + "packages/db/src/query/compiler/route-metadata.ts": "53283291ce25656339492bff678b8742a7fdc7b60c49d3b7f7015569e5c06788", + "packages/db/src/query/compiler/select.ts": "a4b60959e37ea674bfac8f3cc4076c35facb9378891f72603c309e92c2882760", + "packages/db/src/query/compiler/types.ts": "c99fdab9be6b0a9204289c5c296e01350af9af1bebf80110e3a867c8c8d04b5e", + "packages/db/src/query/effect.ts": "0e7cd82b5213edf3666e8793f6bd71ffd1dd9166f7574cbf5c75920c74610f7b", + "packages/db/src/query/equality-conjunct.ts": "02041d880376b46814ab072747a9b0493e24cd4bd72df8389726780e535cc584", + "packages/db/src/query/equality-value-identity.ts": "a7b65759c20ebec4d83c47ec744bbd382da9a86ffa4c9e59028827127081d016", + "packages/db/src/query/expression-helpers.ts": "15d6635f8b28dafee0ca0afe2e8598545f09995a5b992798916d789167383019", + "packages/db/src/query/index.ts": "6e3e166f6570842b7e8da850040960815adc8d15684be5f35bb875854a2d37f2", + "packages/db/src/query/ir-stable-identity.ts": "7d41ca1a6f044d087a6addca6fd4a891828c72d3bf2db079adb95979eedd9f66", + "packages/db/src/query/ir.ts": "c237619948c6fd6120361b0e91bde84f4508ffc693811060f898c4a80964f1a9", + "packages/db/src/query/join-conditions.ts": "c2659d14f7a15203343de19b66cb83195c7ab5c26f953b52bbf6f40bbe012802", + "packages/db/src/query/live/bucket-facade-adapter.ts": "cbd6ea77a9d103fea6253e20656e31ff41ac353d134cb0c49710c56ffdd4b3c3", + "packages/db/src/query/live/collection-config-builder.ts": "cdfb8545fb574c20977e8e105325513a5abdad8a3c6d51cfa3650d3851a404b7", + "packages/db/src/query/live/collection-registry.ts": "c24da0f80292e4fa22370711cdf66e52ec44989f8fda50430025aa5b25510e65", + "packages/db/src/query/live/collection-subscriber.ts": "74e97fe498b44446d5ee21912175afab6a3e58d2787e04e890a26771d5c8e1a6", + "packages/db/src/query/live/graph-scheduler.ts": "bdd37b3814814a2bf32fa55422a2fb039107d04cbf4fbd79df67056c5bc4654b", + "packages/db/src/query/live/includes-runtime.ts": "5be2395cff94fdb99a5712e021109eba16e79b74334fda5a68ffba76242c3c10", + "packages/db/src/query/live/internal.ts": "3aa788cfb3647e2758ff9befe18e4503bc68797fd97d55dc5c3905db9a7145fc", + "packages/db/src/query/live/materialized-pipeline.ts": "f5cf651fea9537e12ad71eb8aff44991e43513aa769016c95e5d38b304f1ea2a", + "packages/db/src/query/live/ordered-source-loader.ts": "95ea59a251cd540406d3dd2dac1ba85f41d1f6422c4ffd44f973d9ea4ca01e18", + "packages/db/src/query/live/public-key-reduction.ts": "6bba0de854b7ddd1417696409c28c92d94f65366ca4e3f26ceb6a46111ea7b83", + "packages/db/src/query/live/subset-demand-controller.ts": "28e49884847544356975afd781bb2d485844c2f43f9f33097aef28d06ba2a1d0", + "packages/db/src/query/live/types.ts": "2bf32bbb3eba980f720dee5db19a657f378cbf92e9538a17a5cf1a77c6f3ea3c", + "packages/db/src/query/live/utils.ts": "00d2a0462ea2a997f50d104f641e9837cdf1341fc763116a0df773f6888f1646", + "packages/db/src/query/live-query-collection.ts": "d9fa71c30a1dbb69d58e2e11c2ba4acbeaa727f8d1ee70ce3d225be08ce7224f", + "packages/db/src/query/optimizer.ts": "4f359a9488bc178dcfcb5bbe56c04ab5589575c1224afffdd5b082d0cd1e8059", + "packages/db/src/query/pooled-live-query.ts": "20a2998f60289026fbacb291079ebdc86cea5437c63eb04229b8e8eed9c710b8", + "packages/db/src/query/pooled-source.ts": "777947447dc196cc34ad0b2be83c13ba4736997215816c228ff2a8abd172e53a", + "packages/db/src/query/query-once.ts": "1399a24809ce83173f0d952ab35620bc4430874cb96ab14229495fad3ef57478", + "packages/db/src/query/runtime-reference-identity.ts": "c1c695e80771ab545e58254381333bd24469813d69f448106df2dc5a5de1db48", + "packages/db/src/query/subset-dedupe.ts": "c535bb2a38db491d1c11024c88c71dc1664a59bae3c19f1f5bec35d37d5b99de", + "packages/db/src/scheduler.ts": "b21cbbdc088b3c1dc4840b7db857ee7f262e6958dc8bc300398ad8e93da4f01a", + "packages/db/src/strategies/commit-completion.ts": "242b52fc7a041f5f9d17172c356dc4e1ec06f2cbeaa85ba5995294356fba9b15", + "packages/db/src/strategies/debounceStrategy.ts": "8ab894803575f1cfab85939adbf67637342bf2e230e34d0db9bb642f205cdce5", + "packages/db/src/strategies/index.ts": "89409b3e87ef0c9366d5fc045289330d204cc58b33b8a08fb27cf0e67332a9e4", + "packages/db/src/strategies/queueStrategy.ts": "c21794ea5707e75de082b87c80ee5a19fd8b1752fd7725adf7435b377a5e4196", + "packages/db/src/strategies/serial-pacer.ts": "63f4aa8f7aacd4bbe5cc3d56173fdb9231fedea68bb721bb2d420cf2303ec679", + "packages/db/src/strategies/throttleStrategy.ts": "028b5a000cc5a49a0176a42f0bb0c5cbb0a71849e543b523b080942947f40cb7", + "packages/db/src/strategies/types.ts": "ac0628103f8d79d035550dfc7486bb4e6be9608ee3c4e820dcdf718f7aa573df", + "packages/db/src/sync-persistence.ts": "eb64401db52d9d2f269e5b6d92f85ebb5d4cfe6479dd1d4832918cd7b9dbad6b", + "packages/db/src/sync-receipt.ts": "5ab4074603d96c23decd4fae499ed55f3a6ee5077e379f370d9b76fd90565399", + "packages/db/src/transaction-commit-work.ts": "b3c341c01f7a8b1207683fb3388b66b1173481f43ea59160e1922778e64872fe", + "packages/db/src/transactions.ts": "aa41ad08a2df85e719e37396272b00c1b3a7d77236834f421e1883abc98530fb", + "packages/db/src/types.ts": "3cfeea62080865a55e4d65a1a2b4859a9be96d6ea87fd1200f18e21ff6ee1035", + "packages/db/src/utils/array-utils.ts": "a151c6b1779cf975e85e53a7b2328865889738df1f0d0fc3b8a8e466d15911f2", + "packages/db/src/utils/browser-polyfills.ts": "b3212d22af7254eb8c286d89393eb043324abd0b5a7ddbc8eaaa505e292b437b", + "packages/db/src/utils/btree.ts": "e2172d757bfdaf3e528fe94864876fac9de24a66302ed1930d108daa1f525b1e", + "packages/db/src/utils/callbacks.ts": "da4d6fc8d94bf93d07442eefbebd1ec0c183ef559fa7eebceb51b3652816708a", + "packages/db/src/utils/comparison.ts": "ed8c22f7315f0119025d47a0ea6432bab974c618665b167012c6c12da2287449", + "packages/db/src/utils/cursor.ts": "b728649d395a8e4e61cd6a63479be92ce2d4d4a08dc73beb549b92dafec5181f", + "packages/db/src/utils/error.ts": "133924e3809af4a69826c08774455f12f10aba675cbc92a4298525868bba4ece", + "packages/db/src/utils/get-or-create.ts": "eb56ba9e1a6eddecc8241062cb9a4ea0afca7437fbb50f0404de1276a611ee82", + "packages/db/src/utils/index-optimization.ts": "034310477e43f29bfe3493a56ee3b3524378ae3f941f4a452e6a15b9aec24425", + "packages/db/src/utils/source-record.ts": "48023571bd2e850170b78a34a3a9d14940d97742f510f96cad4baaf4a75694e3", + "packages/db/src/utils/type-guards.ts": "b7a63accd5abd4dd5c93bb451650fc962a2fdcc3981e70f0b7b373ebeb5f07db", + "packages/db/src/utils/uuid.ts": "0365c5c02b46eabac53562dac87b342656e2774b36385a094c16b3a9db3a5ca1", + "packages/db/src/utils.ts": "67e7143cfb324dc8f97c310679730682ecfb8088c2dc0d9e0efde6bc57ff9c76", + "packages/db/src/virtual-props.ts": "6add3fb9226251bf4dddadc7c5227469d4183a4df7799047f2fdc6b51ef4d9c0" + }, + "convex_production_and_scientific_models_unchanged_from_local_parent": [ + "packages/convex-db-collection/src/index.ts", + "packages/convex-db-collection/src/offline.ts", + "packages/convex-db-collection/src/options.ts", + "packages/convex-db-collection/src/paginated-options.ts", + "packages/convex-db-collection/src/pagination-demand.ts", + "packages/convex-db-collection/src/pagination.ts", + "packages/convex-db-collection/src/subscription.ts", + "packages/convex-db-collection/tests/lazy-paging-model-oracle.ts", + "packages/convex-db-collection/tests/lazy-paging-receipt-model.ts", + "packages/convex-db-collection/tests/lazy-paging-version-model-oracle.ts", + "packages/convex-db-collection/tests/lazy-paging-write-model-oracle.test.ts", + "packages/convex-db-collection/tests/lazy-paging-write-model-oracle.ts" + ], + "new_semantic_mutant_campaign": false, + "full_documentation_sweep": "Six unchanged incoming PowerSync failures and one stale Svelte baseline entry reproduce; changed Convex example passes" +} diff --git a/docs/contributing/oracle-reviews/2026-10-27-convex-push-integration.md b/docs/contributing/oracle-reviews/2026-10-27-convex-push-integration.md new file mode 100644 index 0000000000..2520f9b377 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-27-convex-push-integration.md @@ -0,0 +1,64 @@ +# Convex lazy-paging push integration + +Local implementation parent: `929e3533e358ba70d79941fa8c195b4bb6cf1748`. +Incoming PR branch parent: `26bc43454ddfbdfa65d32afb2a72d06b19c64baf`. +The adjacent [source manifest](2026-10-27-convex-push-integration-source.json) +binds the merged sources and rebuilt DB runtime checked below. Earlier review +records and frozen artifacts retain their original source bindings. + +The remote branch advanced while the lazy-paging work remained local. This +normal merge preserves its main merges, documentation validation and shared +oracle helpers. The only textual conflict was the DB `test:oracles` command; +the resolution retains every incoming entry and adds the joint-replay receiver. + +Incoming oracle lint requires shared run-budget scaling. Literal budgets now +use `oracleRuns`, and the lazy profile composes the shared multiplier with its +existing optional multiplier. Normal budgets and direct-replay overrides stay +the same. No histories, scientific predictions, public observations or oracle +comparisons change in this integration. The Convex production and model/comparator modules recorded in the source +manifest are byte-identical to the local parent. +The merged DB core and React adapter were rebuilt before receiving validation. + +The new lazy-paging guide example supplies its server-query type explicitly; +a generated Convex API normally supplies it. The snippet checker now resolves +provider imports through the documented adapter before treating an absent root +dependency as untyped. This uses the actual installed provider declarations; +it does not suppress the type comparisons. + +## Validation + +- Convex package: 112 cases across 21 files, no type errors. +- Actual isolated backend: 23 cases across 8 files, both real SDK paths. +- Core joint-replay receiver: one generated campaign owner, fixed/random reach + at all five declared states; 35 histories per campaign. +- React receiving path: four cases, no type errors. +- Shared-multiplier witness: value two makes all outcome/abort states reach + 60 histories in each fixed/random campaign, versus the normal 30. +- Oracle lint, affected-file ESLint, checker ESLint, formatting and diff against + the incoming branch pass. No normal budget was lowered. +- The changed Convex snippet compiles with actual provider types. + +The full documentation sweep is not green in this dependency environment. +It reports six PowerSync examples with absent `list_id`/`completed` fields and +one stale Svelte baseline entry. The incoming checker with the incoming Convex +guide reproduces those same six failures and stale entry. PowerSync docs, +PowerSync sources, DB sources and the snippet syntax helper match the incoming +branch. These diagnostics are retained in the task-local integration evidence; +no baseline was expanded to hide them. The new Convex example is the additional +769th example and passes its focused compilation. + +Earlier intermediate diagnostics are retained: backend setup without local-port +permission, missing React coverage instrumentation, a temporary unused import +during run-budget integration, and snippet checks before provider type resolution. +The final backend run has local-port access, the React receiving run disables +coverage instrumentation, and the final package/static checks pass. These setup +and integration diagnostics are not semantic oracle counterexamples. + +Law, reach, RED/GREEN, mutant and limit evidence for the implementation remains +in the earlier versioned records. This merge does not claim a new product-defect +RED, new mutant campaign, TLA+ execution or independent delegated review. Its +new evidence is integration and unchanged-model receiving validation. Prospective +immediate core abort for uncooperative adapters remains #2168 work. + +The local `baseline-browser-mapping` 2.11.32 to 2.11.30 override and its lockfile +changes remain excluded from commits. No reinstall was needed. diff --git a/docs/contributing/oracle-reviews/2026-10-27-ordered-refetch-scope.md b/docs/contributing/oracle-reviews/2026-10-27-ordered-refetch-scope.md new file mode 100644 index 0000000000..3406a2e372 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-27-ordered-refetch-scope.md @@ -0,0 +1,23 @@ +# Ordered signalling-refetch oracle scope + +Parent: `160b5ce2486e7d12ce53cde0518eb761dc5eace9`. + +## Law and authority + +The live-query architecture's "Ordered requests, continuation, and recovery" section states: finite continuation requires an order index, and an underfilled unindexed prefix retains the established full-source fallback. Its work law requires an applicable index. A no-additional-full-source assertion for every joined refetch exceeded that contract. + +CI workflow 40572667149 failed the generated `ordered-work.eager-indexed-window` property at seed `-931507916`, path `21:3:3:3:4:3:4:4:4:5:4:4:4:4`. Local replay reproduced the same work assertion, two full-source requests versus zero, after an empty source receives an ineligible row and a marker change. These were two consumers' permitted fallback reads. No production defect is claimed. + +## Repair and evidence + +The signalling-refetch transfer assertion now requires bounded continuation to be available: an indexed joined window or a direct source-filter window. Unindexed joined histories still execute every row, order, publication, and other applicable work comparison. The coverage map records the bounded-transfer limit and owner. Existing generated dimensions already include both index choices and these actions; they remain unchanged. + +The exact replay passes. All 270 existing tests in the owner pass with no type errors. Two fixed inputs replay the reduced trace through the same `checkEagerWindow` model, driver and comparisons, once without an index and once with it; both pass. They carry no independent expected answers. These fixed inputs are distinguishing witnesses, not a new generated reach claim. + +A temporary wrong-observation control added one full-source request only on indexed histories immediately before the work comparison. The indexed neighboring witness failed at that comparison, one request versus zero; the unindexed witness passed. This demonstrates retained comparator sensitivity, not a production-mutant result. The control was removed. Changed oracle lint and targeted ESLint passed; ESLint reports four unchanged marker-shadow warnings outside this edit. + +Replay command: `TANSTACK_DB_ORACLE_PROPERTY=ordered-work.eager-indexed-window TANSTACK_DB_ORACLE_SEED=-931507916 TANSTACK_DB_ORACLE_PATH=21:3:3:3:4:3:4:4:4:5:4:4:4:4 pnpm --filter @tanstack/db exec vitest run tests/query/ordered-work-oracle.property.test.ts -t 'keeps rows and per-change work bounded for a random' --coverage.enabled=false`. + +## Limits + +This corrects an oracle assertion under an existing architectural contract. It does not revise a product law or establish a new behavior-class closure. Unindexed joined refetch transfer is not bounded by this assertion. Existing independent row checks remain mandatory, and indexed histories continue to reject full-source work at the tested checkpoint. No shipped implementation changed. Fresh CI must pass before readiness is reported. diff --git a/docs/contributing/oracle-reviews/2026-10-28-convex-health-migration.md b/docs/contributing/oracle-reviews/2026-10-28-convex-health-migration.md new file mode 100644 index 0000000000..177dfaff5a --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-28-convex-health-migration.md @@ -0,0 +1,141 @@ +# Convex integration with Collection health, October 28 + +This record binds PR parent `969078c19d4a4e7a888d4d4466e3d0d99a1372cc` +and main parent `7d9ed02e085d11e871e4b6424d47e3c0d7073023`, plus the +source hashes below. Main merged #2168, the maintainer's new Collection status, +health, reload and cleanup contract. This is a normal merge, without rewriting +published history. The only textual merge conflict was the DB oracle command; +its union retains main's hydration owner and the PR's joint replay owner. + +## Law and repair + +Authority: `.changeset/source-health-channel.md`, the project glossary's status +and health terms, and live-query architecture laws 14, 16 and 17 from #2168. +A failed first load is final for its sync run and recovers through `reload()`. +After readiness a recoverable provider or projection failure preserves rows, +reports `failureReason`, and a valid observation clears it. A rejected source +acceptance remains terminal and needs a new sync run. Reload must use its own +run's readiness, since public Collection status can still describe the prior +run. The adapter uses the new fail/reportError/reportHealthy controls and folds +terminal first-load failure into the existing rejected-acceptance slot. + +The existing snapshot owner was false green for health because it observed +rows and getSyncError only. Before the production edit, it gained status and +health comparisons. Its initial-invalid recovery driver now explicitly reloads, +under the maintainer's new contract. All typed-key, cardinality, field-removal, +deletion, atomic duplicate rejection and recovery comparisons remain. +The offline source owner likewise reloads after a failed first load. + +The core joint replay model previously waited for obsolete provider promises. +Law 16 now says abort ends the load for core immediately. The model drops those +participants while retaining every accepted receipt obligation. A generated +history observes the new replacement while the aborted provider promises stay +pending. The failure, ordinary-write hold, surviving peer and restart comparisons +remain. The terminal-graph models now expect `crashed`, preserving their exact +last-row comparisons. Component and actual-backend drivers use internal lifecycle +teardown to exercise that internal boundary. Public cleanup admission is checked +separately and must reject under an active dependent. A synchronous adapter throw +is now checked as a rejected load, as law 16 specifies. No history is skipped. + +## Reach and RED + +On the unchanged Convex adapter after merging main: + +- `pnpm --filter @tanstack/convex-db-collection typecheck` rejected removed + `markError` use. +- `pnpm --filter @tanstack/convex-db-collection exec vitest run tests/snapshot-oracle.property.test.ts tests/offline-oracle.property.test.ts` + rejected a missing health failure reason at seed 20261022, path + `0:0:0:0:0`, snapshot `[0, "", [[]]]`. This is a semantic comparison failure + after ready, independently of the startup removed-method runtime errors. +- The full adapter run had 24 failures among 112 cases, including removed-method + errors and changed cleanup/throw contracts. +- The unchanged joint replay model failed at seed 20261027, path `0:0:0:0:0`, + at public publication during held-receipt application. It predicted the old + rows while current work had completed and obsolete work was aborted. This + exposed a model contract migration, not a reason to restore obsolete waits. + +## GREEN and calibration + +- Full adapter suite: 112 cases in 21 files, no type errors. +- Snapshot owner: fixed and random 150-history campaigns each reached all 13 + declared states 150 times, including initial duplicate rejection and recovery. +- Offline source and acceptance owners retain their fixed/random campaigns. +- Core ordered-work owner: all 272 cases pass. Joint replay owner: fixed and + random 35-history campaigns each reach all five declared states 35 times. +- All 302 offline cases pass; all four React receiving cases pass. +- All 23 actual backend cases in eight files pass on the final adapter, using + isolated local backends, both SDKs and pinned official application functions. + No production cloud infrastructure is used. +- Frozen lockfile install, both Convex/offline typechecks and changed lint pass. +- Doc sweep: 790 examples, zero new failures; 272 unchanged baseline failures, + including 61 parse failures. + +A temporary production mutant omitted reportError after readiness. Snapshot +replay at seed 20261022/path `0:0:0:0:0` rejected it at the health comparison, +expected true and observed false. The mutant was removed. A separate joint +checker calibration changes an observed row's version and must fail its exact +publication comparison. It is a wrong-observation control, not a production +mutant or a claim about native provider behavior. + +## Weight and limits + +`pnpm size:compare --base HEAD --exact-base --packages convex-db-collection` +compares the published PR parent with the merged working tree. Convex grows +from 4106 to 4145 gzip bytes (+39), and +32 brotli bytes. The migration reuses +one terminal-failure slot rather than adding a parallel failure machine. Its +one new run-readiness bit is required because reload retains public status; +using that status would misclassify a new run's first failure. App scenario +sizes also include incoming main's health implementation, about +3069 gzip +bytes for the app scenario; they are not attributed to Convex alone. + +These generated health comparisons cover the controlled callback boundary, +finite arrays, raw/projection paths and accepted/rejected receipts. The actual +backend suite supplies receiving regressions for authentication, paging, +reconnect, writes and interruption. It does not establish arbitrary cloud +failures, OAuth browser flows, all framework schedules or universal histories. +Core's source-health owner remains primary for health-tree composition and +public cascade cleanup. CI on the pushed merge still needs independent +verification before this head can be declared ready. + +## Guide outcomes + +ORC-001: #2168, the glossary and architecture laws 14/16/17 authorize the changed +predictions and limits above. ORC-002/003: snapshot expectations still come from +input arrays and pairwise key equality; the joint model retains only authored +snapshots, current obligations and accepted receipts, with updated local prose. +ORC-004: constructive valid/invalid starts and duplicate/recovery prefixes remain; +raw/selected paths, all three typed-key lanes and generated suffixes remain. +The joint grammar retains both obsolete outcomes, both owner settlement orders, +1-4 rows, 1-3 ordinary writes, peer retirement before/after acceptance, and +cleanup before/after receipt release. Removing abort loses the changed boundary; +request-scoped writes after abort remain excluded. ORC-005: real Collection +callbacks and typed entries compare at each delivery/publication; the recorded +RED and controls prove the checkpoints ran. No missing-input hypothesis is used +to excuse an earlier result. ORC-006: production reporting mutant and joint +wrong-observation control fail at their intended assertions. ORC-007: matching +fixed/random campaigns and direct seed/path replays pass after restoration. +ORC-008/009: obsolete load completion no longer distinguishes public readiness, +but accepted receipts remain separate because their application still does. +Terms follow the glossary. ORC-010: existing withTestCleanup/withOracleCleanup +retains primary and secondary failures. ORC-011: no new shared-semantic-fault +hypothesis requiring another independent formulation was identified. +ORC-012: this record binds the reviewed sources and scopes the claim. +ORC-013: first-invalid versus ready-invalid and current-complete versus +obsolete-pending histories distinguish the changed boundaries. ORC-014: health +comparison is controlled-boundary evidence; the actual backend receiving suite +is separate regression evidence, not a cloud health-tree closure claim. +ORC-015: normal campaigns enforce the measured reach counts reported above. +ORC-016: no standalone example or type assertion was added to an oracle file; +existing shared drivers and comparisons carry the changed judgments. + +## Source binding + +- `packages/convex-db-collection/src/options.ts`: `d3194929c80c16cc6f3240ed573030de80592cbb7d6a87a415df7713fe512449` +- `packages/convex-db-collection/tests/snapshot-oracle.property.test.ts`: `c4607e12ac1b48cf9b894ae5ef28875cc9689219460f84b5408d69d5463acfd2` +- `packages/convex-db-collection/tests/offline-oracle.property.test.ts`: `308607afafbe503159a524328950a5116d899d43cd5506863e0293f7412369a3` +- `packages/convex-db-collection/tests/lazy-paging-model-oracle.ts`: `a410a4c81af20a4ffa27718631237401049567504daed29b22c1a172f8420dcb` +- `packages/convex-db-collection/tests/lazy-paging-oracle.property.test.ts`: `08a477e3605d37011408f57636f0ec5c47c4b5ee3503a026adb28b29467de57a` +- `packages/convex-db-collection/tests/lazy-paging-failure-history-oracle.property.test.ts`: `1953376c011658befdd11d29ba911c7dfad9b5dfa5a02abdb6b5c29b84ca5bd6` +- `packages/convex-db-collection/tests/lazy-paging-receipt-oracle.property.test.ts`: `f56ab7ca6deaebc60d331d99edaa3504134582b71ae80ded9073c2b76c7a9bbf` +- `packages/convex-db-collection/e2e/lazy-boundary-oracle.test.ts`: `4c0815a68426233a3860c52ca307779a98a79179bccccdd855947ea1023283de` +- `packages/db/tests/query/load-subset-joint-replay-oracle.property.test.ts`: `93efd32d76d74d4cd815659e9adb4a0e29183041369dac1fff350a9e58f34a54` diff --git a/docs/contributing/oracle-reviews/2026-10-28-convex-mangle-cache.md b/docs/contributing/oracle-reviews/2026-10-28-convex-mangle-cache.md new file mode 100644 index 0000000000..508b548132 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-28-convex-mangle-cache.md @@ -0,0 +1,28 @@ +# Convex integration minifier cache, October 28 + +Source parent: `48a6c492a6e00439467ee8b0ec9dfafe8ea95aa6`. +PR workflow 40622732106, job 122600360701, failed `pnpm check:mangle`: +`failQuery` was also used by the Convex backend fixture's public control field. +Main's new private method was therefore unsafe under the existing cache policy. + +The authority is `scripts/mangle-private-members.mjs`: every cache key must be +private-only, with no matching property use in another package. Its supported +`--write` generator preserves safe assignments and removes unsafe names. +Running it removed exactly `failQuery: gZ`, added no names, and changed no +source behavior, model, grammar, comparison, fixture schema or public API. +Renaming the fixture or excluding it from the checker would hide the collision. + +RED: `pnpm check:mangle` reproduced the same unsafe-entry failure locally. +GREEN: the regenerated 439-name cache passes that command. A fresh minified DB +build passes `pnpm test:minified-db`, checking 114 exported error names, index +metadata, query rows and live updates. The rest of that CI job's checks also +pass locally: client-only query bundle checks, production error text checks +(263 literals, including 22 shared), and all 318 built-dist cases in five files. +Changed lint passes. Existing minified and dist oracles supply unchanged public +behavior checks; this cache repair adds no new law or generated-history claim. +Fresh remote CI is required before declaring the new head ready. + +`pnpm size:compare --base HEAD --exact-base --packages db --no-scenarios` +measures DB at 108498 to 108501 gzip bytes (+3) and +26 brotli bytes. The +private method keeps its full name to meet the existing public-property safety +policy; no extra recovery machinery or runtime branch was added. diff --git a/docs/contributing/oracle-reviews/2026-10-28-pr-2171-finite-reach-inventory.md b/docs/contributing/oracle-reviews/2026-10-28-pr-2171-finite-reach-inventory.md new file mode 100644 index 0000000000..2bb8c39f6c --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-28-pr-2171-finite-reach-inventory.md @@ -0,0 +1,73 @@ +# O26 finite reach inventory (read-only, 2026-10-28) + +Scope: all tracked changed and untracked source/test files in the RC oracle working tree relative to HEAD `858b174a65f160d13d01f0324c7481cc8e4eba3a`. Source inspection found 36 createOracleReach call sites in 24 files: 28 default-policy sites in 19 finite-owner files; eight explicit `replaying:false` sites in five files. Pure models and support drivers do not add their own counters. + +## Confirmed mechanism and experiment + +`packages/db/tests/oracle-reach.ts:31-38` interprets any shared seed OR path as replay unless overridden. None of the 28 finite lanes below has a direct seed/path selector: they enumerate ordinary `.each` inputs. Other randomized properties in the same Electric/Offline/optimistic files do have replay support and must retain their existing policy. + +Executed receiving experiment: Preact bound-options file, selector `Preact retains bound options.*getKey`. With seed/path/property unset: two native tests pass but the suite exits 1 for missing schema/both reach. With only `TANSTACK_DB_ORACLE_SEED=1`: the identical two native tests pass, ten tests skip and the suite exits 0. Logs: `o26-preact-partial-seed-none.log` and `o26-preact-partial-seed-1.log`. This independently confirms that file's missing-lane bypass; other rows are source-inspected, not separately executed. + +## Default-policy finite counter sites + +| File | Lines / finite scope | Existing entered-campaign handling | +| --- | --- | --- | +| packages/db/tests/live-query-cache-lifecycle-oracle.test.ts |80; claimant aggregate |Global afterAll unconditionally checks every claimant; checker-only selection also owns no native history. No zero-native guard. | +| packages/db/tests/live-query-cache-options-oracle.test.ts |54; claimant/path aggregates |`executedRoutes` gates each route. Correctly ignores unentered routes, but a partially entered route loses all required-state checks under shared seed. | +| packages/db/tests/mutation-handler-types-oracle.test.ts |319; public handler type aggregate, minimum6 |afterAll is inside native-only `public handler result admission` describe; checker controls are outside, so fully unselected describe does not enter its hook. Partial native describe is still exempt under seed. | +| packages/db/tests/optimistic-history-outcomes-oracle.test.ts |320; external-completion per-history cuts |Counter is constructed/asserted inside each finite history. No unentered counter, but seed disables even its local required cuts. Earlier random optimistic-outcomes campaign is separate. | +| packages/db/tests/query/virtual-row-fields-oracle.test.ts |239 root;840 union;1228 timeline;1690 facade;2035 nullable facade |Five global aggregates, unconditional afterAll hooks. File contains checker-only controls and unrelated finite families; no native-entry guards. Keep all required cells of each entered campaign; do not blanket-exempt partially selected families. | +| packages/db/tests/query/virtual-row-fields-types-oracle.test.ts |74; generated public-type aggregate |Global unconditional afterAll plus checker-only controls. No zero-native guard. | +| packages/electric-db-collection/tests/electric-oracle.property.test.ts |134 mutation aggregate;153 per-history mutation cuts |Aggregate afterAll is in native-only mutation describe; local counter is inside a native history. Existing random/replay properties elsewhere are distinct. | +| packages/offline-transactions/tests/transaction-settlement-oracle.property.test.ts |1017; restored-outbox aggregate |afterAll is inside native-only restored-history describe. Earlier and later random settlement/retry campaigns are distinct. | +| packages/preact-db/tests/bound-options-oracle.test.ts |28; retained-options aggregate |Global unconditional afterAll plus in-place-replacement checker controls. No zero-native guard. Executed seed bypass above. | +| packages/preact-db/tests/bound-schema-options-oracle.test.ts |21; mutation-schema aggregate |Existing `ran` boolean correctly ignores a wholly unentered native campaign. Partial entered campaign still exempt under seed. | +| packages/query-db-collection/tests/optimistic-ordered-refetch-oracle.test.ts |79 ordered-history aggregate;91 per-history cuts |Aggregate afterAll is in native-only describe; checker controls are outside. Local counter exists only within entered history. | +| packages/query-db-collection/tests/ownership-lifecycle.oracle.test.ts |1677 mutation aggregate;1693 local cuts;1941 post-handler aggregate |Mutation describe is native-only. Post-handler describe includes checker controls under the same afterAll and has no native-entry guard, so checker-only selection reaches an unrelated aggregate assertion without seed. | +| packages/react-db/tests/bound-schema-options-oracle.test.tsx |31; per-hook mutation-schema aggregate |Existing `ran` boolean per hook correctly ignores wholly unentered live/Suspense campaigns. Partial entered hook campaign still exempt under seed. | +| packages/react-db/tests/cache-first-registration-oracle.test.tsx |63; first-registration per-history cuts |Counter constructed/asserted inside each finite history; no unentered aggregate. Seed disables local required cuts. | +| packages/react-db/tests/cache-options-oracle.test.tsx |41; per-path cached-options aggregate |Global unconditional afterAll across client/server paths. File currently contains native histories only. A partial selected path is seed-exempt; no separate calibration lane currently requires a zero-native guard. | +| packages/react-db/tests/descriptor-start-release-oracle.test.tsx |30; descriptor-start per-history cuts |Constructed/asserted inside `checkHistory`; checker-only controls do not construct it. | +| packages/react-db/tests/source-id-reuse-oracle.test.tsx |103 inline-option local cuts;599 descriptor-release local cuts |Both counters are per-history; no unentered aggregate. Existing unrelated fixed native cases/controls do not construct these counters. | +| packages/svelte-db/tests/cache-options-oracle.svelte.test.ts |26; cached-options aggregate |Global unconditional afterAll. File currently has native histories only. Partial history selection is seed-exempt; no current checker-only lane needs an entry guard. | +| packages/svelte-db/tests/descriptor-start-release-oracle.svelte.test.ts |24; descriptor-start per-history cuts |Constructed/asserted inside `checkHistory`; no unentered counter. | + +## Explicit-policy sites inspected; do not sweep-change + +- `packages/db/tests/query/virtual-row-fields-effect-oracle.test.ts:313`: now explicit false, plus `enteredEffectHistories >0`; preserves every required finite lane once any native Effect history enters. +- `scripts/eslint-plugin-oracle/replay-observation-oracle.test.js:116`: explicit false and `campaignEntered`; retained checker-only subprocess control passes. A partially entered source campaign still owes all source states. +- `scripts/publish-history-oracle.property.test.js:40`: explicit false per finite command history. +- `scripts/publish-http-oracle.property.test.js:62`: explicit false per finite HTTP history. +- `scripts/release-history-oracle.property.test.js:114,120,147,251`: explicit false. This finite owner genuinely implements canonical direct replay: property selects a family, path chooses the authored case, local required cuts always remain enforced. Its aggregate afterAll is intentionally skipped only when `oracleReplayRequested()` is true. Seed-only runs still require aggregate reach. Preserve this policy. + +## O26 receiving checks to add later + +For each distinct aggregate policy: (1) no native campaign entered, checker-only controls run; (2) partially entered finite campaign with missing required lanes; (3) full finite campaign; run each with shared seed unset and irrelevant seed 1. Require the same reach verdict with either seed. A missing local-cut mutant must also fail with either seed for per-history counters. Keep existing direct replay receiving tests for the release owner and fixed/random shrink replay owners unchanged. An entered flag must be set before native setup, not only after a successful comparison. Do not let a per-route/per-hook guard borrow another receiving path's reach or silently shrink the declared domain. + +No repository files or production code were edited. This inventory is not a claim that every listed file has been independently executed under both environments. + +## Source fingerprints at inventory capture + +This is a pre-repair source-inspection snapshot. Later oracle edits invalidate affected rows until rechecked. + +| File | SHA256 | +| --- | --- | +| `packages/db/tests/live-query-cache-lifecycle-oracle.test.ts` | `c9da8b274c8402d2eb374d1a2cd4865f53095a9dce7423d6b1f616c4c45ff8fa` | +| `packages/db/tests/live-query-cache-options-oracle.test.ts` | `7ebf875a183111904b46d7905de3572287ccee5f28af9c7267f9969c9b7d60aa` | +| `packages/db/tests/mutation-handler-types-oracle.test.ts` | `59afc1991c1ffd2362922ca3e8a1ce7ee307ca3b7bc40cb05e9ffd3bad699ca3` | +| `packages/db/tests/optimistic-history-outcomes-oracle.test.ts` | `ee937b8c3191d3c827be4420e42a157d28a068a9fc2a6a6e82df940f9d33b523` | +| `packages/db/tests/query/virtual-row-fields-oracle.test.ts` | `c925791e375f2f2a0941e0ab35b965cabe89570fb13a981b14826b874eaca98f` | +| `packages/db/tests/query/virtual-row-fields-types-oracle.test.ts` | `65508306911b9c1082181cad2b7467a858a86823bc33228665f2affe5f1edfe4` | +| `packages/electric-db-collection/tests/electric-oracle.property.test.ts` | `adce4010e6406d428404a3c4e1745e00bd89baf7a0612b09e1748ea3f1294e2c` | +| `packages/offline-transactions/tests/transaction-settlement-oracle.property.test.ts` | `83eb5551300ebb702e119174a2352c985ad6086cefa18042cc5c947c3c89e91a` | +| `packages/preact-db/tests/bound-options-oracle.test.ts` | `208d617b276b5fd0f1975884d2c337dc26313b94b71b208f9bcd890a974b374a` | +| `packages/preact-db/tests/bound-schema-options-oracle.test.ts` | `97c32b831caf5e7d4c6349197e356594fffee0c0b4cfc113a479717fe3a76f0c` | +| `packages/query-db-collection/tests/optimistic-ordered-refetch-oracle.test.ts` | `462f432f7b20797b8326dc50ae9feb9db177354c802c40d3aad53a46e65a493d` | +| `packages/query-db-collection/tests/ownership-lifecycle.oracle.test.ts` | `d9699cf0554bcc58bc9966be51d0b5f8c622b9f6f14e61344fce05420535029b` | +| `packages/react-db/tests/bound-schema-options-oracle.test.tsx` | `8d4707534b5962b8f249885b10104045869a86d9e86faf8c8e0ac289866b41c6` | +| `packages/react-db/tests/cache-first-registration-oracle.test.tsx` | `2247d32fc1e47c95136dafcf796f6c6a8dd8b93124d05b7981043156dafdd9e5` | +| `packages/react-db/tests/cache-options-oracle.test.tsx` | `7a4a85db8fa822fd96b28e2de6da53a5feeb057ef26d79956c878215faed1f6d` | +| `packages/react-db/tests/descriptor-start-release-oracle.test.tsx` | `46c4264fa4dd1e8b7d46d4983b064317cc790f4ee05f2d8c9167561d99c44974` | +| `packages/react-db/tests/source-id-reuse-oracle.test.tsx` | `af236fff80f7d21be7d458b0be70416ec48d08f8fb26c3f762f2f3e91a9d28a0` | +| `packages/svelte-db/tests/cache-options-oracle.svelte.test.ts` | `2ecf6deca78f7c347719f81cb640a7b5e78f236e9f7df51a90bd12e862180627` | +| `packages/svelte-db/tests/descriptor-start-release-oracle.svelte.test.ts` | `98268189dbb4a4591ebdc983e3aa994509daa48dd24a73cf1ba38b9ce656ea8b` | diff --git a/docs/contributing/oracle-reviews/2026-10-28-pr-2171-oracle-challenge.md b/docs/contributing/oracle-reviews/2026-10-28-pr-2171-oracle-challenge.md new file mode 100644 index 0000000000..16f4916b82 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-28-pr-2171-oracle-challenge.md @@ -0,0 +1,779 @@ +# PR #2171: oracle challenge + +Reviewed source: `858b174a65f160d13d01f0324c7481cc8e4eba3a`. +This is a self-review. The author of the repairs designed these challenges +after inspecting the tests; they are not independent or held-out evidence. + +The initial task was to identify concrete oracle improvements. The initial pass +below proposes 15. Two have executed distinguishing experiments. The remaining proposals +identify source-inspected gaps and experiments still needed. They are not +confirmed production bugs or completed oracle repairs. No production change or +permanent oracle extension was made during this audit. + +The follow-up audit expands this plan to **26 checks** and maps **53 review +items plus four evidence caveats** in the +[review coverage matrix](2026-10-28-pr-2171-review-coverage-matrix.md). +That includes overlapping reports, non-runtime findings, and accepted product +choices. Three current failures now have executable witnesses: cached SSR +admission, hydration replacement of local option claims, and an unconfirmed +successful write in a documentation example. The follow-up evidence below +supersedes the initial pass's statement that it established no production defect. + +## Why the previous green results were insufficient + +The cache oracle compared admission alone. Its grammar coupled the two options +when both were present. The metadata oracle checked a broad set of query shapes +over a narrow history: one row, one held update, and successful settlement. +Its union fixtures always had an empty branch. The neighboring publication +oracle intentionally removed virtual fields before comparison. + +Those instruments provide useful evidence, but their combination does not +establish the interactions they omit. More runs of the same grammar would not +repair these omissions. Earlier counts such as 144 cache cases and 409 metadata +cases describe executions, not comprehensive protection of the contracts. + +Authority remains the maintainer decisions recorded in the existing owners, +the virtual-property contract in `docs/guides/live-queries.md`, and the live-query +architecture's Collection boundary and publication laws. These proposals do +not revise the accepted release policy, metadata attribution, or option +retention decisions. + +## Executed challenges + +All runs used the unchanged source revision above plus the stated temporary +edit. Source and test files were restored after each experiment. Raw logs are +task-local under `/private/tmp/rc2171-oracle-challenge/`. + +Command, from `packages/db`: + +```sh +../../node_modules/.bin/vitest run --config vite.config.ts \ + --coverage.enabled=false --typecheck.enabled=false \ + tests/live-query-cache-options-oracle.test.ts +``` + +### M1: reject only when every supplied option differs + +Location: `packages/db/src/client.ts`, `_assertLiveQuerySources`. +Keep the original `rowOptions.some(...)` check, but additionally require: + +```ts +rowOptions.every( + (option, index) => + (option === undefined && record.rowOptions![index] === undefined) || + option !== record.rowOptions![index], +) +``` + +This wrong rule accepts two callers with different `getKey` references when +their configured `schema` reference is the same. The approved rule rejects +either differing reference under the same reusable identity. + +The distinguishing driver edit keeps the schema reference unchanged in the +existing `option === 'both'` histories, while still changing `getKey`: + +```ts +schema: different && option !== `both` ? { ...schema } : schema +``` + +| Execution | Result | +| -------------------------------------------- | --------------------------------- | +| Unchanged production, existing grammar | 144 passed | +| M1, existing grammar | 144 passed: survivor | +| M1, distinguishing grammar | 12 assertion failures, 132 passed | +| Unchanged production, distinguishing grammar | 144 passed | + +The failures were missing synchronous conflict errors or fulfilled promises +where observer preload owed a rejection. They were not setup or timeout errors. +This demonstrates improvement 1; it does not calibrate every proposed option +presence or framework path. + +### M2: overwrite the claim before rejecting a conflicting caller + +Location: the conflict branch of the same method. Insert this immediately +before the conflict error: + +```ts +record.rowOptions = rowOptions +``` + +The first rejection still looks correct. A second attempt with the conflicting +options can now borrow the corrupted claim. + +The distinguishing checker invokes the rejected synchronous action again and +requires the same conflict rejection. It retains the original model and first +comparison. This experiment extends preload and subscribe retries; the async +observer-preload retry remains future work. + +| Execution | Result | +| ------------------------------------------------ | --------------------------------- | +| M2, existing grammar | 144 passed: survivor | +| M2, repeated-admission checker | 24 assertion failures, 120 passed | +| Unchanged production, repeated-admission checker | 144 passed | + +Failures occurred at the repeated-admission assertion. This demonstrates the +claim-preservation part of improvement 2. It does not establish row, listener, +or resource non-interference after rejection. + +## Concrete improvements + +### 1. Vary cache options independently, including absence + +**Owner:** `packages/db/tests/live-query-cache-options-oracle.ts`, its core +driver, and `packages/react-db/tests/cache-options-oracle.test.tsx`. + +**Gap:** the `both` cell changes both references together. First and second +callers always have the same option presence. + +**Change:** generate first/second declarations independently for each option: +absent, same reference, or different reference. Derive conflict from the pair +of declarations and query identity, rather than a single `different` flag. +Keep the default/default success case and both directions of absent/custom. +Use transforming schema fixtures where that boundary permits schemas. + +**Witness and cut:** cache `(keyA, schemaA)`; request `(keyB, schemaA)` under +the same identity. Reject at admission; a distinct query key must succeed. +Declare reach for each single-axis conflict and each presence transition. +M1 is the executed calibration for the first missing distinction. + +### 2. Compare cache state after rejection + +**Owner:** the same cache model and receiving drivers. + +**Gap:** the check ends at throw/rejection. It never asks whether the first +caller's result or resources remain usable. + +**Change:** extend each rejection history with a repeated conflict, a compatible +third caller, and a source update. Compare original keys, schema output, rows, +and surviving subscriber delivery. Record public subscriber ownership before +and after the rejected attach, without requiring incidental private layouts. + +**Witness and cut:** A establishes a result; B conflicts twice; A's compatible +peer still reads A's keys and receives the next update. No rejected listener +remains. Observe admission, the next source publication, and final cleanup. +M2 demonstrates one missing distinction. Also challenge a listener retained +before validation and a rejection that evicts the first result. + +### 3. Control pending delivery and vary which caller establishes the result + +**Owner:** the core cache driver and React receiving driver. + +**Gap:** the first caller is always explicit preload. The source is a ready +local-only Collection. Omitting `await initial` exercises immediate result +reuse, but does not hold source delivery or prove a transport-pending premise. + +**Change:** add an explicitly held source load. Assert that it started and +remains unresolved before the second caller. Cross preload-first and supported +observer-first registration with preload, subscribe, and render consumers. +Record reach separately for each path and pending/settled cut. + +**Witness and cut:** attach a conflicting observer while A's source delivery is +held; it rejects immediately. Release A and verify its exact result. A wrong +implementation that validates options only after readiness must fail before +the release. A healthy compatible peer must continue waiting and then succeed. + +### 4. Model option claims through hydration, failure, and consumption + +**Owner:** extend the cache option model using the public lifecycle paths in +`packages/db/tests/db-client-preload-identity.test.ts`. + +**Gap:** those paths already have source-object witnesses, but the row-option +oracle does not intersect them. A one-shot admission predicate cannot judge +when a claim begins or stops applying. + +**Change:** retain the first local declarations while a result is reusable; +model retirement when a failed or consumed result is no longer reusable. +Generate pending/settled hydration, first local claim, a later hydration chunk, +conflicting reuse, result consumption/failure, and replacement reuse. Server +function references must never become local identity claims. + +**Witness and cut:** hydrate, claim with A, hydrate a newer chunk, reject B; +consume or fail that stream, then allow a fresh B result. Compare public +dehydration, local row keys, and caller outcomes. Challenge both overwritten +local claims and claims retained forever. These experiments remain unrun. + +### 5. Receive option laws in the other actual framework bindings + +**Owner:** `packages/db/tests/conformance/contract.ts`, the shared conformance +suite, and each materially affected framework driver. + +**Gap:** the new option-specific receiving evidence is React. Source-ID and +general conformance evidence does not establish this option-specific handoff. + +**Change:** reuse the option judgment for independent DbClient callers through +each supported config entry point. Reuse inline-retention histories for +identity-comparing bindings, including Preact. Reactive bindings without a +DbClient follow their own documented recompilation law; do not impose React's +identity policy on them. Run real native scheduler cuts and render budgets. + +**Witness and cut:** a normal render retains a comparing binding's Collection; +identity replacement applies new options; a separate conflicting cached caller +rejects before returning rows. A compare-options-by-reference-on-every-render +mutant must fail in each applicable host, not just a controlled core driver. + +### 6. Compare complete metadata results and independently judged keys + +**Owner:** `packages/db/tests/query/virtual-row-fields-oracle.test.ts`. + +**Gap:** the 400 attribution histories read `live.toArray[0]`, check its label, +and compare metadata. Projected keys come from a clean sibling compiled by the +same machinery. Extra rows and shared key mistakes can escape that comparison. + +**Change:** have the model predict every selected ordinary field, every row, +and multiplicity. Compare complete arrays or multisets as the query contract +requires. For ordinary projected roots, add explicit `getKey` functions with +independent expected keys. Keep default-key differential checks as supplemental +evidence; do not invent an undocumented composite-key encoding. + +**Witness and cut:** two rows with distinct labels and model-derived projected +keys, followed by deletion of one. Observe preload and every publication. +Challenge an extra row, a missing non-first row, a lost `id`, and an incorrect +projected key shared by both compiler paths. None is calibrated here. + +### 7. Populate multiple union branches simultaneously + +**Owner:** the same metadata owner. + +**Gap:** every union attribution history has one populated branch and one +empty branch. Either branch position is covered, simultaneous contributors are +not. + +**Change:** generate nonempty branches with different source Collection IDs, +typed source keys, overlapping ordinary values, and independent pending writes. +Vary branch order, branch removal/reinsertion, and QueryRef consumption. Compare +all occurrences without collapsing them into a value-keyed Map. + +**Witness and cut:** A and B both contribute; hold a mutation only on B, then +remove A's last contribution. Each remaining row must retain its own branch's +attribution at pending and settled cuts. Challenge a union-wide first-source +metadata owner and deduplication of distinct contributions. Duplicate public +key handling must follow the existing key contract, rather than inventing a +winner. These experiments remain unrun. + +### 8. Change outer-join match topology during the history + +**Owner:** the nullable-spread grammar in the metadata owner. + +**Gap:** `matched`, spread order, and pending side vary, but match presence +remains static throughout each history. + +**Change:** generate unmatched → matched → unmatched → matched transitions +through join-key updates and legal delete/reinsert actions. Cross both spread +orders and retain an untouched second parent as an isolation check. Compute the +last present source from the authored query and independent source rows. + +**Witness and cut:** `{ ...a, ...b }` initially takes A's metadata, switches to +B when B matches, and returns to A after B leaves. Compare after each action +and inside its public callbacks. Challenge sticky ownership, first-spread +ownership, and clearing metadata when a later spread becomes null. + +### 9. Exercise attribution through rejection and overlapping transactions + +**Owner:** the metadata owner, receiving the settlement contract protected by +the optimistic-history owners. + +**Gap:** its attribution histories use one held update followed by fulfillment. +They do not combine attribution with rejection or two independently pending +source transactions. + +**Change:** generate fulfill/reject decisions, both settlement orders, and a +source update while an optimistic transaction remains held. Track authored +source snapshots and surviving overlays independently. Predict attribution +from the accepted source-versus-output rule at each cut. + +**Witness and cut:** mutate A and B in a joined query; reject one while the other +persists. The selected source's pending state and origin must follow the +remaining transaction. New projections still receive output metadata. +Challenge premature clearing of pending state and retention of the rejected +source's metadata. Do not change optimistic snapshot policy to fit output. + +### 10. Keep virtual fields in publication and downstream observations + +**Owner:** the metadata owner plus the observation machinery of +`packages/db/tests/query/includes-publication-oracle.test.ts`. + +**Gap:** the metadata owner checks snapshots at three cuts. The neighboring +publication oracle's `stripVirtualProperties` removes all `$` fields, so its +callback comparisons cannot supply metadata publication evidence. + +**Change:** add a separate metadata observation alongside the existing +ordinary-value projection. Capture callback payloads, synchronous Collection +reads, and downstream selections of the four virtual fields. Require callbacks +for histories that owe them. Preserve prior observations before later writes. + +**Witness and cut:** an optimistic source update changes label and pending +state in one publication. A callback must never see the new label with stale +attribution. Compare each layer at its own callback; do not require a downstream +layer to advance during its upstream callback. Challenge event-only stale +metadata and correct-after-a-microtask publication. + +### 11. Run the attribution grammar through Collection facades and inline controls + +**Owner:** the metadata owner, with a receiving driver using the facade +lifecycle histories owned by the includes Collection oracle. + +**Gap:** facade projection and inline collision witnesses are supplemental fixed +tests in `virtual-row-fields.test.ts`. They do not receive the broad attribution +grammar or exercise attribution through bucket departure and reactivation. + +**Change:** feed the same projection/collision model through root Collections, +Collection-valued includes, and inline-value controls. Vary active/empty/shared +buckets, route moves, child updates, and cleanup. Model facade lifetime +separately from row attribution; reuse the existing owner rather than creating +a competing lifecycle model. + +**Witness and cut:** two parents share one child facade; one leaves while the +child has a pending write. The remaining parent's facade retains the right +source/output metadata. Inline projected `$key` values retain authored shape. +Challenge using the parent Collection ID for projected facade rows and enriching +inline values as Collection rows. Calibration remains unrun. + +For A12's Effect claim, first inspect its public value contract separately. +An Effect does not publish through an output Collection. Do not infer an +output Collection ID law from the facade contract or treat every reserved +authored scalar as illegal. Its receiving driver must compare the contract +that actually applies; unresolved authority remains a limit of this plan. + +### 12. Observe metadata inside functional callbacks + +**Owner:** the metadata owner and +`packages/db/tests/query/includes-functional-projection-oracle.test.ts`. + +**Gap:** the metadata grammar's functional QueryRef consumer returns the row +directly. Its final output check does not prove a callback could not use spoofed +input metadata to compute an ordinary scalar. The functional-input owner checks +materialized values, but does not supply this reserved-field collision grammar. + +**Change:** generate a colliding inner projection through direct, QueryRef, +join, and union forms; capture public callback inputs and return an ordinary +`observedCollectionId` or pending badge derived from them. Keep the existing +rejection of compiled Collection-valued functional inputs. + +**Witness and cut:** an inner new projection selects a fake `$collectionId`; +an outer callback copies its observed value into an ordinary field. Assert the +legitimate metadata at callback invocation and on the resulting scalar. +An outer-publication-only sanitizer must fail this check. Sanitizing the final +reserved fields is insufficient once user JavaScript consumed the fake value. + +Maintainer decision, 2026-10-28: new projected subquery objects have their +reserved fields removed before a downstream `.fn.select()` callback. Direct +source rows retain source metadata. Inline include values retain authored +shape. This resolves O12's authority question; its receiving oracle remains +to be authored and independently reviewed before production repair. + +Subsequent clarification, 2026-10-28: structured whole-row source spreads also +retain genuine source metadata at callback entry. The stripping rule applies +to projections that create their own row shape; it does not override the +existing source-owned spread rule. + +### 13. Model release histories and receive them through packed artifacts + +**Owner:** a new release-law model adjacent to +`scripts/version-packages.test.mjs` and `scripts/publish-packages.test.mjs`. +Keep their useful fixed regressions as replay inputs or supplemental tests. + +**Gap:** version and publish tests use small separate workspaces and written +answers. The publishing callback records public names and prerelease state; +it does not observe the real Changesets command's registry operations or packed +dependency capabilities. Manifest peer strings alone cannot prove API support. + +**Change:** generate legal sequences of changesets, version runs, repeated +no-op runs, publish failures/retries, and RC exit. Independently model patch +suffixes, minor candidate increments, precedence, consumed changesets, stable +1.x lines, and dist-tags. Include candidate and patch counters around 9/10. +Run the command boundary against a local registry fixture, never npm production. +Pack the real package inventory and receive representative application paths +with supported DB versions. Check that excluded 0.15 combinations are rejected +and RC combinations can execute newly required APIs. + +**Witness and cut:** patch → patch → minor → repeated version → failed publish +→ retry → exit. Every packed manifest, selected tag, and consumed changeset +must match the model. Exercise Query failure reporting and offline outbox +restoration in installed fixtures. Challenge double-consumption, lexical RC +ordering, incorrect stable-package tags, and peers that claim absent methods. +The stable Query minor decision remains accepted policy, not an oracle defect. + +### 14. Generate type inputs from the same boundary descriptions + +**Owner:** `packages/db/tests/query/virtual-row-fields-oracle.test-d.ts`, with +ordinary mutation-handler type tests as supporting evidence. + +**Gap:** the metadata type file has reusable expected type aliases but applies +them to hand-authored concrete queries. That does not meet ORC-016's generated +legal-input requirement or cover the runtime grammar's composition axes. + +**Change:** generate legal TypeScript query inputs from row/value, projection, +nullability, union, and composition descriptions. A reusable independent law +predicts exposed metadata and nullability. Compile the generated inputs through +the public package exports, preserving diagnostic identity and source locations. +Add adapter handler-result forms with an independent Promise admission +rule; runtime legacy-value tolerance is a separate JavaScript promise. + +**Witness and cut:** vary a projected inline object, a whole-row child, a nullable +joined row, and discriminated union members without adding expected answers. +Reject enriched projected-value types, lost optionality, and accepted legacy +handler-result types. Do not use `any`, casts, or an unreachable callback to +manufacture a successful compile. These experiments remain unrun. + +### 15. Preserve oracle failures when teardown also fails + +**Owner:** both cache-option drivers and their shared test lifetime support. + +**Gap:** their `finally` blocks run cleanup sequentially. An early cleanup +exception can replace the comparison failure and stop later cleanup. The +metadata owner already uses `checkWithCleanup`, including a calibration test; +that protection does not extend to the cache drivers. + +**Change:** use cleanup support that attempts every owned release, preserves +the original law/checkpoint as the primary cause, and retains secondary errors. +Register partially acquired resources before subsequent steps can fail. + +**Witness and cut:** cause a cache comparison mismatch, make observer disposal +fail, and verify that view, client, and source cleanup still run. The reported +cause must remain the comparison mismatch. Also check a failed mount before an +unmount handle becomes available. This is harness evidence, not a new product +recovery promise. Its fault-injection calibration remains unrun. + +## Initial pass: implementation order and evidence limits + +Start with 1–2: both have demonstrated survivors. Then add 6–10, since those +histories exercise the metadata boundaries that repeatedly escaped review. +Follow with cache lifecycle/host receiving work, facade/callback receiving +work, and release/type boundaries. Apply 15 to each driver as it changes. + +For every implemented improvement, keep the old regressions, add the missing +grammar or observation, declare per-path reach, and run the unchanged baseline +before considering production changes. Demonstrate a relevant mutant failure +at the named comparison. Do not mark an item complete because this report +describes its experiment. No claim of universal correctness or zero future +reviewer findings follows from this audit. + +The two demonstrated gaps remain open in the checked-in oracle. The remaining +13 proposals still need calibration. No production defect was established by +the experiments here. Only core cache comparisons were mutation-tested; the +React and other native-host counterparts were not run with these mutants. + +After restoring every temporary edit, the core metadata and cache owners pass +together: 553 tests across two files. The existing release tests also pass: +11 tests. These are baseline checks; they do not implement the proposals or +establish full PR CI success. +`pnpm lint:changed`, the report's Prettier check, and `git diff --check` pass. +pnpm reports the existing node_modules/lockfile override mismatch; this audit +did not install dependencies or change the manifest or lockfile. + +## Follow-up: make the plan account for every review item + +The original 15 were insufficient. They concentrated on cache and metadata +interactions. They did not explicitly preserve several already repaired laws, +exercise documentation behavior, or require clean build and exact-head evidence. +Runtime oracles also cannot decide a maintainer's version policy or establish +that unused code has been removed. + +The matrix preserves A01–A15 from the first supplied review, B01–B10 from the +next, C01–C15 from the latest supplied review, G01–G11 from GitHub review +comments, and W01–W02 from the bot's pre-merge warnings. B10 and C01 retain +both of their subclaims. Overlaps retain separate IDs. Four source caveats +remain explicit. The bot's reach-wording suggestion is separate from its +remaining `isPersisted` comment; the first inventory accidentally conflated them. + +No new product decision is needed for these checks. The accepted policies +remain the authority. A request to identify sufficient improvements does not +make proposed checks into implemented coverage. Confirmed failures remain open. + +### 16. Check cached server reads before any subscription + +**Owner:** the shared cache option model and core driver, with a React +`renderToString` receiving driver alongside `cache-options-oracle.test.tsx`. + +**Law and gap:** independent callers cannot borrow conflicting local row +behavior. The current owner reaches preload, observer preload, subscription, +and client render; it does not reach a cached `getServerSnapshot()` before +subscription. An SSR hook never subscribes, so that later guard cannot help. + +**Change:** generate cached/uncached server reads, source-only/query preloads, +same/different/absent options, and keyed/derived identities. Reuse the admission +model. Require a cached row to be present before the conflicting read. Add a +browser hydration control under the same law, without inventing transportable +function identity. + +**Witness and cut:** preload A, then server-render B with the same identity and +a different `getKey` or schema. Reject before returning A's snapshot. A same-option +caller succeeds. A guard placed only in subscription must fail. The core and +actual React server paths both fail this comparison on the current head. + +### 17. Preserve mutation outcomes and explicit confirmation across adapters + +**Owner:** Query's mutation histories in `ownership-lifecycle.oracle.test.ts` +and Electric's handler histories in `electric-oracle.property.test.ts`. + +**Law and gap:** the handler's fulfilled/rejected outcome remains its outcome; +legacy return values are ignored at runtime. Query performs no automatic +refetch. A confirmed write survives settlement; an unconfirmed optimistic row +reverts. Types reject non-void handler results. These are approved decisions, +not an invitation to reinstate old automatic behavior. + +**Change:** keep the existing 18 Query and 12 Electric histories in the required +validation set. Extend their grammar across CRUD, fulfillment/rejection, +legacy/void results, and no confirmation versus explicit refetch/direct write +or Electric acknowledgement. Cross delayed confirmation and ordered consumers +where those existing owners apply. Compare exact handler error identity, fetch +counts, public rows, and transaction outcome while held and at settlement. + +**Witness and cut:** a successful server write followed by a legacy result +must not become a caller failure. A Query handler with no confirmation starts +no fetch. A documented successful-write example must use a confirmed history. +Earlier records calibrate the Electric rejection wrapper and Query automatic +refetch removal; this audit does not newly implement those owners. + +### 18. Enforce external completion through transaction state and Offline restore + +**Owner:** `optimistic-history-oracle.ts`, its outcomes driver, and the real +Offline restoration/settlement receiving paths. + +**Law and gap:** external completion is admitted only for pending work. It +cannot turn failure into success or override persisting/completed work. The +first 15 proposals omitted this law even though the previous review added its +missing action to the primary model. + +**Change:** retain pending/persisting/completed/failed starting states and +repeated completion attempts. Intersect them with restored outbox work, peer +ownership, and mutation outcomes that the Offline owners already express. +Compare public rows, terminal outcome, and transaction state after each action; +observe outbox deletion and caller settlement at the receiving boundary. + +**Witness and cut:** fail a transaction, attempt external completion, and +require rejection without changed rows/outcome. Restore permitted pending +work as a compatible control. Removing the admission guard previously failed +four state histories. Packaged API availability is a separate O13/O22 check. + +### 19. Release descriptor deferrals after failed or uncommitted renders + +**Owner:** React's source-ID reuse oracle and descriptor-query binding driver; +use shared conformance receiving paths where another framework owns this law. + +**Law and gap:** a rejected render cannot leave a descriptor Collection unable +to start for a later consumer. Mounted identity retention does not prove +partial-construction cleanup. The existing React owner already covers rejected +source-ID reuse, Suspense, infinite hooks, and multiple throwing starts; retain +that evidence rather than claiming a new model is needed. + +**Change:** inventory render preparation steps that acquire deferrals. Generate +failure before/after each acquisition, suspended and abandoned renders, and +subsequent direct readers. Reuse the existing lifecycle judgment. Register +cleanup for partial construction and require per-hook reach. + +**Witness and cut:** reject preparation after deferring a shared descriptor; +a later direct preload must reach ready and expose the authored row. A second +source throwing during release must not prevent a healthy third source from +starting. Mutants that omit release or stop after one failure must be rejected +at those public cuts, rather than by an unrelated cleanup error. + +### 20. Observe proxy expressions through diagnostics and actual UI consumers + +**Owner:** the virtual-field query law, index-suggestion tests, and a receiving +witness for the React Native shopping-list projection and badge. + +**Law and gap:** the selected ordinary pending indicator must follow the source +row. JavaScript `!proxy` is a constant, not a query expression. An outer reserved +field receives output metadata. Correct generic metadata cannot prove that the +example actually consumes the ordinary field. + +**Change:** generate pending/settled and true/false source states through +supported predicate, order, and projection expressions. Check both actual +result variation and the intended diagnostic path. Compile the example and +receive those states through its actual component, not a copied projection. + +**Witness and cut:** hold a list write, observe the Saving badge, settle it, +and observe the badge clear. Ordinary projected `hasPendingWrites` must vary +while output `$hasPendingWrites` remains governed by the projection law. +Calibrate a JavaScript-negation replacement and a badge reading the reserved +property. Static wiring plus a core query test is useful but does not establish +native component execution. + +### 21. Execute authored documentation and check migration/API consistency + +**Owner:** `scripts/check-doc-snippets.mjs`, package type checks, and behavior +receivers reusing the relevant mutation/query models. Handwritten guide and +skill references are in scope; generated reference output remains CI-owned. + +**Law and gap:** runnable guidance must resolve public exports and demonstrate +the behavior it promises. Existing removed-symbol searches and snippet +compilation cannot detect a successful POST whose optimistic row disappears. +An existing snippet baseline can also preserve a newly introduced diagnostic. + +**Change:** extract the actual fenced snippet, supply only its documented +external collaborators, and run its successful and failing histories through +the matching model. Compile examples and skills against exports. Reject new +removed-symbol references outside explicitly marked historical/before examples; +compare diagnostic identities against the baseline without expanding it to +waive new regressions. Check prose for source/output attribution, explicit +confirmation, internal helpers, stable-minor upgrade costs, and removed APIs. + +**Witness and cut:** the documented POST succeeds and the server owns the row; +at transaction settlement the Collection must show the confirmed row. An +unexported migration helper fails compilation. A remaining `isPersisted` +reference fails the current-API check. The successful POST witness is RED at +this head; broader generated docs receiving coverage remains proposed. + +### 22. Validate the actual workspace before versioning and packed consumers after it + +**Owner:** workspace install/build/typecheck CI, actual package inventory, and +the release model's package-consumer receiving fixture. + +**Law and gap:** a truthful published peer range does not guarantee a local +workspace can build before the RC versions are written. Existing node_modules +can retain a symlink that a clean install will not create. The first plan's +packed-consumer checks omitted that pre-versioning boundary. + +**Change:** use a disposable, clean, frozen install and the repo's package +build graph. Resolve every imported workspace API, including declaration/test +inputs and examples. Check the actual non-private inventory against the RC +changeset after main integration, including Convex and Preact. Verify local +workspace dev dependencies independently from published peer ranges. Then +version, pack, install, and execute supported consumers through O13. + +**Witness and cut:** Offline has an RC-only peer while the local DB is still +0.15.2; workspace builds must resolve the local source via a workspace build +dependency. A persisted Query handler returning `{}` must fail the new void +contract's typecheck. Neither failure is established by an already linked +runtime test. Both source findings remain open; no fresh install was run here. + +### 23. Check dead exports, branches, and duplicated wrappers with source evidence + +**Owner:** production caller/export inventory, TypeScript control-flow checks, +and the shipped-byte comparison. This is a static review check, not a runtime +state model. + +**Gap and change:** behavior tests cannot reject an unused warning helper, +an impossible SSR branch, an unused recompile mode, or equivalent duplicated +wrappers. Enumerate callers and exported uses before deletion; check narrowed +types and every framework's policy modes. Compare consolidated wrappers/types +and run their unchanged behavioral owners. Do not delete a shared policy merely +because React alone supplies a constant argument. + +**Witness and cut:** leave an unreferenced warning export or dead mode in the +production inventory; require the source check to report it. Preserve a real +reactive-framework caller as a negative control for overbroad deletion. Compare +size after consolidation, without declaring a harmless internal branch a +product failure. + +### 24. Measure repeated work independently from result correctness + +**Owner:** a narrow compiler work probe and `pnpm size:compare`, supplementing +the metadata/functional-projection owners. + +**Gap and change:** correct rows do not expose an alias-array or copy-closure +allocation per row. Instrument the relevant compile/evaluation site or use a +source/AST placement check. Compile once and process increasing row counts; +require compile-invariant source discovery to happen once per compilation. +Count copies only under the conditions that require a copy. Keep output +comparisons alongside these counters. + +**Witness and cut:** hoistable alias discovery or closure construction inside +the row loop fails its deterministic count. An allocation required by a changed +row remains permitted. This is a proposed engineering work check; no new +wall-clock performance promise or measured slowdown is claimed. + +### 25. Receive coded diagnostics and public APIs through published build artifacts + +**Owner:** production error census, `scripts/test-production-errors.mjs`, and +`scripts/test-minified-db.mjs`, with affected adapter artifact checks. + +**Law and gap:** adding a plain Error can bypass coded diagnostics and ship its +full text. Source-only testing does not establish production message erasure +or public export/name stability. The original plan omitted these existing +independent observations. + +**Change:** include newly reachable identity/mutation errors and all affected +packages in the development census. Build actual ESM/CommonJS and minified +consumer entries; compare error codes/names and test message erasure. Keep +intentionally removed errors distinct from missing public exports in docs. + +**Witness and cut:** an uncoded preparation error is reported by the census; +a newly introduced literal survives production bundling and fails erasure. +A documented helper missing from the public entry fails O21/O22 compilation. +Use existing calibrated owners; no new artifact run was performed here. + +### 26. Tie all validation to the exact head and the path actually exercised + +**Owner:** a validation manifest joining package/test inventory, oracle reach, +build fingerprints, and review-item reconstruction. This is execution gating, +not a universal correctness model. + +**Gap and change:** three reported failures were in tests already on the PR. +They needed an ordinary full affected-test run. Other review runs lacked +Changesets dependencies, loaded stale DB builds, or reviewed a moving branch. +Record source head, build inputs/dist age, resolved DB entry/copy, test selection, +exit status, and per-host comparison reach. Run every affected changed test; +include newly merged packages. Rebuild before adapter receiving tests. A new +head invalidates affected evidence. Preserve every raw review ID, outside-diff +comment, additional suggestion, and setup caveat in the validation inventory. + +**Witness and cut:** restore each stale assertion and require its own test to +fail. Remove a required host from the test selection and require the missing +reach to fail validation. A second/stale DB or absent Changesets dependency +must produce invalid setup, never a GREEN product result. Unrun checks remain +unrun even when another suite has thousands of passing cases. + +## Follow-up experiments at unchanged `858b174a6` + +These were temporary production drivers; no source repair or permanent test +extension was retained. The core/React drivers reuse +`checkCacheOptionAdmission` from the existing independent cache model. They +vary `getKey`, schema, and both, each with same/different references. Fixture +schema validation is an identity operation; these probes judge declaration +admission, not schema transformation. + +| Path and named checkpoint | Expected under the approved law | Observed comparison | +| --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------- | +| Core `getServerSnapshot()` after a one-row query preload, before any subscription | Same options succeed; conflicting options throw before returning cached rows | Three assertion failures, three compatible controls pass | +| Actual React `renderToString` with `DbProvider` and `useLiveQuery`, same cached premise | Same admission rule, through the real server host | Three assertion failures, three compatible controls pass | +| `hydrate()` with a newer stream timestamp after local preload, then another preload | Preserve the original local claim and reject conflicts | Three assertion failures, three compatible controls pass | +| Actual error-handling guide's successful POST handler, then `when('settled')` | Server row and Collection row agree after documented success | One row on the server, zero in the Collection: assertion failure | + +The server runs stub `window` as undefined and do not subscribe. The hydration +run dehydrates the established query, checks its row exists, hydrates a stream +with `dehydratedAt + 1`, and checks that the newer timestamp replaced it before +admission. This distinguishes the real replacement path from ignored hydration. +The documentation run extracts the fenced setup before `// Usage` in +`## Transaction Error Handling`, transpiles it, and evaluates it with the real +Query Collection/QueryClient and a successful in-memory fetch collaborator. +It preloads an empty server, inserts the documented todo, awaits settlement, +and compares all authored ordinary fields with the server's independent row. + +Commands used the root Vitest binary from each package, `--config vite.config.ts`, +`--coverage.enabled=false`, and `--typecheck.enabled=false`. Temporary test +paths were `tests/cache-review-coverage-probe.test.ts` in DB, +`tests/cache-server-review-probe.test.tsx` in React, and +`tests/doc-success-review-probe.test.ts` in Query. Drivers and logs are retained +in `/private/tmp/rc2171-oracle-challenge/` for this task. These paths are scratch +artifacts, not checked-in replay interfaces. The witness construction above +states the inputs and observations needed to reconstruct them. + +The first documentation driver used a non-file `import.meta.url` under Vite +and failed setup. That run supplied no product evidence. Reading the actual doc +from the package working directory reached the row comparison and failed there. +All other RED results above were intended assertion failures, not timeouts or +teardown errors. No repair GREEN or hostile-mutant result is claimed for these +three current defects. + +The existing external-completion, core option-identity, and index-warning files +also pass at this head: 58 tests across three files. Earlier review records +supply their historical RED/GREEN evidence. This bounded baseline does not +execute the new SSR/hydration histories in the checked-in test suite. + +## What the expanded plan does and does not establish + +Every identified review item has a concrete check and a distinguishing witness +in the matrix. Behavioral findings map to independent laws and receiving paths; +source/build/documentation findings map to appropriate checks. Accepted release +policies remain explicit costs, rather than defects a test should reject. + +This is **plan accounting**, not demonstrated interception of all 53 items. +The expanded checks would need implementation and replay against each original +failure or a suitable mutant before making that stronger claim. Known gaps +remain assigned to their existing owners in the coverage map. The current SSR, +hydration, and documentation counterexamples prevent a readiness or bug-class +closure claim. No production fix, permanent oracle extension, commit, push, +generated-document edit, dependency installation, or lockfile change was made +in this audit. diff --git a/docs/contributing/oracle-reviews/2026-10-28-pr-2171-oracle-chunks.md b/docs/contributing/oracle-reviews/2026-10-28-pr-2171-oracle-chunks.md new file mode 100644 index 0000000000..4188a7d895 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-28-pr-2171-oracle-chunks.md @@ -0,0 +1,2841 @@ +# PR #2171 oracle implementation chunks + +Production baseline: `858b174a65f160d13d01f0324c7481cc8e4eba3a`. +All entries below describe test/document changes on that unchanged production. +The maintainer requires every oracle chunk to finish before production repair. +RED results are therefore retained; no product-readiness or bug-class closure +claim is made. The 26-check proposal and 53-item matrix remain the work inventory. +Generated API documentation is excluded. + +## Chunk 1: independent cached options and public observations + +**Law and authority.** The maintainer's independent-caller decision requires +same-identity getKey/schema declarations to agree by reference, including +absence. Distinct query keys permit independent behavior. SSR initial views +remain frozen at their capture cut (`docs/guides/ssr.md`, “Prepare every server +snapshot”). Mounted inline retention is a separate owner. + +**Inventory and grammar.** Six declaration pairs per option cross independently +with derived/keyed identity and same/distinct key: 144 bounded histories. Core +adds held/settled writes and five paths: explicit preload, observer preload, +subscribe, cached server read and preload after positively observed newer +hydration replacement. React supplies mounted hooks and actual renderToString; +these are separate receiving paths, never inferred from a core result. +Schemas test runtime declaration identity only; no transform/type support is +claimed. The domains are exhaustive at these bounds, so ORC-007 random campaigns +do not add a missing finite case. A key cannot change under the same model +identity without a conflict; a distinct key is its legal neighboring control. + +**Observations and reach.** Each core path reaches 288 histories, including 144 +held deliveries. Each path reaches 32 getKey-only, 32 schema-only and 64 both +conflict premises; 144 distinct-key controls; getKey/schema addition 48 each and +removal 24 each. These premise counts are recorded before the actual admission +comparison; a failed comparison does not claim later checkpoints ran. Preload, +observer preload and subscribe each complete admission/retry/original reuse/ +source-update comparisons in all 288. Server-read completes those in 224; +hydrate-preload in 160. Each React receiver reaches 16/16/32 independent/both +conflict premises, 72 distinct-key controls, additions 24 each, removals 12 each. +Client admission/retry/original reuse and settled keyed-row comparisons run in +all 144 histories. SSR completes admission/retry/original reuse in 80. + +Original custom keys use `first-id`, second custom keys use `other-id`, and the +default uses `id`; losing any declaration is observable. Both successful callers +and the original after rejected retries retain exact keys and ordinary fields. +Core checks every callback after the next atomic source publication and no +callback for a refused listener. React checks accepted rows immediately and +after its async act flush. The server held-row check compares the initial empty +view, rather than incorrectly requiring later writes to rewrite the capture. + +**Baseline result.** Run the root Vitest binary from each package with +`run --config vite.config.ts --coverage.enabled=false --typecheck.enabled=false`. +Core selector: `tests/live-query-cache-options-oracle.test.ts`: 192 assertion +failures and 1,314 passes (1,440 histories plus 66 checker controls). The failures +are missing conflicts at 64 settled SSR and 128 held/settled hydration admissions. +React selector: `tests/cache-options-oracle.test.tsx`: 64 SSR assertion failures, +224 passes; zero scheduling warnings after the native handoff correction. +`tests/utils.test.ts`: 132 passes, including three cleanup-failure positions. +DB/React TypeScript checks pass. These are intended assertion failures, not setup, +runtime or timeout evidence. Scratch logs: `chunk1-core-final.log`, +`chunk1-react-settled.log`, and `chunk1-core-rereview.log` under +`/private/tmp/rc2171-oracle-challenge/`. Set +`TANSTACK_DB_ORACLE_STATISTICS=1` to print the reach counts. + +**Calibration.** The shared comparison rejects accepted conflicts on both retry +attempts, wrong second/original keys, and an earlier wrong callback followed by +a correct callback. The prior challenge record retains executed production +mutants for all-options-must-differ and overwrite-before-throw guards. Those +historical experiments are not fresh mutation evidence for the expanded paths. +The cleanup fault injection varies the failing release position and checks the +original mismatch remains the cause, the secondary failure stays distinct, and +all later releases run. No production edit, even a temporary repair, was made. + +**Review.** Independent subagents `cache_oracle_review` and +`cache_receivers_review` applied oracle-review. Their first rounds found +last-only callbacks, weak reach cells, indistinguishable original keys and a +missing React settled observation. These are corrected. Final rounds found no +further supported defect in this bounded scope. The receiver reviewer separately +ran the three cleanup controls: three passed. Challenges that shaped this chunk +are retained evidence, not held-out evidence. + +**ORC accounting.** 001–005: authority, independent declaration model, literate +layers, bounded reference/presence grammar and public receiving cuts described +above. 006: wrong-observation controls described above. 007: finite exhaustive +grammar, random/shrink campaigns inapplicable. 008: stateless comparison, no +stateful transition revision. 009: declaration labels denote references or +absence, not production cache state. 010: fault injection and all-resource +cleanup. 011: no concrete shared semantic hypothesis requiring a second model; +the accepted reference rule is independent of hash/guard internals. 012: this +versioned record, without a closure claim. 013: single-axis conflicts and added/ +removed neighbors distinguish wrong coupled rules. 014: actual React client and +server receiving paths; broader hosts remain pending. 015: per-path counts +above. 016: oracle tests use shared judgments; cleanup examples stay in ordinary +utils tests. + +**Remaining work.** O01 and O02 are implemented within the declared boundary; +O03 held-delivery and O16 cached-SSR witnesses are implemented, while O03 +first-registrant variation and O04 failure/consumption/transport remain pending. +O15 has generic cleanup fidelity evidence but is not closed for every partially +constructed native host. Other frameworks, hydrateRoot, semantic equivalence of +functions and schema transformations remain outside this chunk. Product GREEN +and fresh production-mutant campaigns are deferred until all oracle chunks +finish. The reachable SSR/hydration counterexamples keep the product class open. + +## Chunk 2: hydrated claim lifetime and framework receiving paths + +**Law and inventory.** A portable result contains no server getKey/schema +references. Its first local caller claims them; a newer payload retains those +claims, while failure or consumption retires them. Authority: the maintainer's +option decision, the source-claim lifetime in `db-client-preload-identity.test.ts`, +and the SSR guide's matching-result configuration requirement. React and Preact +compare inline query identity; Preact has no DbClient hook input. Svelte is the +other public client-backed hook. Vue, Solid and Angular expose no DbClient input; +their client-free reactive compilation policy is not changed into React retention. + +**Model, grammar and observations.** The lifecycle owner uses one independent +active/retired judgment over named declarations. It runs 2,160 bounded histories: +72 same-identity declaration pairs, three first local claimants (preload, +observer preload and server read), active/replaced/failed/consumed results, +pending/settled transport, plus replacement and failure before original pending +transport release. Server configuration has equivalent key/schema semantics +with independent references. Actual server dehydration is scanned for callables +before a JSON round trip, so serialization cannot silently hide leaked claims. +Pending conflicts/retries are checked before delivery; compatible promises stay +pending. Newer payloads have different ordinary labels. A pending replacement +is checked immediately and again after obsolete transport settles. Initial +transport failure checks exact error identity on the claimant and compatible +pending peers, then fresh local admission. Consumed/failed results accept the +new declaration and expose its independent local keys after live handoff. + +Per first-claimant path: 720 portable premises, 432 pending premises; +384 pending-conflict and 48 pending-compatible histories reach comparison. +Pending replacement reaches 64 conflicts and eight compatible controls; +pending failure reaches the same split. All 72 pending-failure histories reach +failed-stream absence and retired reuse. On the unchanged RED baseline only +eight compatible pending replacements reach the post-obsolete comparison; +64 stop earlier at the intended missing-conflict assertion. The later check is +present, but no evidence claims those blocked conflict histories completed it. +Phase × conflict/compatible reach prevents borrowing a retirement witness from +an active-result case. Full counts print with TANSTACK_DB_ORACLE_STATISTICS=1. + +**Result and calibration.** The DB lifecycle selector +`tests/live-query-cache-lifecycle-oracle.test.ts` gives 640 admission assertion +failures and 1,526 passes, including six checker controls. There are 576 missing +replacement conflicts and 64 settled server-first missing claims. No setup, +timeout or unhandled-error failure is counted. The shared judgments reject +leaked callables, a no-claim policy and a forever-claim policy. The same pending +and replacement comparisons observe the actual open product defects; production +GREEN and fresh repaired-production mutants remain deferred. + +The shared immutable capture/checker in `live-query-bound-options-oracle.ts` +is received by the existing 12 React retention histories and six new Preact +histories. Each Preact history reaches rerender, source update and changed +identity; each option has two histories and each identity mode three. A render +budget rejects rebuild loops. Six hostile in-place config replacements prove +that mutating the original Collection cannot mutate the expected references. +React cleanup now attempts every returned mount's release while retaining the +primary mismatch. Svelte receives the 144 independent cached-option histories +through actual effect roots and flushSync; all admission/retry/original reads +pass, including 16 key-only, 16 schema-only, 32 both conflicts and 72 distinct +key controls. It compares accepted keyed rows after native effect delivery. + +Commands use each package's root Vitest binary/config with coverage/typecheck +disabled for runtime. Preact selector `tests/bound-options-oracle.test.ts`: +12 passed (six host histories plus six controls). Svelte selector +`tests/cache-options-oracle.svelte.test.ts`: 144 passed. React selector +`tests/source-id-reuse-oracle.test.tsx -t 'retains inline options'`: 12 passed, +80 other histories outside that selector. DB/Preact/React TypeScript checks and +Svelte's test:types pass. Lint:changed passes with only the pre-existing installed +lockfile/override mismatch warning. Scratch logs use `chunk2-*` under the task's +existing scratch directory; lifecycle final is `chunk2-lifecycle-r4.log`. + +**Review and ORC accounting.** Fresh independent reviewers +`cache_lifecycle_review` and `framework_options_review` applied oracle-review. +Their findings added before-release pending comparisons, pending replacement, +phase/schedule-specific conflict reach, equivalent server keys, immutable option +capture, and failure-preserving React cleanup. The lifecycle reviewer reran the +final full suite and confirmed the same 640/1,526 split. The framework reviewer +independently ran all three host selectors. Final reviews found no additional +supported defect within these boundaries. + +ORC-001–005: authority, independent active/retired rule, literate responsibilities, +finite legal timelines and positively observed native/transport cuts above. +006: callable, opposite-policy and in-place-mutation controls. 007: bounded +exhaustiveness rather than random sampling. 008: retaining a claim distinguishes +changed-declaration reuse; failure/consumption removes it, with active neighbors +proving the distinction is needed. 009: declaration labels denote local refs; +portable results and local claims remain distinct. 010: error identity and all +returned-resource cleanup; generic throwing-cleanup calibration is in chunk 1. +011: no remaining named shared semantic hypothesis requiring a second model. +012: this record preserves open product results. 013: active/retired and +before/after transport neighbors. 014: actual Preact/Svelte schedulers receive +the shared judgment. 015: per-path phase/schedule reach above. 016: all oracle +cases replay common judgments; controls supply hostile observations to them. + +**Limits and next work.** This completes the declared O04 transport, first-local +claim and retirement timelines, and O05's applicable native receiving paths. +It does not prove arbitrary stream interleavings, hydrateRoot, Svelte SSR, +Svelte continuing source publications, schema transformations, or cleanup before +a native mount returns. O15 still needs partial-construction fault injection; +O03 first result registration without an existing transported result remains +separate. These limits remain owned by the cache/binding owners. No production +file, dependency installation, generated doc, commit or push changed. + +## Chunk 3: complete metadata results and populated unions + +**Law and authority.** Public virtual-row properties and ARCHITECTURE's facade +boundary require structured whole-row/spread and functional direct-source +results to retain source metadata. New projected objects receive output +Collection metadata (maintainer decision). Authored reserved names cannot choose +attribution. This chunk extends O06/O07's snapshot boundary; it does not claim +callback, facade-lifecycle or functional-input coverage. + +**Model and legal grammar.** Authored source facts and pending-key sets predict +every ordinary selected field and all four virtual fields. No source row or +compiler classifier supplies expected values. The 1,056 attribution histories +retain all prior compositions, add a second source row and its deletion, and +cross independently configured keys with default-key differential checks. +Only the latter consult a sibling query for undocumented compiler key encoding. +Both boolean collision values and legal local origin distinguish authored +values from output metadata. Direct-source callbacks cannot add collisions; +that nearby invalid combination is excluded by grammar. Collisions cross +single/joined sources and direct, recursive, joined, functional and union +consumers, so removing any such axis loses its receiving path. + +Another 264 histories populate two union branches with numeric 1 and string +"1", different source IDs and equal labels. They cross branch order, QueryRef +consumption, either pending branch, five projection shapes and reserved-name +collisions. A peer is deleted and reinserted while the other branch persists; +settlement without confirmation restores its independent authored base. The +custom public key preserves typed identity, avoiding an invalid duplicate-key +winner policy. Model objects never cross into production without copies. + +**Observation and calibration.** Public toArray snapshots are compared at +preload, held mutation, peer deletion/reinsertion and settlement. The comparison +normalizes only unspecified order and retains duplicates, every ordinary field +and all metadata. Fourteen wrong-result controls cover both source/output +policy and reject omitted/duplicated second rows, missing ordinary IDs, wrong +keys, union-wide ownership and stale pending/origin. Allowed reverse order +passes. These are checker calibrations, not fresh production mutants. Full +production repair and its RED/GREEN/mutant record remain deferred by instruction. + +**Reach and unchanged baseline.** 1,343 tests pass: 1,320 attribution histories, +eight prior nullable-spread histories, one prior classification case and14 +checker controls. Multi-row/deletion reach1,056 each, independent-output-key +reach384, simultaneous populated/typed/reinserted union reach264 each, and +true-authored output-pending discrimination reach192. Each of24 union +path × source/output policy × pending-side × held/reinserted/settled cells +reaches18 source-owned or48 output-owned histories. The oracle asserts all +these states; a missing receiving path cannot borrow another path's count. +Command: package-root Vitest with config, coverage and runtime typecheck flags +listed above, selector tests/query/virtual-row-fields-oracle.test.ts. Final +log: chunk3-metadata-collisions.log. DB tsc --noEmit was clean before the final +collision-value grammar addition; the final type/lint check is recorded below. + +**Independent review.** metadata_complete_review and metadata_union_review +applied oracle-review and ran the tests independently. Findings corrected: +position-dependent pending wait, source/model object sharing, aggregate reach, +missing row2 metadata controls, and false-only authored pending collisions. +Final union review independently passed1,343 tests with all required reach +(chunk3-union-review-final.log). No further supported executable defect was +found in this bounded scope. + +**ORC accounting.** 001–003: authority, independent authored-fact judgment and +literate boundaries above. 004: reconstruction of prior forms, meaningful +composition/key/branch/collision axes, typed-key margins and direct-return +collision exclusion. 005: full public snapshots at positively reached cuts. +006:14 calibrated controls, including legal reordered positives. 007: finite +exhaustive grammar, random/shrink campaigns inapplicable. 008: direct +recomputation; pending facts distinguish held/settled rows. 009: authored facts +and pending-key sets denote source state and active optimistic overlays. +010: held gates settle before all-resource failure-preserving cleanup. +011: independent configured keys address the shared-default-key hypothesis; +no further named shared fault requires another formulation. 012: this record +and updated coverage map preserve limits. 013: true/false collisions and +source/output, either branch and peer-return neighbors distinguish wrong laws. +014: controlled core only, no real-provider/framework handoff claim. 015: +per-path counts above. 016: new histories and controls use the shared model/check. + +**Limits.** Dynamic join matches, rejection/overlap, callback/downstream +metadata, facade activation/retirement, functional callback inputs and generated +public types remain separate work. Default compiler-key encoding remains a +supplemental differential observation. No production file or generated doc +changed. No commit or push was made. + +## Chunk 4: join transitions, independent write outcomes and publication + +**Law and authority.** The accepted source/output attribution rule applies at +each publication. ARCHITECTURE's coherent-publication law requires installed +reads and change payloads to agree; downstream callbacks are judged at their +own layer, without requiring downstream advancement inside an upstream callback. +The optimistic-history owner supplies settlement and first-queued-confirmation +origin rules. This is O08–O10's bounded core receiving witness. + +**Model, grammar and observations.** A full left join is recomputed from +independent authored rows and separate optimistic snapshots. The driver has +112 finite histories: five projection shapes, spread order where meaningful, +join-key versus membership transitions, independent success/rejection with +separate errors, and either settlement order. A held left write spans unmatched, +matched, unmatched and rematched cuts. A right write then overlaps and receives +a queued same-key source update. The unconfirmed left row restores its base; +the right source confirmation becomes local only after successful work. A later +remote write distinguishes consumed attribution. An untouched parent remains. + +Every action compares full root/downstream snapshots and every recorded +callback's installed read, change value and previous value. Raw metadata is +captured at callback time. Deltas across the entire callback trace must equal +the independent before/after delta; message multiplicity survives capture. +Batch partition is permitted. Initial-state subscriptions establish subscriber +replicas before update classification is judged. The first attempted driver +omitted that premise and got valid insert messages for first delivery; that was +a fixture error, not product RED. Another run failed only on undeclared reach +names; that was harness setup, also not product evidence. + +**Reach and unchanged baseline.** Full owner: 1,463 passed, including prior +1,343 cases,112 histories and eight new checker controls. For each projection +shape/topology, all rematched-held/two-pending/first-settled/both-settled/remote +cuts and root/downstream held callbacks run. Spread shapes reach16 histories +per such cell; other shapes reach8. Either rejection reaches8/4, both rejection +reaches4/2, and either settlement order reaches8/4 respectively. All120 required +states are asserted by createOracleReach. Final runtime log: +chunk4-metadata-r2.log. Commands use the same package Vitest invocation as above. +No unchanged-production defect was found in these histories. Controls reject +stale installed metadata, stale event origin, wrong previous values, duplicated +messages/whole batches, missing messages/callbacks and an earlier bad callback +followed by a correct final read. No production-mutant or repaired-production +GREEN is claimed. + +**Review.** Fresh metadata_timeline_review and metadata_publication_review +applied oracle-review. They identified duplicated whole batches passing the +checker, grouped unsubscribe cleanup, and missing both-reject histories with +separate caller errors. All are repaired. Both independently passed1,463 tests +(chunk4-metadata-independent-r2.log and chunk4-publication-review.log). The final +bounded verdict found no further supported executable defect. + +**ORC accounting.** 001–003: authorities and literate, independent recomputation +above. 004: initial absence/presence, both topology transitions, meaningful +spread order, caller outcomes and settlement order distinguish the grammar; +missing-key updates and same-source overlap are excluded from this domain. +005: public snapshots, callback reads/payloads and downstream scalar projection +at named cuts. 006: eight wrong-observation controls, including duplicate traces. +007: exhaustive finite grammar, random/shrink campaigns inapplicable. +008: separate base/overlay/pending/origin facts distinguish acceptance, +settlement and later writes. 009: model facts map to applied source rows, +optimistic snapshots and pending writes, not D2 graph state. 010: both gates +settle and independent subscription/Collection cleanup entries attempt all +resources; capture preserves earlier metadata. 011: no identified shared +semantic fault needs a second formulation. 012: this record and coverage map. +013: matched/unmatched, held/settled, local/remote and separate errors distinguish +nearby wrong rules. 014: controlled core scope; no provider/framework handoff. +015:120 path states above. 016: histories and calibrations use shared judgments. + +**Limits.** Same-source overlapping writes, truncate, dynamic joins beyond LEFT, +provider/framework scheduling, facades and partial-construction cleanup remain +open under their recorded owners. Production files, generated docs, package +manifests and lockfiles remain unchanged; no commit or push was made. + +## Chunk 5: facade metadata, active intervals and inline values + +**Law and authority.** ARCHITECTURE's Nested materialization section applies +the Collection metadata boundary to facade rows and preserves authored inline +values. Parents sharing an active bucket share its ready facade. Last-route +departure retires that interval; reactivation gets a new facade. These histories +have no facade-local optimistic mutations, which could legally keep retired +handles nonempty. This is bounded O11 receiving coverage. + +**Model and grammar.** The primary includes owner now recomputes active bucket +intervals from the complete current route groups. Model-only interval tokens +name lifetimes, not production IDs. Complete inputs avoid redundant route-count +state. The metadata receiver crosses 33 projection/collision descriptions, +populated/empty destinations and child success/rejection: 132 histories. +Sharing, one-route departure, last-route departure, held reactivation, +settlement, later source update and resharing compare every child row and all +four metadata fields. Facade output uses its public ID; source-owned rows use +the independently supplied source ID. Inline toArray/materialize comparisons +preserve authored shape. Reads of all four source virtual fields make inline +spread values explicit rather than depending on property enumerability. + +**Results and calibration.** Unchanged production passes 1,632 tests across +both owners (1,599 metadata, 33 includes). Six lifetime controls reject split +sharing, retargeting, stale retired rows/status, missing groups and a cleaned-up +active empty facade; four inline controls reject facade enrichment. The first +checker accepted that active-empty survivor. The review's actual checker probe +now fails at the intended readiness assertion. No production repair GREEN or +production mutant is claimed. Logs: chunk5-ready-final.log, +chunk5-facade-lifecycle-rereview.log and chunk5-active-empty-probe-rejected.log. +DB types and lint:changed pass (chunk5-db-types-final.log and +chunk5-lint-final.log). + +**Reach and review.** Every declared lifecycle cut reaches 32 histories per +ordinary shape and four for direct functional returns. Each facade/inline form +reaches nonempty held/reactivated observations. Active-empty-ready has the same +32/four counts. facade_metadata_review and facade_lifecycle_review used +oracle-review; the latter found the readiness gap, verified its rejection and +independently passed the full suite. No further supported bounded defect found. + +**ORC accounting.** 001–003: authority, limits and literate independent models. +004: shared/sole/absent routes, populated/empty destinations, return intervals +and caller outcomes distinguish each axis; inactive groups have no route. +005: actual public facades and inline values at every named cut. 006: ten +hostile controls plus the actual former checker survivor. 007: exhaustive finite +grammar, so random/replay campaign requirements do not apply. 008–009: active +interval and next token are minimal lifetime facts mapped to public facade +sharing; complete route inputs supply current activity. 010: pending mutation +settles before all-resource failure-preserving cleanup. 011: no new shared-fault +hypothesis requires another formulation. 012: this versioned record at unchanged +858b174a65f160d13d01f0324c7481cc8e4eba3a. 013: last-route and reactivation +neighbors distinguish reuse/retargeting. 014: controlled core receiving scope. +015: per-shape/form cuts above. 016: histories and controls use shared judgments. + +**Limits.** Single-source string child keys only; composite key encoding, +facade-local mutations, construction failures, Effects and provider/framework +scheduling remain separate work. No production or generated docs changed. + +## Chunk 6: generated public metadata types and nullable facade receiving path + +**Law and authority.** VirtualRowProps and ARCHITECTURE's Nested materialization +section supply readonly metadata at Collection roots, source-row metadata on +whole rows and authored shapes for inline values. A nullable selection is either +a complete source row or an empty object. Publishing that object through a +facade supplies required metadata in both cases, while ordinary fields remain +absent in the empty case. The ordinary optional-fields type tests also establish +required selected properties whose value can be undefined. This is bounded O14 +core-source coverage; adapter handler-result generation remains pending. + +**Model and grammar.** Twenty-four finite row descriptions cross three key +domains, two ordinary scalar types, nested optionality and discriminated unions. +Each receives the five projection/eight composition descriptions shared with the +runtime owner. Seven child shapes cross the legal facade/toArray/materialize/findOne forms: +66 query witnesses per description, plus query/index nested refs and +virtual-field removal. Array/Date facade projections are illegal and excluded; +their inline forms remain. Inputs import the public source entry. Every generated body also executes +public query/index construction before compilation, with reverse-order +failure-preserving cleanup. The generated index explicitly chooses BTreeIndex. +Two admission controls preserve the illegal opaque-facade and missing-index-type +fixtures as rejected grammar neighbors. Type erasure prints import-free source +before rebinding; an earlier emit-only removal produced db_1 setup errors and +supplies no product evidence. A recursive type +signature preserves per-member keys, values, optionality and readonly modifiers; +Date built-ins stay opaque while their extra properties are checked. Arrays +retain element shape, mutability and extra fields. Bidirectional signature +comparison permits duplicate equivalent union members, which strict generic +Equal incorrectly rejected in the first draft. Missing whole rows use a +complete-or-absent alternative rather than Partial, which would admit partial +source rows. Expected types come from declarations and authoring shape. + +Concrete helper examples moved from virtual-row-fields-oracle.test-d.ts to the +ordinary virtual-row-fields-reference-types.test-d.ts. They remain supplemental. +The normal package oracle script selects the generated owner. Every compiler +diagnostic, including dependencies, is retained with its file/span/code. + +**Baseline and receiving evidence.** On unchanged production, all 24 generated +descriptions fail only Observation_nullable_whole_facade: inferred ordinary +fields are falsely required because the empty alternative disappears. This is a +new production type defect, deliberately unrepaired. Every one of 69 declared +type-comparison states and construction-admitted reaches in all 24 histories; this counts comparison +reach, not successful histories. The runtime owner adds two legal correlated +histories, initially matched/unmatched, each changing match presence twice. +Complete source rows retain source metadata; empty facade rows receive output +metadata, while inline values stay empty. The runtime owner passes 1,604 tests, including three nullable-boundary checker +controls. +Default empty-row key encoding is only checked against the facade's own public +keys, so no independent encoding claim is made. Generated imports judge public +source types, not published declaration resolution. + +The first generator used invalid root/union API calls. The first runtime draft +omitted required include correlation, and another draft omitted explicit index +type. Those runs failed setup and supply no product RED. After fixture repair, +the source compiler reaches only the intended law mismatch, and the runtime +receiving comparisons pass. Logs: chunk6-independent-types-review-v3.log, +chunk6-independent-runtime-review.log and chunk6-runtime-final.log. Final DB TypeScript and lint results appear with the construction evidence +below. Final-delta runtime review passes 1,604 tests; the second reviewer +independently passed all 24 public construction bodies before the permanent +construction driver was added. + +**Calibration and review.** Seven controls reject enriched inline values, lost +nullability, one mutable metadata union branch, nested any, Date metadata any, +optional nested metadata and nested readonly changes. Each starts with an +accepted generated neighbor. Its hostile compilation must contain only TS2344 +Assert failures at generated Observation/Removal aliases; syntax/setup errors +cannot count. metadata_types_review found the per-union-member readonly, +Date-any and nested-shape survivors; the revised checker rejects each at the +intended type comparison. Its independent complete run has 24 intended RED and +seven calibration passes, with every type state reached 24 times. A second receiver review found the illegal opaque facade and optionless index +inputs. Both are corrected and retained as actual admission calibrations. Its +independent two-owner run has 24 intended RED and 1,611 passes. The permanent construction driver then passed independent re-review: 24 intended +type failures and nine controls pass; all 70 states reach 24 histories, including +construction admission. Log: chunk6-construction-independent-final.log. Main +agrees (chunk6-construction-r3.log). Final package types and lint pass: +chunk6-construction-types-r2.log and chunk6-construction-lint.log. + +**ORC accounting.** 001–003: public/architecture authority and visible textual +models, source grammar, compiler and comparison. 004: key domains, optional +nested values, discriminated unions and every meaningful projection/composition +cell; invalid boolean keys and nullable source Collections are excluded. +005: real compiler public-source imports and actual runtime facade transitions. +006: seven intended diagnostic controls, three nullable-runtime controls, +two construction admission controls and rejected actual checker survivors. +007: exhaustive finite domains, no random campaign obligation. 008: stateless +type judgment, so state minimization does not apply. 009: authoring descriptions +map directly to user syntax; signatures are model-only type observations. +010: compiler source is memory-only, all diagnostics retained; runtime resources +use failure-preserving cleanup. 011: runtime nullable receiving witness supplies +the distinguishing non-type observation for the lost-empty-row hypothesis. +012: this record at unchanged 858b174a65f160d13d01f0324c7481cc8e4eba3a. +013: matched/unmatched, projected/whole, nullable/required and per-member modifier +neighbors reject nearby wrong laws. 014: core-source type and controlled runtime +scope; built/npm/provider receiving claims remain open. 015: 69 type cells plus construction-admitted at +24 histories plus two runtime match states reached by both initial conditions. +016: generated public consumers use one independent law; retained concrete +assertions are ordinary supplemental files. + +**Limits.** Built declarations/npm, adapter handler admission, opaque facade +forms (rejected by admission), nullable QueryRef-facade runtime paths and provider/framework +scheduling remain separate work. Known production type RED prevents a closure +claim. No production, generated documentation, manifest dependency or lockfile +repair was made; the package change only selects the test owner. + +Chunk 6 reviewed test-source SHA-256 fingerprints (before later chunks): + +- `packages/db/tests/query/virtual-row-fields-types-oracle.test.ts`: `65508306911b9c1082181cad2b7467a858a86823bc33228665f2affe5f1edfe4` +- `packages/db/tests/query/virtual-row-fields-grammar-oracle.ts`: `e9587864267256821b898d671c982dc81b3403e8fdb0ef7f20b6d96f0fb1c23b` +- `packages/db/tests/query/virtual-row-fields-oracle.test.ts`: `a2e1e963a9e365cc0ff6cfaa1223c1aacd3e1fc62aa9f5b7aea9c20dc3eaa2c8` + +## Chunk 7 — mutation outcome versus explicit confirmation + +**Law and scope.** Handler Promise outcomes and source confirmation are independent. +Discarded values affect neither. Query handler completion starts no fetch. +Authority: accepted first-RC maintainer decisions and the optimistic-history +acceptance/settlement contract. The shared pure model rebuilds one edited key +and an optional peer from authored inputs. It contains no Query/SDK classifier. +This chunk does not close O17's ordered/later-application paths. + +**Grammar, path and cuts.** 78 Query +42 Electric bounded histories cross CRUD, +absent/present peer, fulfilled void/legacy results, reject-before-write and +reject-after-write. Only fulfilled handlers have a return-value axis; only +written histories request confirmation. Query uses none/refetch/direct write; +Electric uses none/explicit txid. An actual QueryClient and Electric adapter +receive held provider responses. Cuts separately observe handler entry, server +write, requested acknowledgement, accepted acknowledgement, the settlement +waiter, final settlement, and later source confirmation for unconfirmed writes. +Complete ordinary rows include an unchanged scalar and peer when present. +Exact error identity, transaction state, utility outcome and real work counters +are retained. Frozen settlement rows are rechecked after later confirmation. + +**Baseline.** Unchanged production 858b174a65f160d13d01f0324c7481cc8e4eba3a. +Full Query owner 253 passes; full Electric owner105 passes; shared13 controls +pass. Logs chunk7-query-r2.log, chunk7-electric-r2.log, chunk7-model-r2.log. +No new production RED was established by this chunk. Query/Electric package +types pass (chunk7-querytypes-r2.log, chunk7-electrictypes-r2.log); lint exits0 +after import-order correction (two existing warnings in the large Query owner). +No production change, repair GREEN or source-mutant execution is claimed. + +**Reach.** Every declared description cell reaches once. Query cuts: entry78, +server-written72, requested-refetch24, accepted-confirmation48, settlement78, +waiter78 and late-confirmation24. Electric: entry42, server-written36, +requested-txid18, accepted-txid18, settlement42, waiter42 and late-confirmation18. +Each local history asserts its applicable cut reach. + +**Calibration/review.** Thirteen shared controls reject extra Query or SDK work, +omitted peers, partial server replacement, changed success/failure verdicts, +replaced error identity, lost accepted data, leaked optimistic rows, premature +waiter state, duplicated writes and early acknowledgement. Controls first pass +a legal neighbor through the same comparison. Query reviewer caught lost empty +Collection margins and unconditional afterAll reach that broke unrelated replay. +Peer absence restores the margins, and nested suites isolate reach. Electric +reviewer caught a constant work recorder and immediate stream delivery hiding +early acknowledgement. Actual SDK counters and the requested-wait cut fix both. +Independent final review: Query78 +controls13; Electric42 +controls13. +Logs chunk7-query-r3-independent-review.log, chunk7-electric-independent-r2.log, +chunk7-shared-controls-independent-review.log, chunk7-calibration-independent-r2.log. +Unrelated filtered tests pass in both packages. Reviewers named two next witnesses: +post-handler external refetch application (leaked active-handler count) and +rejecting ordered handlers. Those are required next-subchunk work, not waived. + +**ORC accounting.**001–003: accepted contract, independent row recomputation, +visible grammar/driver/check.004: every meaningful finite cell, empty/peer margins, +CRUD and outcome ablations; returned values after throws and prewrite confirmation +are excluded.005: real public Collection mutations, utility calls and waiter cuts; +server facts are supplied before each claimed observation.006:13 intended +wrong-observation controls.007: exhaustive finite grammar, no random obligation. +008: model retains only edited-key membership/version and outcome distinctions +that change these public cuts.009: server-written combines backend write/response; +source acceptance and transaction settlement remain separate.010: all gates +release; verdict/Collection/client cleanup each receive an attempt and primary +errors survive.011: no new shared-fault hypothesis needing a second reference; +independent reviewers reconstructed post-write rejection from the acceptance law. +012:this exact-head record.013:no-confirmation versus explicit confirmation, +rejection before/after write and requested/accepted neighbors.014:controlled +ShapeStream boundary only, no live-provider claim.015:per-description and local +cut reach above.016:shared model/check and generated receivers; calibration-only +owner contains no standalone product examples. + +## Chunk 8 — ordered mutation outcomes and refetch after handler exit + +**Law.** Handler failure and accepted source confirmation remain independent +through the ordered Query receiver. After every handler exits, a later public +refetch again waits for its application obligation. Authority: optimistic +settlement, Query explicit-refetch documentation and the wrapping-sync receipt +contract. A wrapping sync may publish rows before its durable receipt settles; +pending receipt and old public rows are not synonyms. + +**Owners/model.** `optimistic-ordered-refetch-oracle.test.ts` recomputes the +window by filtering/sorting/slicing the independent backend ledger. The existing +Query ownership call-obligation ledger governs post-handler settlement, composed +with `checkPostHandlerRefetch`'s whole-snapshot and waiter checks. Neither model +imports Query's handler count, result classifiers or persistence queue. + +**Grammar/path.** Ordered: CRUD ×fulfilled void/legacy or rejection before a +write/after explicit confirmation ×optional invalidation while held =24 histories. +They observe immediate optimistic windows and callbacks, bounded refill, held +invalidation/report, exact handler failure/state and the final ordered fixed +point. The original two update/fulfilled-void traces retain invalidation head5. +Insert uses head6 to avoid colliding with its local key5. Source/index/live-query +acquisitions register cleanup before subsequent setup can fail. Three controls +reject lost committed data, legacy-value failure and reversed order. + +Post-handler: six CRUD ×fulfilled/rejected histories use a real persisted Query +Collection. After its handler settles, a later refetch's actual durable commit +is held, with no optimistic transaction remaining. A task boundary lets the +utility process completed Query fetch work while the receipt remains held. +The utility must remain pending; either whole old/new snapshot is allowed then. +After release its own waiter must read the complete new rows. Three controls +reject premature receipt fulfillment, partial rows and stale waiter rows. + +**Calibration failures repaired in the oracle.** The first draft required old +rows during the durable hold; source rows already publish there, so those six +failures were a wrong observation/model cut, not a production defect. The next +draft read pending at Query's early flag change. A reviewer removed only the +handler-count decrement with a Vite in-memory transform; production files were +never edited. It ran the faulty wrapper but all9 tests survived, exposing the +timing gap. Keeping the receipt held through a task boundary then rejected all +six histories at the intended pending-versus-resolved comparison; three controls +still pass. Logs chunk8-leaked-count-mutant-independent-review.log, +chunk8-leaked-count-instrumented-independent-review.log and +chunk8-leaked-count-task-turn-independent-review.log. Permanent-source rerun and +final reviewer verdicts follow below. + +**Final chunk8 evidence.** Query full ownership262 +ordered27 =289 passes +(chunk8-runtime-final.log). Package types and lint exit0; lint retains two +existing warnings in the large ownership file. Reviewer mutation_query_review +passes the permanent nine-test post-handler scope and confirms the in-memory +counter mutant fails all six histories at the intended comparison, with all +three controls passing (chunk8-post-handler-r3-independent-review.log, +chunk8-leaked-count-r3-independent-review.log). All12 declared cut/description +states reach once. Reviewed owner fingerprint: +`d9699cf0554bcc58bc9966be51d0b5f8c622b9f6f14e61344fce05420535029b`. +Reviewer mutation_ordered_review passes27 tests and the control-only selector +passes3 with24 skipped. Every ordered description state reaches once; local +cuts:optimistic24, fixed-point24, bounded-refill16, held-report12 and +handler-confirmation18. Logs chunk8-ordered-resumed-final-review.log and +chunk8-ordered-selector-resumed-review.log. Ordered fingerprint: +`462f432f7b20797b8326dc50ae9feb9db177354c802c40d3aad53a46e65a493d`. +No further supported oracle defect remains within either bounded scope. + +**ORC accounting.**001–003: contract authority and independent ledger/window +models beside the driver/comparison.004: CRUD, before/after rejection and +invalidation axes; old update witnesses retained; never returning after a +throw and duplicate insert keys are excluded.005: actual Query/Collection/ +persisted adapter paths, controlled backend/receipt premises supplied before +the cuts.006: six wrong-observation controls plus intended in-memory counter +mutant rejection; the initial survivor and false row-hiding draft are classified +above.007: finite exhaustive descriptions, no random campaign requirement. +008: promise outcome/obligation state retained separately from publication; +ordered expected results are stateless recomputation.009: the call ledger's +application obligation includes the wrapping sync's durable receipt; an +ordered final cut is its eventual fixed point, not the mutation waiter.010: +release gates, attempt each cleanup, preserve original failure.011:no unresolved +shared semantic-fault hypothesis requiring a second reference.012:this record +at unchanged858b174a65f160d13d01f0324c7481cc8e4eba3a.013:prewrite rejection +versus accepted postwrite rejection, and pending receipt versus settled receipt +distinguish neighboring wrong rules.014:controlled provider/storage boundaries; +no live/native provider claim.015:declared reach above.016:all regressions use +the shared models; controls challenge the same checks. + +**Remaining bounds.** Fixed two-row numeric ordered window, one handler at a +time, no ordered persistence wrapper, no entire settlement callback trace, +no late application rejection, and no real SQLite/provider receiving claim. +The Query delay cut and ordered receiver are distinct paths; their combined +arbitrary network scheduling is not established. Types remain next chunk. +Production repairs remain deferred. + +## Chunk 9: generated public mutation-handler types + +**Law and authority.** The approved Query/Electric migration requires +Promise handlers. Fulfilled void/undefined, rejected async work and a +synchronously throwing never-returning handler are admitted. Fulfilled values +and synchronous void are rejected. This law is independent of adapter types. + +**Grammar and receiving paths.** The new DB owner +`mutation-handler-types-oracle.test.ts` generates36 consumers: both adapters, +three key domains, two scalar domains, schema absent/present and, for Query, +rows/envelope select. Each consumer crosses Config annotation, explicit and +inferred factories, CRUD and13 result descriptions:4,212 cells. All702 named +adapter/schema/data/surface/CRUD/result states reach six histories. Positive +neighbors prove each actual public source overload accepts the surrounding +consumer. Invalid imports or dependency diagnostics cannot supply a rejection. +Packed declarations and npm resolution remain O22. + +**Review repairs and calibration.** Two independent oracle reviewers found +missing schema/select receivers and an overly permissive diagnostic classifier. +An in-memory schema-only widening survived the original1,404 cells. The expanded +grammar supplies those paths. Diagnostics now owe a Promise incompatibility +anchored on the CRUD property. Distinct auxiliary overload errors are allowed; +exact duplicates and every diagnostic on an admitted candidate are rejected. +The six permanent controls remove a rejection, duplicate one, substitute a setup +code, append a positive-handler rejection, substitute unrelated same-code text, +or move the actual incompatibility into the callback body. + +The compiler reviewer independently passes42 tests and scans all36 descriptions: +2,592 primary diagnostics, all anchored to the intended CRUD identifiers. Its +actual-compiler substitution probe replaces63 forbidden-result diagnostics per +adapter with body-local Promise assignment errors while preserving the +nine synchronous-void primary diagnostics. Every local error satisfies the old +file/line/code/declaration/cause guards; the new handler-anchor membership check +rejects them. Both the prior same-code substitution and global Promise +widening are rejected. Broad Promise attempts were setup failures and +are not kill evidence. Warm compiler A→B→A equals cold B, including restored +handler rejections. No source production mutation was made; probes use overlays. + +**Results.** Main42 passes in chunk9-handler-types-final.log; compiler reviewer42 +passes in chunk9-handler-compiler-final-baseline.log. Additional logs: +chunk9-handler-anchor-calibration.log, chunk9-all-diagnostic-anchors.log and +chunk9-compiler-probes-substitution-r2.log. DB types and lint pass (r4 logs). +The package oracle selector includes the owner. This chunk reports no new +production defect. Product repairs remain deferred. + +**ORC accounting.**001–003: authority, independent output/transport rule and +literate visible responsibilities.004: schema/select/surface axes reconstruct +public overloads; key/scalar domains keep input shapes legal; synchronous void +and fulfilled value exclusions are explicit.005: actual source entry imports +and retained compiler diagnostics.006: controls and executed mutants above. +007: bounded exhaustive grammar, random/replay campaigns inapplicable.008: +stateless admission law.009: result descriptions are generated input facts. +010: no temporary files or acquired runtime resources; dependency diagnostics +remain visible.011:no unresolved shared semantic-fault hypothesis requiring a +second model.012:this versioned record at unchanged858b174a6.013:fulfilled +undefined versus object, synchronous throw versus synchronous void, and schema +versus absent-schema distinguish adjacent rules.014:actual TypeScript compiler; +no packed-artifact handoff claim.015:702 states each6.016:every consumer and +control uses the same model/check, with no isolated concrete type assertions. + +**Limits.** Finite generated row/result descriptions and the installed compiler +version. Schema transformations, arbitrary provider utility types, packed +ESM/CJS declarations, npm peer resolution and application migration remain +separate receiving evidence. Reviewer final receiver-specific mutant evidence +is appended below when complete. + +**Final receiving review.** handler_types_review independently passes42 tests; +all702 states reach6. A Query schema+select overload-local widening passes the +plain rows, plain envelope and schema rows consumers, then fails the schema +envelope candidate-membership comparison. Electric's overload-local widening +instead causes dependency TS2352: setup failure, no kill evidence. The global +Electric widening and permanent controls retain their narrower evidence. +An add-only diagnostic on an admitted handler fails independently with every +negative diagnostic preserved. Logs:chunk9-handler-types-independent-review-final.log, +handler-types-query-schema-select-local-mutant-r2.log, +handler-types-electric-schema-local-mutant-r2.log and +handler-types-final-positive-auxiliary.log. Final owner fingerprint: +`59afc1991c1ffd2362922ca3e8a1ce7ee307ca3b7bc40cb05e9ffd3bad699ca3`. +Both reviewers find no remaining supported defect in the bounded source scope. + +## Chunk 10: external completion and Offline restoration + +**Law and authority.** Core's transaction admission permits external completion +only while pending. A failed snapshot stays failed even when independently +admitted Offline outbox work later succeeds. The README's per-ID settlement +contract requires that outbox work to keep its own provider outcome and deletion +obligation. Core rollback cancels directly overlapping pending snapshots; it +does not cancel their durable provider work. Completion releases the snapshot's +pending-write contribution before its settlement observer runs. + +**Core grammar and evidence.** The outcomes owner now runs264 finite completion +histories: four admission states, five absent/pending/persisting same/other-key +peer kinds, four source shapes (none, target, unowned key, truncate), plus a +peer-key source for distinct-key peers, each with1–3 completion attempts. The +former12 histories remain prefixes. The primary model compares rows, metadata, +publication cuts, immutable requests, outcomes and states after every action; +tails settle independent peers and apply later source rows. Each history reaches +both comparison-completed cuts once. Full owner287 passes. + +Review found that external completion omitted the primary driver's waiter-time +row comparison. A deferred-touch in-memory mutant survived all263 tests in the +first draft. Adding the same waiter snapshot comparison used for normal +settlement leaves unchanged production287 GREEN and rejects the mutant with39 +intended assertion failures, including six new peer-key witnesses. Removing the +pending-only admission guard fails242 tests. Failures retain the primary +mismatch and distinct cleanup diagnostics through the established helper. +Reviewer handler_compiler_review finds no remaining supported core defect. +Logs:chunk10-core-final-independent-baseline.log, +chunk10-core-final-deferred-touch.log, chunk10-completion-guard-removal.log. +Fingerprints:primary model/driver +`e3df9fb84cbe0ca4212d8f314ddc34f9d7e9d20879db2651c056c56765028892`; +outcomes owner +`ee937b8c3191d3c827be4420e42a157d28a068a9fc2a6a6e82df940f9d33b523`. + +**Real Offline receiver.** The settlement owner expands the old four restores +to60 histories:2/3 transactions, every success/failure mask, shared/disjoint +width1/2 and partial-overlap chains of width2. Alternating authored field edits +preserve the initial/value-edit/flag-edit snapshot law: a later flag-only +snapshot carries the earlier authored value. A stateless prefix fold predicts +provider-applied rows, surviving optimistic snapshots, pending keys, core +restoration states, per-ID outcomes, complete calls and remaining outbox IDs. +The actual executor receives serialized mutations from the real outbox encoder. +Raw restore registration IDs are checked before building a lookup, preserving +duplicate evidence. Every provider tail is held. Actual waiter callbacks eagerly +copy ordinary rows and pending flags; later reads cannot move that cut. + +Intentional provider failures may warn with their exact authored Error. Another +exception cannot substitute for that allowed reason. The first draft captured +console.error only and falsely passed while console.warn reported cleanup +exceptions. The corrected recorder/check catches14 real failures: a successful +replay calls completeWithoutCommit on a snapshot a sibling already rolled back. +The per-ID waiter has resolved, and all preceding ordinary rows, pending flags, +server rows, states, calls and outbox assertions agree; cleanup then throws +TransactionNotPendingCommitError. Shared widths1/2 and chain width2 fail for FT, +FTF,TFT,FTT; shared widths1/2 also fail for FFT. Chain FFT passes, distinguishing +direct overlap cancellation from an unsupported transitive rule. All production +repairs are deferred by the maintainer's instruction. + +**Results and reach.** Final whole settlement owner:14 intended failures, +73 passes (87 total, including seven new checker controls). Scoped receiver: +14 failures,53passes,20 skipped. Of220 declared registration/settlement cuts, +214 reach once; six final cuts for shared/chain FTF/FTT are blocked by earlier +RED comparisons. Those assertions stay present; no later-cut coverage is claimed. +Core and Offline package types pass after correcting one test-only index-signature +mismatch; lint passes with the existing dependency override mismatch warning. +Logs:chunk10-offline-full.log,chunk10-restored-r4.log, +chunk10-db-types-r2.log,chunk10-offline-types-r2.log,chunk10-lint-final.log. + +**Calibration and review.** Seven controls reject extra errors/warnings, leaked +pending flags, stale waiter flags, resurrected failed state, early peer success +and a missing row. Reviewer handler_types_review independently reproduces the +same14 RED failures and53 scoped passes. Removing only restoration touch fails +pending metadata on otherwise unchanged rows, including an all-success shared +history. Deferring that touch lets later reads recover but fails recorded +waiter metadata, including an all-success three-transaction history. Each mutant +campaign gives44failures/23passes; the all-success witnesses distinguish those +mutant failures from the14 baseline product failures. An initial overlay config +attempt failed setup and supplies no kill evidence. Logs: +chunk10-restored-no-touch-mutant-r2.log, +chunk10-restored-deferred-touch-mutant.log. Offline owner fingerprint: +`83eb5551300ebb702e119174a2352c985ad6086cefa18042cc5c947c3c89e91a`. +No further supported oracle defect remains in this bounded receiving scope. + +**ORC accounting.**001–003: transaction/README authority, independent prefix +and primary row models, literate layers.004: old prefixes retained, repeated +admission/peer/source/mask/width/overlap axes distinguish adjacent legal cases; +duplicate outbox IDs, nested rollback cascades and completed source ownership +are excluded by the stated grammar.005: real core calls and real Offline +restore/provider/outbox paths, eager waiter/public metadata observations. +006: controls, intended comparison kills and earlier survivors classified above. +007: finite exhaustive new grammars; existing fixed/random settlement campaigns +remain unchanged.008: failed core snapshot versus successful outbox work and +pending versus persisting peers remain distinct.009: snapshot/provider/outbox +terms stay separate.010: all gates/releases attempted, seed primary preserved, +console spies restored before resource cleanup so secondary failures remain +visible.011: chain histories challenge transitive cancellation; no remaining +shared-fault hypothesis needs another model.012:this record on858b174a6. +013: failed versus pending admission, direct versus chain overlap and waiter +versus later read distinguish wrong rules.014: actual executor receives the +controlled storage/provider premises; native/provider durability remains open. +015:core264×two completed cuts; Offline214/220 as bounded above.016:all new +histories and controls share models/checkers. + +**Limits.** Core completion+touch path, one Collection,2/3 restored transactions, +width1/2, unmarked whole-row insert/update records, controlled in-memory storage +and provider, Node/jsdom observations. Deletes, retries, terminal phases, +multicollection work, real native storage and multi-owner schedules remain +separate owners. The14 reachable product counterexamples keep this class open. + +## Chunk 11: rejected renders and independent descriptor startup + +Source remains `858b174a65f160d13d01f0324c7481cc8e4eba3a`; no production +repair, generated-document edit, commit, dependency install or lockfile change. +This chunk strengthens O19 and O15 at native React/Svelte boundaries. + +**Law and authority.** A rejected preparation or uncommitted render cannot keep +an independently read descriptor Collection from starting and serving its row. +Releasing one descriptor's deferral cannot stop at a sibling's startup failure. +Each source owns its eager/lazy start policy; the preparation rejection remains +the caller's rejection. Healthy returned adapter resources clean up once. +Authority is the existing source-ID/descriptor cleanup contract, the SSR guide's +client-owned descriptor lifetimes, and the glossary's crash/cleanup laws. + +**Grammar and reach.** The React source-ID owner now runs 344 release histories: +applicable causes × derived/explicit-key ordinary/infinite/Suspense paths × +initial mounts/rerenders × hook/preload/start/direct-subscription readers. +Source-ID collision needs a prior claim, and explicit keys intentionally bypass +source-ID/derived-IR rejection. Each history reaches `prepared`, `reader-ready` +and `source-update` (344 each, 1,032 cuts). Preparation reach checks descriptor +materialization, intended rejection class or exact authored error identity, and +execution after the hook for authored throws/suspensions. Four checker controls +reject missing/extra start, wrong value and duplicate rows. + +The shared `descriptor-start-release-oracle.ts` judgment has 85 finite histories: +zero through three acquired sources, every failed-start mask, and independently +every eager-start mask, followed by a throwing factory and an unvisited tail. +React's three hooks receive 255 histories; Svelte's native effect root receives 85. Each reaches rejection, independent demand and cleanup (255/85 of each, +1,020 cuts). Checks compare successful factory counts before any test demand, +exact preparation failure identity, starts, complete ordinary rows/status and +healthy-resource cleanup counts. References are retained across client cleanup; +reacquiring a Collection would exercise restart instead of the cleanup cut. + +Reconstruction includes the old two-throwing-starts-before-healthy witness. +Deleting prefix length loses before/after acquisition cases. Deleting the failure +mask loses the first-exception blocking distinction. Deleting independent policy +masks loses mixed eager/lazy neighbors. Zero acquisition is the boundary control; +a mask naming a source outside its acquired prefix is invalid. The bounded +enumeration is direct replay through its parameterized test name, not a random +campaign. A removed dependency-array SSR cause required `usesDeps=true`; every +current hook supplied false. The removed API therefore contributed no executed +history, and no current public behavior was retired. + +**Review repairs and calibration.** Independent reviewers +`handler_compiler_review` and `handler_types_review` found: + +1. A direct-subscription reader used an empty callback and read Collection + storage. A memory mutant dropping update deliveries reached 122 drops and + passed all 86 subscription histories. Recording a callback-derived replica + makes the same mutant fail all 86 at the updated-row comparison. +2. Boundary text alone accepted unrelated render errors. Cause-specific class or + authored identity checks and after-hook execution markers now validate the + intended path before reach. Replacing the preparation rejection with a later + source error fails 255 React histories at the exact-error comparison; four + controls pass. +3. Uniform source policies could not distinguish applying one source's policy to + all siblings. Independent per-source eager masks expanded 30 to 85 histories. + +A memory overlay that stops resuming after the first startup exception fails +12 React histories and 32 Svelte histories at rejection: later starts stay zero, +status stays idle, and healthy rows are missing. Other cells can legitimately +recover during React retries; those cells are not credited as kills. No mutant +modified production files. Removing only the outer Suspense wrapper's catch +survives and its injected marker is unreached: current failures occur in the +shared ordinary-hook preparation first. This is an explicit limit, and the +coverage map's stale blanket claim that every hook's own boundary was killed is +corrected. + +**Results.** Independent final runs pass 638 React tests (370 complete source-ID +owner, 259 startup owner including four controls, nine ordinary tests) and 85 +Svelte histories. Native partial-construction fault injection in the ordinary +React file throws after acquiring a mount but before returning its handle. Root +inventory cleanup releases its subscriber, a later cleanup still runs, and the +primary/secondary Error identities survive in the AggregateError. +React/Svelte typechecks, changed-file lint and diff whitespace checks pass. +The existing node_modules/lockfile override mismatch is preserved. + +Initial draft failures were setup mistakes, not production RED evidence: +passing a callback instead of an array to cleanup; retaining a constant explicit +key while changing the authored query; re-acquiring Collections after cleanup; +and not catching an intentionally failing lazy start. Two scratch overlays +resolved built output and never applied; they supply no kill evidence. A +review run spanning the policy-field edit had transient calibration errors and +is excluded. Final logs supersede these runs. + +**ORC outcome and limits.** ORC-001–006, 008–010, 013, 015–016 have the law, +independent source-fact judgment, grammar controls, public native paths, +calibrations, reach and failure-preserving cleanup above. ORC-007 is inapplicable +to the finite enumeration. No additional concrete shared-fault hypothesis +requires a second formulation under ORC-011. ORC-014 is bounded to native +React/Svelte with controlled synchronous adapters, not arbitrary provider +behavior. This versioned entry supplies ORC-012. Sources throw Error values; +arbitrary thrown values, provider cleanup before returning a resource handle, +async startup failures, and arbitrary render schedules remain outside scope. +Abandoned-render histories establish eventual release on the encountered +schedules without separately measuring both reader-versus-reclamation orders. +The outer Suspense catch remains unreached. No product repair/closure is claimed. + +Evidence directory: `/private/tmp/rc2171-oracle-challenge/`. +Final baseline: `chunk11-final-react-independent-review.log`, +`chunk11-final-svelte-independent-review.log`, `chunk11-release-r4.log`. +Mutants: `chunk11-drop-subscription-updates-repaired.log`, +`chunk11-stop-first-release-final-mutant.log`, +`chunk11-stop-first-svelte-final-mutant.log`, +`chunk11-replaced-rejection-final-mutant.log`. +Types/lint: `chunk11-react-types-final.log`, `chunk11-svelte-types-final.log`, +`chunk11-lint-final.log`. +Fingerprints (SHA256): source-ID owner +`af236fff80f7d21be7d458b0be70416ec48d08f8fb26c3f762f2f3e91a9d28a0`; +shared startup model +`c6c9bd6ab72ae057062da04f4d81be953b4387cb70950ee8add5b9c37da13add`. + +## Chunk 12: first registration without a transported query result + +Reviewed unchanged production HEAD: `858b174a65f160d13d01f0324c7481cc8e4eba3a`. + +**Law and paths.** The approved local-declaration admission rule also applies +when no query result has been preloaded or transported. First registration must +retain the caller's getKey/schema claim before another same-identity caller can +read or attach. Conflicts depend on supplied declarations, so a held source +cannot defer their rejection until delivery. Compatible callers retain the +original keys and ordinary values. The SSR guide's initial server capture stays +initial until observer attachment and its microtask handoff; it is not a live +server view. The shared reference judgment remains independent of cache/hash +machinery. + +**Grammar and observations.** Core now crosses 144 declaration histories with +three first claimants (explicit preload, streaming observer preload and server +read), five receiving paths and held/settled source delivery: 4,320 histories. +Each of the 15 routes reaches 288 first registrations and 144 held deliveries. +Public dehydration proves no prior query result and positive first registration. +The server-first driver preloads only its source. Two admission attempts, +compatible original reuse, callback-derived rows, exact keys, source updates and +subscriber ownership remain checked. Removing claimant variation loses the +observer registration path; removing held delivery loses conflict rejection +before readiness. The finite domain includes absent/added/removed declarations +and independent references; schema transformations remain excluded. + +The new React `cache-first-registration-oracle.test.tsx` uses actual +`renderToPipeableStream` for both ordinary and Suspense readers. Its 1,152 +histories cross the same declarations with first reader, second preload/server +caller and held/ready delivery. Streaming is enabled without Router transport +or hydration seeds. Every raw render error is validated, including later errors +after an expected conflict. Compatible reuse continues through a core observer +and the next source update. The file supplies no client DOM receiving claim. +Aborted renders retain the exact authored abort diagnostic and all render +completion promises settle during cleanup. + +**Instrument repairs.** Independent oracle-review agents +`handler_compiler_review` and `handler_types_review` challenged the driver: + +- Initial source writes shared the flat expected row objects. A mutant corrupted + initial labels and left later updates distinct; both core and React retained + their baseline outcomes, demonstrating a false green. All five cache drivers + now write copies. The same mutant produces 4,320 core history failures (66 + controls pass) and 1,152 new React failures. Previously passing compatible + cells fail the authored ordinary-row comparison. Existing admission failures + are not credited again as mutant kills. +- A wrongly accepted observer preload could remain pending while delivery was + held, preventing cleanup. The shared checker now records promise outcome at + one host-task cut before delivery. Pending and fulfilled outcomes fail there; + the original rejection judgment still rejects unrelated errors. Four checker + controls pass. An unconditional observer-options omission now finishes with + assertion failures and releases the provider, rather than timing out. +- Focused checker selection previously ran zero histories but asserted all route + reach. Execution is now recorded before each fixture starts; every executed + route retains all declared-state requirements. Four selected controls and one + selected complete route (288 histories) pass separately. The parent inspected + this selector repair independently. + +**Unchanged-production results.** Core: 640 intended admission failures and +3,750 passes (4,390 tests). New React first-registration receiver: 192 intended +failures and 960 passes. Existing core lifecycle: 640 intended failures and +1,526 passes. Existing React cache receiver: 64 intended failures and 224 passes. +Svelte cache receiver: 144 passes. These are product RED evidence, not repaired +GREEN. Baseline controls and neighboring legal histories supply bounded GREEN. +DB/React TypeScript and the changed-file lint implementation pass. The pnpm +wrapper in one review attempted automatic install and stopped before installation; +the exact lint script was then run directly. The existing override mismatch is +preserved; no dependency or lockfile change was made. + +**Calibration.** Omitting observer registration's row options adds 448 core +assertion failures (1,088 fail / 3,302 pass), including 64 held observer-preload +cells now rejected at the pre-delivery outcome cut. No timeout or suite error +remains. React's unconditional omission adds 128 failures (320 fail / 832 pass), +through held Suspense registration. A ready-only omission legitimately survives +React because ready server capture establishes the claim first; it adds 192 +core failures. Source mutation uses memory overlays only. Initial filtered probes +with aggregate reach errors, a pnpm setup abort, unsupported mixed renderer +warnings in an abandoned draft and a pre-repair hanging omission supply no +intended-comparison evidence. Final runs supersede them. + +**Review scope.** ORC-001–006, 009–010 and 013–016 are supported by the authority, +independent declaration judgment, finite grammar, real paths, public cuts, +calibrations, cleanup and per-route reach above. ORC-007 is inapplicable to this +exhaustive finite enumeration. ORC-008 introduces no new stateful reference +transition. No concrete shared semantic fault requires a second formulation +under ORC-011. This append-only head-specific record supplies ORC-012. Both +reviewers found no remaining supported blocker in the stated boundary. Schema +transformations, arbitrary transport/concurrent schedules, Router delivery, +hydrateRoot and first-registration receivers in other frameworks remain open +limits. No production repair or product-class closure is claimed. + +Evidence directory: `/private/tmp/rc2171-oracle-challenge/`. +Baseline: `chunk12-held-admission-route-final-review.log`, +`chunk12-react-first-final-independent-review.log`, `chunk12-svelte-final.log`. +Controls: `chunk12-held-controls-route-final-review.log`, +`chunk12-selected-route-final-review.log`. +Mutants: `chunk12-omit-observer-row-options-final.log`, +`chunk12-core-initial-shared-row-mutant-before.log`, +`chunk12-core-initial-shared-row-mutant-after.log`, with equivalent React logs. +Core test SHA256: +`7ebf875a183111904b46d7905de3572287ccee5f28af9c7267f9969c9b7d60aa`. +Shared checker SHA256: +`05b42da4457279807137f408d9184e8c3bd0e57fbdc636199e465895e512d7e1`. +React first-registration driver SHA256: +`2247d32fc1e47c95136dafcf796f6c6a8dd8b93124d05b7981043156dafdd9e5`. + +## Chunk 13: release histories, actual Changesets commands and finite replay + +Production remains unchanged at `858b174a65f160d13d01f0324c7481cc8e4eba3a`. +This chunk changes tests and their lint/replay instruments only. + +**Authority and independent judgment.** CONTRIBUTING.md's Changesets/Releases +policy defines first `1.0.0-rc.1`, patch suffix increments, minor/major candidate +increments, stable semver/latest and candidate rc tags. The stable Query minor +is an explicit maintainer decision, not a rejected semver policy. The new +`scripts/release-history-oracle.js` uses numeric tuples and an authored pending/ +consumed/token ledger. It imports no Changesets planner or production version +helper. A bounded workspace:* dependency model requires a patch when its exact +published target changes; compatible peers retain their range. Arbitrary semver +range planning is outside this model. + +**Version grammar and cuts.** Seventy-two histories cross first/candidate-nine/ +patch-nine boundaries, direct versus peer stable dependencies, minor/major +precedence, insertion order and three exit forms (no pending work, stable-only, +candidate-plus-stable). Each runs first seeding, standalone stable work, mixed +work, no-op versioning, strongest pending bump, independent candidate work and +exit. Every action compares manifests, version-attributed changelog token bags, +consumed IDs, retained files, dependency protocols and private package state. +No-op actions compare all fixture bytes. The real Changesets CLI supplies pre +entry/exit. Fourteen first-seed failure histories cross seven failure points +with both dependency profiles, then compare exact restoration and successful +retry. The after-apply fault first observes generated changelog bytes, so it +cannot pass by failing before those files exist. + +Each version history enforces its own required cuts. The normal campaign also +enforces aggregate reach: first candidate, standalone stable work, mixed work, +precedence, no-op and post-exit no-op each occur 72 times; each numeric-nine +boundary and exit form occurs 24 times; stable-dependent exit occurs 36 times; +consumed-minor/pending-patch exit occurs 24 times. Each of the 14 failure/profile +states occurs once. Removing standalone stable work loses a plan with no +candidate release. Removing no-candidate exits hides synthetic exit planning. +Unknown packages, reused IDs and unsupported bump values are outside the grammar. + +**Actual command receiver.** `publish-history-oracle.property.test.js` invokes +production `publishDuringRc` with its default command. A strict local pnpm shim +forwards to the installed real Changesets CLI; registry info/publish shims supply +only metadata and retain every raw attempt, selected tag, version, access and +success. They select no tag. Forty histories cross four numeric versions, +stable/only-RC/never/current histories and publish failure in either phase. +Already-current versions cannot have a publish failure; those cells are excluded. +Partial successful writes persist through retry. Each attempt must restore +manifest/pre-state bytes. Every public fixture package must have an info request; +success bags preserve duplicates and omissions while allowing independent order. +All 40 histories reach registry reads/restoration; 24 reach controlled failure +and retry. Twenty-eight pass the final judgment and 12 fail there as described +below. Six permanent wrong-observation controls challenge multiplicity, omission, +tag, version, registry and access. + +**Independent review repairs.** `handler_types_review` found batched stable-only +work and candidate work on every exit. The expanded grammar rejects an omitted +synthetic exit plan that survived the former 36 tests. It also requested a real +late write fault; removing changelog backups now fails after-apply restoration. +`facade_metadata_review` challenged duplicate/foreign argument masking, inherited +Git configuration, unknown-command lock cleanup and an overly strict command +shape that disallowed a legitimate explicit tag. Strict shims now reject malformed +arguments, isolate Git/npm configuration without repurposing HOME, and permit +known explicit rc/latest forwarding. Six ordinary fixture controls cover this +boundary. The receiver supplies command evidence, not npm HTTP or installation. + +**Finite replay.** Registered properties `scripts.release-version` and +`scripts.release-seed-failure` use the standard PROPERTY/SEED/PATH interface. +PATH is one finite case index; SEED is required request identity, not a source +of random cases. Direct replay runs that case and preserves its local cuts. +The shared manifest points at the actual script runner. A completed finite +comparison emits a truthful witness without fabricated fast-check RunDetails. +Setup before comparison emits none. The wrapper preserves primary, witness and +cleanup failures, including thrown undefined. Nineteen ordinary controls pass; +41 existing shared replay checks pass. Independent matching/mismatching-channel +and real EISDIR probes, actual guarded replays and a wrong-family zero-execution +control test the new API. These probes do not close the older standalone writer +policy conflict P03, which remains recorded separately. + +**Lint instrument law.** Canonical replay exposed a false positive: recorded +`numRuns: 1` was treated as a one-run random input budget. Before changing that +test tool, the new `scripts/eslint-plugin-oracle/replay-observation-oracle.test.js` +ran an independent authored-intent source model. It knows the program's role; +it does not parse the generated source or reproduce the rule's AST classifier. +The final grammar contains 3,848 programs and two checker controls: location, +annotation, value flow, binding spelling, type-query and JSON binding axes, +plus five custom-hook initializer forms at otherwise admissible recording states. +All 1,924 declared states reach twice. The repaired allowance recognizes only +the shared writer's typed plain-data record, exclusively serialized by standard +unshadowed one-argument JSON.stringify. Actual budgets, aliases, exports, +mutations, field reads, replacers and custom toJSON/prototype hooks stay reported. +Type-only references and declarations do not invent runtime shadows; a hidden +outer runtime shadow still matters. + +Every newly exposed distinction ran RED before its tool repair: two original +false positives, eight export/type-query mismatches, 32 JSON type/value +mismatches and 200 custom-hook escapes. Final source owner plus ordinary pins +passes 3,984 tests. A broad binding-flow bypass fails 520 comparisons; removing +the plain-initializer guard fails 200. Both fail at the report comparison. +The controlled premise trusts unmodified standard built-ins; this is not whole- +program runtime dataflow analysis. Other serializers/destructuring/intermodule +flows are outside the allowance. A calibration-only selector runs two checks; +any entered source campaign still owes every declared source state. An ordinary +subprocess control retains that selection behavior (bounded 16 MiB output). + +**Results and calibration.** Version/seed: 86 pass. Publish: 34 pass (28 histories +plus six controls), 12 intended tag assertion failures. In those 12 only-RC +histories, real Changesets selects latest for both candidate packages; expected +rc remains unchanged. This is P01, deliberately unrepaired. Fixture controls: +19 pass; shared replay: 41 pass; classifier: 3,984 pass. DB TypeScript and final +changed-file oracle lint pass. Canonical hostile replays reject missing exit +planning at direct-dependent path 12, missing changelog backup after application +and missing local reach. Peer-only path 0 legitimately survives the exit mutant; +it cannot observe a stable direct-dependent bump. Each failed canonical replay +emits an exact failed:true witness. No package install, production repair, +commit, push or generated-reference change occurred. + +Initial attempts using an unavailable vi.getConfig, a nonexistent shared-replay +config, a private selector rejected by lint, old zero-campaign reach behavior, +and an undersized subprocess output buffer are setup/harness failures, not +product RED or mutant kills. Earlier classifier runs with reach marked after +the assertion are superseded. Runtime evidence is reused only where final source +fingerprints did not change during later test-tool repairs. + +**ORC outcome.** 001–006: explicit policy, independent ledger/source-intent model, +literate layers, bounded legal grammar, actual CLI/parser paths, public file/ +request/report observations and hostile calibrations above. 007 is inapplicable +to exhaustive finite enumeration; canonical direct replay is nevertheless tested. +008–009: pending/consumed work and numeric counters retain distinguishable future +actions; source intent is explicitly model-only. 010: exact rollback and +failure-preserving cleanup/witness controls. 011: no unaddressed shared-semantic +hypothesis in the declared boundary. 012: this head-specific versioned entry. +013: standalone stable work, no-pending exit, numeric 9/10 neighbors and +independent registry shapes distinguish the weaker rules. 014: actual Changesets +command/parser reception; npm HTTP, tarballs, installed consumers, credential +services and Actions remain separate. 015: local and aggregate reach above. +016: histories and calibrations share their judgment; ordinary harness examples +remain in ordinary test files. Independent reviewers found no remaining supported +blocker in this bounded chunk. No product-class closure is claimed. + +Evidence directory: `/private/tmp/rc2171-oracle-challenge/`. +Runtime: `chunk13-release-version-final-r7.log`, +`chunk13-publish-final-r7.log`, `chunk13-fixture-final-r7.log`, +`chunk13-shared-replay-r7.log`. +Classifier: `chunk13-lint-observation-independent-final-r9.log`. +Mutants: `chunk13-exit-plan-mutant-r9.log`, +`chunk13-changelog-mutant-r8.log`, `chunk13-local-reach-mutant-r8.log`; +source-classifier RED/GREEN/mutant logs have the chunk13-lint-observation prefix. +Final lint: `chunk13-lint-final-r12.log`; types: `chunk13-db-types-final.log`. +SHA256 model: `ad378df8f61783fbdba2d53609de5a62b1e5b0f8e0f15643753c55744c4baf8d`; +version runner: `79567b1d43b48d7c9d1d0125057b1056f7eb024bfe8a4456646d16f5d99b0eb3`; +publish runner: `512218cbed6cfdd007620a3fed1ae1ce34a303a72fa0cf5dc30ad66b960856b7`; +fixture: `a4c3a87dd719160866d6fd2749d55996df2f4e1e40f8fadced4025c4391fcc29`; +classifier owner: `f4e51294ebcfefc2bfdb9127dae91a5f557a75b7e1e2763635038e60fd308f4d`; +classifier rule: `3a8139f2c2fa1fd55291ce68ab3593dad754b14ad35e3bf9cbe367e676a9fa28`. + +### Work remaining after chunk 13 + +Production repair stays deferred. O13 now has version and actual Changesets +command evidence; chunk 14 is the real loopback HTTP/npm receiving witness. +Packed consumers and clean workspace/build reception remain O22. Test:release +and CI wiring for the new script owners remains part of final O26 execution; +the PR's existing test:lint-rules already discovers their property files. + +The following boundaries still need implementation/review before the campaign +can finish: + +- Transforming schema retention through actual client mutations in the shared + bound-options owner and React/Preact receivers (O05). Schemas do not transform + synced query rows. Runtime absent/trim/uppercase probes establish the correct + mutation observation. Typed live-query schema support remains an existing + separately recorded limitation. +- Effect source metadata copied to ordinary selected fields, with exact batch + and named delta value/previousValue ledgers (O06–O11 receiving limit). Effects + have no output Collection ID. Implicit reserved Effect-root attribution is + not authorized by the Collection decision. +- O12 subquery reserved-field sanitation still awaits the already-asked + maintainer decision. Do not silently select a normative answer. +- O20 proxy diagnostics and actual ListsScreen/ListCard via controlled React DOM + host primitives; native emulator/renderer tools are unavailable. The actual + component scratch probe shows Saving absent/held/settled transitions. It + supplies no native-host scheduling claim. +- O21 actual fenced mutation guidance through the shared confirmation judgment, + active API consistency and diagnostic-specific snippet baselines. The actual + error-handling POST retains its current intended stale-Collection RED. P02 + identifies another stale current instruction. +- O22 clean frozen install/build graph, public package packing and supported/ + rejected installed consumers. Inventory25 is correct. Offline lacks a local + DB project edge in pnpm's closure; the lock still contains its DB link, so + installation failure itself is unproven. The exact SQLite core source + typecheck already fails on the Promise<{}> handler (G08). +- O23 retired exports/modes/wrappers; O24 compile-invariant work placement; + O25 built diagnostics and ESM/CommonJS/public export receiving checks. Read- + only inventory finds the originally cited code already removed/hoisted. + Calibrated checks must preserve that evidence without inventing an all-adapter + coded-message promise. Preact intentionally lacks CJS; Svelte has a distinct + build layout. O26 must tie actual selected/build inputs and all remaining + affected validation to exact fingerprints, and retain P03's older writer + policy conflict rather than declaring the whole instrumentation class closed. + +## Chunk 14: actual npm/pnpm HTTP publication + +Library production remains unchanged at +`858b174a65f160d13d01f0324c7481cc8e4eba3a`. The release publisher is deliberately +unrepaired. This chunk adds a receiving driver and fixture controls. + +**Law and receiving boundary.** The accepted release policy and chunk 13's +independent `expectedPublishes` ledger still require candidates on `rc`, stable +packages on `latest`, and each unpublished version once. The new +`scripts/publish-http-oracle.property.test.js` invokes the default production +publisher through actual Changesets 2.29.8, npm 11.17.0 and pnpm 11.1.0. A child +registry binds a loopback ephemeral port because the production command blocks +its parent event loop. It supplies authored package metadata and controlled +failure responses; it never chooses the client's tag. Raw GET/PUT records retain +origin, attempted identity, access, tag, failure and duplicate multiplicity. +The server reads each actual uploaded tarball's manifest without extracting +paths and checks its name, version, byte length and digest before recording it. +Requests are persisted before the HTTP response, so response receipt establishes +that the recorder has the request. + +**Grammar and observations.** Fourteen histories select candidate +`1.0.0-rc.1.10` from the shared grammar. Stable, prior-RC, never-published and +already-current histories cross every possible failure phase. Prior-RC and +current metadata separately use an `rc` tag alone or an additional `latest` +alias. This distinction matters to actual npm's default metadata lookup. +Each failure history receives one controlled HTTP 400 and retries over retained +successful publications. Already-current versions have no injected publication +failure. Both attempts compare exact manifest/pre-state bytes before judging +raw failed attempts, fulfillment, packed identities, final publication bags +and registry state. Independent package order is permitted; duplicate or omitted +successes are not. A failed retry is recorded before comparison, so it cannot +escape as an unexamined command error. + +Final measured reach: `registry-read` and `restored` each occur in all 14 +histories; `controlled-failure`, `partial-publication` and `retry` each occur in +eight. Later `packed-upload` occurs ten times, `already-current` once and +`compared` eight times. The six intended RED histories stop at earlier violated +comparisons; they do not receive credit for unreached later states. Each passing +history enforces its own declared reach. Optional statistics report zeros on +RED histories too, while preserving the primary error if reporting fails. + +**RED and neighboring results.** The focused HTTP command is: + +```sh +TANSTACK_DB_ORACLE_STATISTICS=1 node node_modules/vitest/vitest.mjs run \ + --config scripts/oracle-scripts.vitest.config.mjs \ + scripts/publish-http-oracle.property.test.js +``` + +It has 11 passes (eight histories and three origin controls) and six assertion +failures. Three prior-RC histories with a `latest` alias fail the shared tag +comparison: candidates use `latest` (P01). Three RC-only/no-latest histories +fail the no-republication comparison: an already-current release, plus retries +after candidate failure from prior-RC and never-published starting states, +attempt to upload an existing RC and receive HTTP 409 (P05). Exact local byte +restoration is checked before those failures. No library repair GREEN is claimed. +The real receiving path expands the earlier shim's evidence; it does not make +the shim's RC-only metadata forms equivalent to actual npm lookup. + +**Fixture repairs and calibration.** The receiver discovers installed tools, +isolates npm/Git configuration, uses fixture credentials and preserves HOME. +Its disposable workspace disables pnpm's automatic dependency installation and +supplies local links only for packing. An early pnpm probe attempted automatic +installation against the fixture registry and failed before Changesets; it is +a setup failure, not product evidence or a successful install. Initial loopback +and `ps` EPERM results are also setup failures; final local-only runs use the +necessary socket/owned-child access. + +Owned process groups bound the command and include its descendants. Eight +ordinary controls check successful/timeout descendant termination, executable +startup failure, bounded output, primary and secondary termination errors, +callback Error/undefined preservation and unsupported routes. The output control +first failed because an exact 16 MiB boundary discarded later chunks and +reported timeout. An independent trace then exposed repeated group signals after +the first successful kill. Counting incoming bytes before truncation and making +termination one-shot repair these test-tool defects. Final eight controls pass; +three repeated formerly failing overflow probes also pass. The secondary-error +control first ran GREEN after a concurrent helper change; no original RED is +claimed for that subcase. The earlier misleading scratch filename +`chunk14-secondary-control-red.log` contains that GREEN result. + +The shared publish checker adds only an optional expected registry argument; +its original fixed-origin default remains. All six existing checker controls +and three new default/variable/foreign-origin controls pass. The version model +is unchanged, so the 86 version/seed histories are not rerun for this parameter. +Raw observed origins are never normalized to the expected origin. + +**Independent review and ORC outcome.** `facade_metadata_review` independently +executed the HTTP histories and fixture controls, challenged tool portability, +process ownership, response/record ordering, failure fidelity and raw duplicate +observation, then accepted the bounded instrument. ORC-001–006 have the policy, +shared independent ledger, finite grammar, actual client/server path, full raw +attempts and wrong-tag/duplicate/origin witnesses above. ORC-007 is inapplicable +to this finite enumeration. ORC-008–009 retain registry versions, tags and +partial success because retry distinguishes them. ORC-010 has the cleanup and +diagnostic controls. No further concrete shared-model fault invokes ORC-011. +This head-specific entry supplies ORC-012. ORC-013 has the RC-only/latest-alias +neighbor; ORC-014 receives metadata through actual clients; ORC-015 has local +measured reach. Under ORC-016, ordinary fixture controls remain separate. + +Limits: four fixture packages, one candidate version, the installed tool +versions, POSIX process groups and controlled HTTP responses. Tarball dependency +closure, exports, installed consumer APIs, clean workspace installation, +production authentication and GitHub Actions remain separate O22/O25/O26 work. +No commit, push, installation workaround or generated-reference edit occurred. + +Evidence directory: `/private/tmp/rc2171-oracle-challenge/`. +Final baseline/reach: `chunk14-http-baseline-final-r3.log`. +Independent HTTP: `chunk14-http-independent-final.log`. +Controls: `chunk14-http-fixture-controls-final-r2.log`, +`chunk14-http-fixture-independent-r3.log`, +`chunk14-output-bound-independent-r3.log`, +`chunk14-checker-calibrations-r1.log`. +Instrument REDs: `chunk14-fixture-process-controls-r1.log`, +`chunk14-output-bound-kill-trace.log`. +Final lint: `chunk14-lint-final-r4.log`. +SHA256 model: +`973e20a17d2faaafb2b459eab904008b85304a7722bdf92f1fb4dd86f8292759`; +HTTP owner: +`bc919160a72f3a6ce87c24f4a929ea113febec42fac78ae349df46e6e39cf10e`; +fixture: +`2390c87bacc10b4f948d58ec3724d01297bfb3f7d2587173f09eaa7f8c0d0dc7`; +registry: +`5847b68adc499871b4c6958fa204fd8dc124f6d554bc02ae388d2f739f010d95`; +runner: +`e804c8674a25f8ef18a937d79208e6e592ea0f3d78dc1f4fc908b7146c969618`; +ordinary controls: +`ac3062fa2b910c769bc3553bf7e9c72b779355c9f6f582f4a6b01c2fedd8cafe`. + +### Work remaining after chunk 14 + +The HTTP release receiver above is now reviewed. Chunk 15 begins the transforming +schema/client-mutation receiving extension described after chunk 13. Effect +metadata, the pending O12 authority decision, actual UI/doc guidance, clean +workspace and packed consumers, calibrated source/artifact checks and final +exact-input validation/CI wiring remain. All library repairs stay deferred. + +## Chunk 15: bound schemas govern client mutations (reviewed) + +Frozen production head: `858b174a65f160d13d01f0324c7481cc8e4eba3a`. +Independent reviewer: `handler_types_review`; author: `facade_lifecycle_review`. +No library production file changed. The schema guide's client-input contract +and the maintainer's identity-retention decision authorize this receiving law: +sync rows stay raw; same-identity renders retain schema/key behavior; query or +queryKey replacement applies the proposed behavior to the next mutation. + +The shared `live-query-bound-options-oracle.ts` now models all nine ordered +absent/trim/uppercase schema pairs, two identity boundaries, independently +same/different key proposals and two padded lowercase inputs. Its word/ASCII +computation is independent of the fixture's String transformations. React live, +ready-source React Suspense and Preact live each execute 72 native histories. +Held updates compare immediate Collection reads, committed hook data/state and +handler original/modified/changes, followed by raw synced updates, identity +replacement and a further mutation. Ordinary own enumerable fields remain +observable; only four virtual metadata fields are removed from this judgment. + +Independent GREEN: 216 native histories and 72 shared calibration tests pass. +The 72 calibrations reject 3,816 wrong observations and accept 648 permitted +unordered comparisons; those are checker evidence, not native histories. +All 2,592 path × history × cut reach cells execute exactly once. The twelve +cuts distinguish raw initial/source/followup input, held mutations, settlement +and replacement. DB, React and Preact typechecks and changed-file lint pass. + +Review exposed two instrument defects. Loose equality admitted extra own +undefined fields; five independent rejection probes reproduced this before +strict equality repaired it. Omitting the final next-use mutation originally +left the whole run green because repeated reaches collapsed into earlier cuts. +Distinct followup-held/settled declarations now reject that omission, after +individual assertions pass. Read-only hostile overlays that preserve configured +schema references but bypass validation fail 192 immediate native value +comparisons, while 24 absent-schema controls pass. Corrupting initial or later +production sync inputs fails every native history at the raw-row comparison. +These are intended assertion/reach failures, not setup errors or timeouts. + +ORC-001–005: authority, finite bounds, literate independent model, all native +receiving paths and value checkpoints are explicit. ORC-006: checker controls, +validator bypass, input corruption and omission attacks reject as above. +ORC-007 does not apply to this exhaustive finite grammar. ORC-008–009: binding +phase is model-only and identity replacement distinguishes retained options; +twelve observation cuts do not introduce a second production state machine. +ORC-010: cleanup preserves failures while releasing handler gates, transactions, +hosts and Collections; hostile runs exit without timeout. ORC-011: review found +no remaining concrete shared-semantic-fault hypothesis in this bounded model. +ORC-012: this frozen record supplies review evidence. ORC-013: all ordered +schema pairs, both identity boundaries and independent key proposals distinguish +wrong retention policies. ORC-014: the three real hook paths receive the same +model, without broader provider claims. ORC-015: all 2,592 cells have min=max=1; +live-only, Suspense-only and calibration-only selections each pass 72 tests, +while a partial entered-host selection fails mandatory lane reach. +ORC-016: native histories and generated calibration observations share the +model and refinement checks; no fixed expectations were added to oracle files. + +Limits: valid synchronous schemas, flat scalar ordinary rows, explicit string +keys and mutations settled before identity replacement. Ready-source Suspense +does not establish initial suspended retry. There is no SSR, concurrent-render, +provider or typed-schema claim: the public schema option remains typed never. +Default-getKey absence, invalid/async schemas and mutations spanning replacement +remain outside this receiver. Passing bounded histories do not prove universal +production correctness or finish the campaign. + +Evidence directory: `/private/tmp/rc2171-oracle-challenge/`. +Independent GREEN: `chunk15-react-independent-final-r1.log`, +`chunk15-preact-independent-final-r1.log`. +Author GREEN: `chunk15-react-final-r1.log`, `chunk15-preact-final-r1.log`. +Controls: `chunk15-react-schema-bypass-mutant-r1.log`, +`chunk15-preact-schema-bypass-mutant-r1.log`, +`chunk15-{react,preact}-{initial,later}-input-mutant-r1.log`, +`chunk15-omit-followup-mutant-before-r1.log`, +`chunk15-omit-followup-mutant-final-r2.log`. +Types: `chunk15-{db,react-db,preact-db}-types-final-r1.log`. +Lint: `chunk15-lint-changed-final-r1.log`. +Strict-undefined instrument RED/GREEN: +`chunk15-extra-undefined-calibration-r1.log` (five failed rejection checks), +`chunk15-extra-undefined-calibration-r2.log` (five pass). +Selectors: `chunk15-live-only-selection-final-r2.log`, +`chunk15-suspense-only-selection-final-r2.log`, +`chunk15-controls-only-selection-final-r2.log`, +`chunk15-partial-live-selection-final-r2.log`. +SHA256 shared model: +`c6b6da5fe6bbeb0d3d6dd8e389d2825468acc7aa94ecb1ece11fb741bf85952a`; +support: +`ed4bd6bb340d8d16d64914d500e14c597f28ef124ce0439cae6d606770b81e75`; +React: +`8d4707534b5962b8f249885b10104045869a86d9e86faf8c8e0ac289866b41c6`; +Preact: +`97c32b831caf5e7d4c6349197e356594fffee0c0b4cfc113a479717fe3a76f0c`. + +### Work remaining after chunk 15 + +Effect metadata is the next edit chunk. Pending O12 authority, actual UI/doc +guidance, clean workspace and packed consumers, calibrated source/artifact +checks and exact-input validation/CI wiring remain. Library repairs stay +deferred until all oracle chunks are through independent review. + +### Subsequent authority for O12 + +The maintainer answered the pending callback-input question on 2026-10-28: +strip reserved metadata fields from new projected subquery objects before +downstream `.fn.select()`; direct source rows keep metadata and inline include +values keep authored shape. The earlier pending references describe the state +at their recorded review. O12 can now proceed as the next chunk after Effect +metadata review. This decision changes no production file. + +Pre-edit O12 inspection identified one narrower authority edge: structured +whole-row spreads have an existing source-owned attribution law, although they +construct an object. The maintainer has been asked whether that rule also +preserves metadata at callback entry. New-shape projections, direct source +returns and inline values have the decision above; spread-dependent assertions +remain pending that answer. + +The maintainer subsequently chose preservation for structured whole-row source +spreads at callback entry. O12's authority is now complete: direct source rows +and structured whole-row spreads retain genuine source metadata; own-shape +projections lose reserved fields; inline values retain authored shape. + +### Remaining receiving plans (read-only preparation) + +These plans do not claim implemented coverage. Chunk 16 Effect metadata is +currently under construction and independent review. Its first LEFT-join run +exposed P09's phantom initial update; raw startup observations remain retained. +The revised driver will collect cut-labelled mismatches and continue through +the authored history, distinguishing exercised cuts from passing comparisons. +O12 callback-input work starts only after this chunk's review finishes. + +During chunk 16 review, omitting the right-source remote-followup action from +both authored model and driver preserved every aggregate reach and the same +64 startup REDs. The independent read-only attack demonstrates a reach gap, +not another production bug. Side-specific followup/pending/acceptance/settlement +declarations and actual held-handler premises are being added before final +review. Evidence: `chunk16-before-side-reach-{baseline,omission}.log` under the +scratch directory. This is an in-progress note, not a reviewed-chunk verdict. + +O21 is divided into actual mutation-fence receiving, diagnostic-specific snippet +baseline checks, and active/historical guidance checks. Its read-only baseline +is 802 snippets in 300 files, 252 known failures including 59 parse failures, +zero new failures. The successful unconfirmed POST reproduction still lives +only in scratch; the durable mutation owner currently supplies checker controls, +so a real fenced receiving driver remains necessary. Shared mutation judgment +must accept complete authored row shapes rather than force todo snippets into +the existing numeric id/name/stable fixture. Required confirmation derives from +the guidance obligation, never from whether a snippet calls refetch. P02/P04/ +P06/P07 and candidate P08 remain under these checks. Authored guidance is not +repaired yet. + +O20 mounts actual ListsScreen and its private ListCard through a controlled +ReactDOM/RN-primitive host. No native tooling is available. The old unsaved +screen probe establishes feasibility only and needs retained rerun evidence. +Reuse the source metadata and mutation judgment, deduplicate React runtimes, +and judge Saving after delivered delay callbacks without inventing an exact +200 ms product threshold. Include fulfilled/rejected, confirmed/unconfirmed, +early/delayed settlement, independent peers and next-use histories. + +O22 stages a clean snapshot without node_modules/dist, attempts frozen offline +installation, then actual build graph and source typechecks. Missing cache is +a setup failure. Preserve the confirmed persisted-handler TS2769 and missing +Offline build-graph edge separately from unproven frozen-install failure; the +committed lock already links DB. Run the actual version CLI in staging only. +The proposed four release cuts (RC1, RC1.1, RC2, stable) receive all 25 public +tarball manifests/exports, 32 minimal installed Query/Offline runtime histories, +eight declaration consumers and strict positive/negative peer installations. +Installed consumers must resolve only disposable packed entries, with no source +alias or DB/adapter override standing in for peer admission. Existing workspace +installation and user lock/override state remain untouched. + +O23–25 use calibrated, narrow source-maintenance/work checks and actual supported +artifact formats. Core's coded-error law does not imply zero plain errors in +all adapters. The census omits errors.ts, TSX and Svelte files, so an adapter +inventory needs explicit public classes and package-aware source enumeration. +Preact and Solid are ESM-only; Svelte has its distinct svelte-package output. +The current minified tool's db-ivm source alias cannot prove packed-artifact +reception. Fresh builds, fingerprints and bundle-input checks are required. + +O26 joins all affected selections, per-path reach, exact source/build inputs and +CI entry points without duplicate script-owner execution. P03's standalone +recorder currently throws EISDIR before owner analysis in three of twelve real +RunDetails boundary cases. Planned test-infrastructure repair separates a +nonthrowing recording outcome from a caller wrapper that preserves owner +failure as primary and writer failure as secondary, failing on writer-only +failure. This is authorized routine harness design under ORC-010. An artificial +two-body it.fails experiment does not establish a legal-owner counterexample +and is not another confirmed defect. Guarded child-process witnesses remain +necessary. No library repair, commit, push or dependency workaround is part of +these read-only preparations. + +Additional O26 instrumentation audit: chunk 16's finite owner initially +inherited createOracleReach's seed/path exemption despite having no replay +mode. With an unrelated seed, a partial single-source selection passed while +omitting required join/union lanes. Effect now declares its finite replay +policy explicitly; independent final verification is pending. The remaining +campaign finite owners are being inventoried read-only for the same class, +distinguishing actual direct replay from irrelevant seed inputs and wholly +unentered campaigns. Evidence before repair: +`chunk16-independent-replay-env-selector-before.log`. No library defect or +repair is inferred from this test-infrastructure failure. + +## Chunk 16: explicit source metadata reaches Effect deltas (reviewed) + +Production head remains `858b174a65f160d13d01f0324c7481cc8e4eba3a`. +Author: `metadata_timeline_review`; independent reviewer: +`effect_metadata_review`. No library implementation changed. + +VirtualRowProps/VirtualOrigin, public DeltaEvent/EffectConfig and the Effect +guide authorize the law: source metadata explicitly selected into ordinary +fields reaches both actual onBatch and named callbacks, with exact values, +previous values and multiplicity. No output Collection exists for an Effect; +this owner assigns no implicit root metadata or Collection ID. Event keys are +opaque: distinct active occurrences have distinct keys, updates retain the +active key, exit retires it, and reentry may choose a fresh key. Typed source +keys are independently compared through the ordinary observedKey field. + +The independent model authors base rows, overlays, queued confirmations and +applied origins, recomputes projected rows and differences before/after arrays. +The small source judgment is shared with the existing root metadata owner; +output attribution remains separate. The finite grammar executes 200 native +histories: structured/functional selection, single source, LEFT join with both +matched and unmatched rows, and populated unions in both branch orders. +Confirmation masks, outcomes and two-source settlement orders vary independently. +Held writes, accepted-but-held confirmation, each settlement, remote followups, +peer exit/reentry, survivor next-use and final drain are compared. Both ledgers +preserve every event and copy scalar fields before later source changes. + +Independent baseline at the final freeze: 64 initial LEFT-join assertion REDs +and 152 passes (136 native histories plus 16 controls). All 5,512 per-history × +channel × applicable-cut reach cells execute exactly once. Of 2,756 combined +checkpoint comparisons, 2,692 pass; every mismatch is at initial delivery. +Each history collects cut-labelled errors and continues authored actions, so +the initial defect neither disappears nor prevents later evidence. P09 is the +actual startup update for a never-entered unmatched occurrence, after its load +settled. No class closure or product GREEN is claimed. + +Review repaired four instrument gaps with prior executed evidence. Omitting a +right-source followup from both model and driver escaped aggregate reach; +per-lane side cuts now fail exactly the two missing channel cells. Checker-only +selection previously failed all native reach; wholly unentered native campaigns +now stand aside while partial entered selections still owe every lane. A new +nonempty-batch assertion rejected lawful empty partitions; it had no public +authority in this owner's unconstrained partition judgment, so it was removed +and replaced with permitted empty-partition controls. Finally, an unrelated +replay seed disabled finite reach: eight selected histories passed before the +explicit finite policy. They now fail for 5,376 missing cells, while seeded +checker-only selection passes all 16 controls. Invalid seed/path invocations +without required property selection were setup failures, not rejection evidence. + +Five deliberately wrong production overlays change named dispatch, substitute +current for previous value, duplicate delayed events, collide active keys, or +add an own undefined field. Each adds intended comparison failures in all 200 +native histories; all 16 controls pass. The joined baseline RED is distinguished +from the added mutant violations. Lawful opaque-key renaming, fresh reentry keys +and empty partitions preserve exactly the baseline result. No production file +was changed for these overlays. Existing root metadata coverage remains +1,604/1,604 GREEN after extraction; six ordinary regression/cleanup controls pass. +DB TypeScript and scoped ESLint exit zero; ESLint retains two existing +require-await warnings in the root owner. + +ORC-001–006: sourced bounded law, independent recomputation, literate layers, +exhaustive legal grammar, actual callback receiving paths and calibrated wrong +observations/overlays. ORC-007 is inapplicable to finite enumeration. ORC-008: +settlement distinguishes base/overlay/confirmation/origin; no production queue +is reconstructed. ORC-009: source/public delta vocabulary and opaque active +intervals are explicit. ORC-010: cut and later driver errors remain aggregated; +copied ledgers and shared failure-preserving cleanup retain the first mismatch. +ORC-011: no further concrete shared-semantic-fault hypothesis remained in scope. +ORC-012: this frozen independent review record. ORC-013: confirmation/outcome +neighbors, typed 1/"1", both settlement orders and exit/reentry distinguish the +law. ORC-014: controlled eager core sources only, no real-provider claim. +ORC-015: all 5,512 cells reach once, source/partial/seeded selector attacks and +unentered controls above. ORC-016: generated histories and calibration observations +use the same independent judgment, without fixed product expected answers. + +Limits: flat scalar projections, eager controlled sources, one persisting +mutation per source, and the named single/LEFT-join/populated-union paths. +No arbitrary provider scheduling, frameworks, ordered repair, same-source +overlap, callback-input sanitization or exact internal graph-run callback-count +claim follows. P09 stays open for the later production-repair phase. + +Final evidence under `/private/tmp/rc2171-oracle-challenge/`: +`chunk16-independent-baseline-final-r3.log`, +`chunk16-independent-reach-final-r3.json`, +`chunk16-independent-replay-env-selector-before.log`, +`chunk16-independent-replay-env-selector-after-r3.log`, +`chunk16-independent-replay-env-checker-r3.log`. +The `chunk16-independent-*-final-r2.log` overlays remain applicable: the only +later source difference is the explicit finite reach option/comment; model, +driver, recorder and comparisons are unchanged. Earlier gap evidence: +`chunk16-before-side-reach-{baseline,omission}.log`, +`chunk16-independent-checker-selector-before.log`, +`chunk16-independent-empty-partition-before.log`, +`chunk16-one-lane-omission-after.log`. +Author checks: `chunk16-{effect,eslint,typecheck}-final.log`, +`chunk16-root-extraction-r2.log`, `chunk16-ordinary-controls.log`. +SHA256 Effect owner: +`4b738962e30f2b0f55b1894bab1f76334da8d66e6001773bbe24599408bb6130`; +source judgment: +`d60c483456f6d222060976ebd10dd68ddbb9481c685f785f7a36e0c82ec1f80f`; +root owner: +`c925791e375f2f2a0941e0ab35b965cabe89570fb13a981b14826b874eaca98f`. + +### Work remaining after chunk 16 + +O12's callback-input metadata law now has full maintainer authority and is the +next edit chunk. Actual guidance and example UI, clean installed/packed +consumers, source/work/artifact checks, final exact-input validation/CI wiring +and the broader finite-reach/P03 instrumentation work remain. The read-only +finite audit has already reconstructed the seed bypass in Preact; its wider +inventory belongs to O26, without erasing prior normal-run evidence. All +library production repairs remain deferred until every oracle chunk is through. + +The read-only finite inventory is preserved in +`2026-10-28-pr-2171-finite-reach-inventory.md` with 19 source fingerprints: +28 default-policy sites need examination/repair; five other files have explicit +policies, including genuine release replay, and must not be sweep-changed. +Only Preact's irrelevant-seed bypass has an executed receiving witness so far; +the other rows are source inspection, not independently executed failures. +Exact-final-hash chunk 16 type/lint logs are +`chunk16-typecheck-final-r2.log` and `chunk16-eslint-final-r2.log`. + +Read-only O21a refinement: parameterize the existing mutation judgment with +complete before/optimistic/confirmed/peer rows, retaining numeric defaults and +existing callers. Actual handlers use string todo IDs and text/completed or +product fields; no field-dropping projection into name/stable is acceptable. +The proposed receiving inventory is 18 fences, 30 current handler ASTs (21 Query, +nine Electric), plus three explicitly negative Before/WRONG examples. Three +collaborator outcomes and peer absence/presence give 186 histories (130 Query, +56 Electric); negative examples use only successful collaborators. This is +exact-handler reception through real adapters, not full schema/query/component +execution. It must retain the successful unconfirmed POST RED and rerun all +unchanged default adapter/ordered/post-handler evidence after generalization. + +Independent pre-edit O21a review identified a necessary distinction: the old +reject-after-write histories reject after confirmation acceptance, while an API +collaborator can commit and reject before returning to its handler. The latter +never reaches the handler's later refetch/acknowledgement, so its settled +Collection may correctly retain the old base even though server data changed. +Confirmation intent and independently authored confirmation acceptance must be +separate. Preserve old default histories and the unconfirmed-success RED; +do not infer either promise from observed utility calls. + +## Chunk 17: metadata is correct before functional callbacks (reviewed) + +Production executable baseline: `858b174a65f160d13d01f0324c7481cc8e4eba3a`. +Author: `metadata_types_review`; independent reviewer: `handler_types_review`. +Only narrow architecture contract prose changed under src; no executable +library repair, generated documentation, installation, commit or push occurred. + +The two maintainer decisions authorize this law: direct source rows and +structured whole-row source spreads expose genuine metadata before downstream +fn.select, including when a source spread selects conflicting reserved names. +Own-shape projections, including functional new objects/spreads, expose none +of the four reserved fields. Nested inline values keep authored selected shape; +compiled Collection-valued inputs reject before callback invocation. The +architecture now states this boundary explicitly. Metadata's readable presence +and value matter; genuine source metadata's own-property layout is not promised. +Ordinary selected fields, including nested authored reserved names, retain +strict own shape and own undefined. + +The small input model uses authored source facts and the shared source metadata +judgment, not compiler provenance. Thirty-three legal shape/collision +descriptions cross seven consumers and two actual compiler receivers: root and +correlated inline child, 462 histories. Two direct-source controls, 66 nested +array/materialized preservation histories and 33 Collection-input rejections +complete 563 native histories. Seventeen controls calibrate the same judgments. +Typed numeric/string peers, each-name/all/no collisions and both authored +pending booleans distinguish sanitizer shortcuts. QueryRefs reach depth two, +with one LEFT join and both populated union positions. + +Actual callback inputs are copied before returning an ordinary observed value. +Every captured input must match one coherent authored before/after source row; +this permits legitimate retractions without accepting old/new field mixtures. +Startup joins may lack a side, but established public output is the strict +after-state. Initial readiness, held successful mutation, settlement and later +remote use all execute. Errors accumulate through those four cuts and are +thrown inside failure-preserving cleanup. Final publication cannot erase a bad +input already copied into an ordinary nested output. + +Independent final result: 580 tests, 377 pass and 203 intended +MetadataCallbackOracleFailure REDs, all from correlated-child receivers. +Root's 231 histories, nested inline preservation, direct-source controls and +Collection rejection pass. P10's leaks/spoofing remain unfixed. All 1,856 callback +and 297 inline/rejection states reach their minimum once (2,153 total), including +later cuts after an earlier mismatch. Valid unrelated seed/path/property inputs +still run the full matrix. Seventeen checker controls pass with native campaigns +unentered; the two inherited-field controls pass independently. Selecting only +two native direct-source histories fails mandatory missing-matrix reach, with +or without unrelated seed configuration. An invalid unknown-property selection +was a setup error and is not counted as rejection evidence. + +Review corrected a representation overconstraint before acceptance. Genuine +metadata need not be own; the checker now compares readable presence/value. +Nested authored inline fields are ordinary children data and remain strictly +compared. Two durable controls reject replacing an authored nested field or an +own undefined with inheritance. Independent scratch probes also reject mixed +before-label/current-pending facts and verify that an initial correlated-child +RED retains callback observations/reach at all four cuts. Illegal initial +correlation and mixed-key ordering fixtures were repaired as setup premises, +not claimed as product RED. Raw structured literal undefined remains a separate +P11 investigation, not an approved normalization or a law of this model. + +Existing owners remain unchanged in behavior: root metadata 1,604 GREEN, +functional projection 125 GREEN, functional boundary 18 GREEN; Effect retains +152 passes/64 known P09 REDs. Whole DB test TypeScript, targeted ESLint and +changed-file lint pass; two pre-existing require-await warnings remain. + +ORC-001–006: explicit decisions/architecture, independent authored facts, +literate layers, finite legal syntax/path grammar, real root/child callbacks, +ordinary derived outputs and 17 calibrated controls plus actual P10 REDs. +ORC-007 is inapplicable to bounded enumeration, with replaying:false explicit. +ORC-008: complete before/after facts distinguish retractions, without a second +queue model. ORC-009: callback input differs from published row; top-level +metadata descriptor freedom differs from nested authored shape. ORC-010: +all-cut reports and failure-preserving cleanup retain primary violations. +ORC-011: no further concrete shared-semantic-fault hypothesis remained. +ORC-012: this exact frozen record. ORC-013: individual/all/no collisions, +booleans, source/new/inline boundaries, typed peers and four cuts distinguish +the law. ORC-014: controlled core boundary only. ORC-015: all 2,153 states reach, +including valid seeded/unentered/partial selection controls. ORC-016: every +history/control uses the shared independent judgments, without fixed product +answers. This is instrument acceptance, not closure of P10's production class. + +Limits: bounded syntax/key grammar, one held successful mutation, flat inline +children, QueryRef depth at most two and one LEFT join. Schemas, complex +descriptors/prototypes, greater depth, other joins, same-key overlapping +transactions, truncate and real provider/framework paths are outside it. + +Evidence under `/private/tmp/rc2171-oracle-challenge/`: +`chunk17-independent-final-full.{json,log}`, +`chunk17-independent-final-seed.log`, +`chunk17-independent-final-controls.log`, +`chunk17-independent-final-unentered.log`, +`chunk17-independent-final-partial-seed.log`, +`chunk17-independent-controls.{test.ts,log}`. +Pre-delta no-seed selector: `chunk17-independent-partial-selection.log`. +Author current checks: `chunk17-final-delta-{full,lint,types,lint-changed}.log`. +SHA256 model: +`8ed276ebe90b678ad94ccfa83fbd76b3c0d0cbcb6e3397ffce7bd4ff6de76d1c`; +driver: +`330d7e0084af2848aed36829f5194c000e224be72141b71a35d0971cf847d84d`; +runner: +`817e7d63c4d560b511727f92434f2a176ba32342fd40d78adecf84a5d410d7a7`; +shared grammar: +`301154bdaa1fe42fbd9b0f668947bac3a54f7cdb33a83cd4df7579a9f12c16c1`; +source judgment: +`d60c483456f6d222060976ebd10dd68ddbb9481c685f785f7a36e0c82ec1f80f`; +architecture contract: +`fdfa5e2adef393dfd0818d2847ce1af22d231ff513fdaa07a0c34802e3a84ecb`. + +### Work remaining after chunk 17 + +Actual mutation guidance reception (O21a) is next. P11 literal semantics has +read-only evidence and needs owner/law scope and its own oracle work. Actual +example UI, diagnostic/guidance consistency, clean installed/packed consumers, +source/work/artifact checks and final finite-reach/P03/input-manifest/CI work +remain. P09, P10 and every earlier production RED stay deferred for the later +repair phase. + +## Chunk 18: actual authored mutation handlers (reviewed) + +Executable production baseline remains `858b174a65f160d13d01f0324c7481cc8e4eba3a`. +Author: `handler_compiler_review`; independent reviewer: `mutation_electric_review`. +No library, guidance, generated-documentation, dependency or lockfile repair, +commit or push occurred. The first pnpm exec attempted automatic dependency +verification and aborted with `ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY`. +Subsequent commands use installed Node entry points directly. Initial combined +runtime/typecheck rootDir errors are setup evidence, not product failures. + +Law: recommended successful handlers explicitly confirm their server changes +before settlement. Legacy returned values coordinate nothing. Authority is the +maintainer's explicit-confirmation decision, the optimistic acceptance/settlement +owner and the current After/CORRECT guidance. An API can commit and reject before +its handler reaches confirmation; that transaction may correctly settle on old +Collection base. Later explicit confirmation exposes committed server data +without changing the captured settlement-waiter rows. Resolved HTTP failure +differs from transport rejection. Its independently specified Error kind/message +is checked before comparing actual handler and transaction rejection identity. + +The shared mutation model now accepts complete authored rows while preserving +all original defaults. `confirmationEntry: unreachable` distinguishes API +rejection before acknowledgement from the old rejection-after-acceptance lane. +The new documentation owner supplies a separate actual-handler Promise judgment. +Expected values never come from observed utility calls or Collection output. +Strict ordinary rows preserve Dates and own undefined; metadata remains with its +own owner. API request operands are checked and used to construct the controlled +server ledger, rather than installing expected rows irrespective of the request. + +Eighteen actual fences supply 30 current handlers (21 Query, nine Electric) and +three explicit Before/WRONG examples. Current handlers cross success, rejection +before write and rejection after server write, with absent/present peer. Four +additional fetch histories resolve HTTP 503 before/after server commit with both +peer states. This is 190 bounded histories: 134 Query and 56 Electric. Only exact +initializer ASTs execute. Real QueryClient/Query and Electric adapters receive +controlled backend/SDK collaborators. Source delivery and actual utility +acceptance are separately held and observed; no missing confirmation is supplied. + +Independent frozen baseline: Query 132 PASS and two intended G01 settled-row REDs; +Electric 56 PASS; 43 shared/checker/ordinary support controls PASS. Query reaches +134 handler entries/API requests/settlement waiters, 90 server writes, 40 of 42 +required acceptances, 32 of 34 required source holds and 48 later confirmations. +G01's missing acknowledgement stages/comparisons remain zero and separately fail +reach; declared intent earns no execution credit. Electric reaches 56 entries/API +requests/waiters, 38 writes, 18 acceptances/holds and 20 later confirmations. +The frozen waiter is checked again after later source confirmation. Default +receivers still pass: Query 78 mutation plus six post-handler histories and three +controls (87), Electric 42, ordered refetch 24 histories plus three controls (27). + +Independent review found and repaired an instrumentation false green. An exit +gate kept the transaction pending after the actual handler returned. In-memory +transpile overlays removed exactly one actual refetch or awaitTxId await; both +wrong handlers passed at the initial freeze with all selected reach satisfied. +R3 removes that return gate and observes the actual extracted handler Promise. +A Node event-loop turn drains runnable continuations while real source delivery +remains held. Only the accepted asynchronous utility return is held for its +accepted-before-handler-return cut. Identical omitted-await overlays now fail +at `confirmation-requested: actual documented handler Promise`, without timeout. +Removing the HTTP status guard fails its independent handler-outcome comparison. +Removing await from already accepted synchronous writeUpsert and writeBatch +neighbors remains permitted and passes. Await spelling is not itself a law. + +Review also demonstrated an overbroad seed rejection: a valid shared seed failed +before any native handler ran. R3 follows the shared configuration contract: +seed 42 preserves full outcomes/reach, an own finite case plus seed 1 passes, +and unrelated actual path replay skips all 134/56 cases. A partial native +`mutations-after` selection runs six passing cases and skips 128, then fails +missing declared reach equally with seed unset or 42. Exact finite selection uses +`TANSTACK_DB_DOCUMENTED_MUTATION_CASE`; G01 replay preserves its settled mismatch. +Own selector reduction and unrelated shared seeds are separate policies. + +Direct bounded TypeScript, targeted ESLint and changed-file lint pass. Runtime +commands use the installed Vitest entry from each package, with coverage and +typecheck disabled; typechecking is a separate command. This does not claim +whole-workspace type correctness. The independent reviewer checked all seven +frozen SHA256 values before and after its native, selector, original-owner and +overlay executions, and found no further supported instrument defect. + +ORC-001–006: explicit authority, independent shared outcome/handler judgments, +literate layers, bounded exact-handler grammar, real adapter cuts, full rows and +executed hostile controls. ORC-007: randomized campaigns are inapplicable to this +finite enumeration; own direct replay is verified. ORC-008–009: API commit, +confirmation entry/acceptance, handler settlement and transaction settlement are +independently distinguishable. ORC-010: exact errors, frozen waiter rows and +failure-preserving all-resource cleanup. ORC-011: no further concrete shared +semantic hypothesis requiring a second formulation remains after the direct +handler observation repair; no universal independence claim follows. ORC-012: +this versioned frozen entry. ORC-013: genuine holds reject detached confirmation, +while synchronous accepted-write neighbors remain lawful. ORC-014: no real +provider/host handoff is claimed beyond the controlled boundary. ORC-015: actual +per-case stages and comparison counts above, including G01's unreached stages. +ORC-016: fixed histories use the shared judgment; extraction/request/selector +examples remain in ordinary support tests. This is instrument acceptance, +not a full GREEN, readiness or production bug-class closure claim. + +Limits: selected initializer ASTs, one edited key and optional peer. Full +factories, schemas, query/component execution, awaitMatch/timeout guidance, +concurrent handlers, persistence wrappers, durable direct-write receipt holding, +live Electric/backend providers and framework scheduler cuts remain outside it. +G01 stays open for the later repair phase. + +Evidence under `/private/tmp/rc2171-oracle-challenge/`: +`chunk18-author-evidence-r3.md`, `chunk18-freeze-r3-sha256.json`, +`chunk18-{query,electric,controls}-independent-r3.log`, +`chunk18-original-{query-owners,electric-owner}-r3.log`, +`chunk18-ordered-owner-r3.log`, `chunk18-unawaited-{query,electric}-before-r2.log`, +`chunk18-unawaited-{query,electric}-r3.log`, `chunk18-http-status-guard-r3.log`, +`chunk18-write-{upsert,batch}-neighbor-r3.log`, `chunk18-seed-before-query.log`, +`chunk18-partial-query-{no-seed,seed}-r3.log`, +`chunk18-{query,electric}-seed-r3.log`, +`chunk18-{query,electric}-unrelated-path-r3.log`, +`chunk18-{typecheck,eslint,lint-changed}-r3.log`. +Frozen file hashes: + +- Shared mutation model: `a521be9b638934556542837670261c1cb0eee0eae44c9912968c1add40a14e8d`. +- Documentation owner: `478405dd2af4964da9c3981caa5ddaba6e792e2151c2c1c9faf6e03e53c6f0d2`. +- Support driver: `d35aad2629ef81d9f8ccec7226e33e85be08df00119bf838714a2d3a0bc826d7`. +- Documentation controls: `450e51629ce8a885211dd35c4c1941d5e3585864053b405bf3bdc2d08254bf65`. +- Ordinary support tests: `8a7778c86642d10e0fa74382b859e42ad2dd7191c52ea34624937ef95c00a85e`. +- Query receiver: `86092d4b33c4640a78fe5e385ad9afcffb4fb9af9d5cc8fc66957ab2c78fbdc3`. +- Electric receiver: `72ced5986b32722b9bdb2a891bd634d0e7bb8d064e72474c37aa275ba5039dbe`. + +### Work remaining after chunk 18 + +P11 now has separately supported runtime literal-preservation and public type +inference findings. Its next three review gates are shared root/QueryRef runtime, +same-model correlated inline array/singleton/facade receivers, and exact public +type receivers. Current rejection of a broader mixed-expression array is an +unresolved admission question, not authority that the input is invalid. Types +judge required keys; runtime judges own properties. The corrected pre-edit +inventory and evidence manifest are retained in scratch. Actual example UI, +diagnostic/guidance consistency, clean installed/packed consumers, source/work/ +artifact checks and O26 finite-reach/P03/input-manifest/CI work remain. Every +library and guidance repair remains deferred until all oracle chunks finish. + +## Chunk 19: selected literal values at root and QueryRef boundaries + +Accepted after independent oracle review at unchanged production HEAD +`858b174a65f160d13d01f0324c7481cc8e4eba3a`. The final R2 freeze changes three +files: the shared literal model, its production driver, and the existing +`subquery-user-value-oracle.test.ts` registration owner. The independent reviewer +found one conformance defect: the companion module lacked its own declaration. +R2 adds only that declaration and its import. Removing those additions +reconstructs the exact R1 hash; both evidence sets remain available. + +Law and authority: selected null and undefined pass through, and recursive +authored records preserve their selected members, including empty records. +The public builder descriptions at `builder/types.ts:402–437`, plus the +architecture's user-value and publication laws, supply this authority. +Ordinary JavaScript interprets test-only data/reference descriptions. The model +imports no production IR or semantic classifier. Virtual-field attribution and +public type accuracy remain separate judgments. + +Before edits, 48 exact emitted public query forms compiled without diagnostics. +Nested omission then exposed empty-record loss. Five adjacent forms were +predicted from the same recursive-record law before their addition; their +20 exact translations also compiled without diagnostics. The expanded grammar +has 17 forms, two property positions and two receivers: 68 histories, each +observed at initial readiness and two synchronous full-row source commits. + +RED on unchanged production: **103 tests, 85 pass and 18 fail**. The new +histories contribute 50 passes and 18 intended assertion failures: eight P11 +undefined-to-null cases and ten P12 empty-container-loss cases. The other +passing tests are six existing relational histories, 24 hostile controls, +four lawful controls and one grammar calibration. An independent one-row +P12 witness selects only id/version references and `value: {}`; no raw nullish +member is needed to expose the missing selected property. + +Reach: all **204 receiving states and 102 QueryRef callback states** execute +exactly once. Root independently verified frozen hashes, reran the native owner +and recomputed these counts. Eager tagged captures retain own undefined, +absence, null, dense array indices/length, complete ordinary rows, event +type/key/current/previous values, event-time public reads and callback inputs. +All lawful later cuts are recorded before judgment, so initial REDs do not +erase subsequent evidence. Cleanup diagnostics remain distinct. + +The shared comparison rejects 24 wrong observations, including container loss, +wrong or duplicate changes, stale previous values, mixed callback inputs and +missing current callback inputs. Four positive controls preserve legal event +partition, change ordering, repeated callbacks and empty startup callbacks. +Independent scratch controls additionally verify immutable nested capture, +root-only metadata exclusion and continuation through all cuts of an actual +failing QueryRef history. Partial native selection still fails missing reach +with a valid unrelated shared seed/path; calibration-only selection passes. + +Strict compilation of the actual three-file authored program has zero +diagnostics. It proves driver admission, not public inferred-type correctness. +Targeted ESLint, formatting and scoped diff checks pass. R1 and R2 changed-file +lint both pass. The final R2 hashes were verified again after lint. + +ORC-001–006: authority, independent judgment, literate responsibilities, +finite legal grammar, actual public receivers and executed negative/positive +controls are evidenced above. ORC-007 does not apply to exhaustive finite +enumeration; selection/reach behavior is checked. ORC-008–009: stateless +before/after recomputation and explicitly mapped expression descriptions avoid +duplicated lifecycle machinery. ORC-010: eager capture, continued lawful cuts +and separate cleanup diagnostics; cleanup-fault injection remains unclaimed. +ORC-011: no concrete remaining shared-semantic hypothesis requires another +formulation. ORC-012: this exact R2 versioned entry and the independent audit. +ORC-013: undefined/null/omission, raw/explicit/reference, pure/mixed and +empty/nonempty neighbors distinguish the wrong rules. ORC-014: the claim ends +at controlled core sync sources. ORC-015: all 306 declared states execute and +partial campaigns fail. ORC-016: every history and calibration uses the same +model/check; there are no standalone concrete type assertions. + +Limits: dense finite arrays, bounded records and two source updates. Correlated +arrays/singletons/facades and generated public-type receivers remain separate +sequential gates. Sparse authored arrays, symbols, descriptor flags, arbitrary +recursion, cleanup/restart, provider/framework scheduling and broad mixed +raw-nullish/IR.Value admission are unproved. The latter admission question is +not an invalid-input exclusion. All product REDs remain open; this accepts an +instrument, not a production repair, GREEN result or PR readiness claim. + +Evidence under `/private/tmp/rc2171-oracle-challenge/`: +`chunk19-freeze-manifest.json`, `chunk19-r2-freeze-manifest.json`, +`chunk19-freeze-evidence.md`, `chunk19-reproduction-commands.md`, +`chunk19-typecheck.mjs`, `chunk19-root-native.log`, +`chunk19-independent-r2-{native,types,controls,partial-seed}.log`, +`chunk19-emitted-types-preedit.log`, `chunk19-empty-container-types-preedit.log`, +`chunk19-minimal-empty-record-red.log`, `chunk19-r1-r2-model.diff`. +Final frozen file hashes: + +- Model: `898ef5e00aec151cbb9367dc1f8b0fe5f488b02e75f57f504dc8224b9dbdc5b6`. +- Driver: `4afcf5213153c285ba9e917fffd6e44c816bf0953bdd872aced065a087e865d9`. +- Registration owner: `3cb39cfb878b7bbd8682838cc1958c70dc36536c2ea0de200f2952ba18f44ebf`. + +### Work remaining after chunk 19 + +Next are same-model correlated inline array/singleton/facade receivers, followed +by exact public-type receivers. The actual example UI, guidance/diagnostic +consistency, clean installed/packed consumers, source/work/artifact checks and +O26 finite-reach/P03/input-manifest/CI work remain. Source-inspected P13/P14 +guidance conflicts join that inventory. Library and guidance repairs remain +deferred until all oracle chunks finish. + +## Chunk 20: correlated literal receiving paths + +Accepted after independent oracle review at unchanged production HEAD +`858b174a65f160d13d01f0324c7481cc8e4eba3a`. Freeze manifest SHA256: +`df44856c6d4772f3af501cfb4a0abc82e40cc1e8605a721852a3fe2787de097b`. +Four new test modules supply the materialization judgment, concrete factories, +production driver and campaign. The fifth frozen input is DB's package manifest, +which registers the new campaign once and preserves earlier registrations. +Accepted chunk 19 files remain unchanged. + +The same ordinary-JavaScript literal model now receives 17 forms × two selected +positions × direct/QueryRef child composition × four public materializers: +`toArray`, array `materialize`, singleton `materialize`, and Collection-valued +`include`. Architecture's nested-materialization/publication laws add ordered +children, canonical empty values and coherent Collection-facade observations. +Two parents share a populated bucket, a third has an empty bucket, and two +atomic full-row child updates reuse the literal source states. The existing +facade interval judgment checks handle identity; it is not reimplemented here. + +Exact public queries compiled before receiver edits. The first native run +rejected 136 QueryRef constructions before comparison: their outer child had +no correlation WHERE. Public live-query guidance at lines 1307–1323 requires +the child query's own WHERE to contain the parent/child equality. This is missing +premise evidence, not a literal-law RED. The revised legal query explicitly +selects `routeGroup`, correlates it in the outer WHERE, captures that complete +authored callback input and removes only that field in the actual returned +JavaScript object. No recorder normalization hides it. Every revised exact form, +including a later lexical rename to satisfy lint, compiled before factory edits. + +Native result: **305 tests, 233 pass and 72 intended assertion REDs**. The 272 +main histories supply 200 passes and 72 failures exposing the same P11/P12 +literal defects through all four materializers and both child compositions. +All **3,264 declared path × cut states** execute exactly once, including public +rows, root events, legal functional inputs and facade events/identity/empty +readiness. No driver-execution failures remain. Independent native and unrelated +seed-123 runs match. Root separately verifies all five hashes and reproduces +the 72/233 result and 272 distinct main titles. + +Every observation retains complete ordinary rows, ordered inline values and +facade layouts, event type/key/current/previous values, callback-time root and +facade snapshots, authored callback inputs and retained inline values. Facade +handles in old parent payloads remain live; inline snapshots remain immutable. +All lawful later cuts run before judgment, preserving the first mismatch and +distinct execution/cleanup diagnostics. No compiled Collection-valued include +enters a parent functional callback. + +Review repaired three instrumentation boundaries. Facade rows and event-time +layouts now use ordered comparison, so bag equality cannot hide reversal. +Startup callback inputs may have the canonical empty child value before the +initial snapshot arrives; each complete current parent must still be consumed +by readiness. This prospective correction follows the architecture, with old +and new predictions recorded before editing; the native run already passed +that facet. After readiness, atomic before/after comparisons remain strict. +One initial startup calibration mutated an aliased public array. Those runs are +preserved but excluded from RED evidence; the corrected isolated callback +control rejects three inputs under the copied old checker and passes under the +corrected law. Wrong partial/mixed startup inputs still fail. + +Vitest's formatted table labels also truncated distinct cells into duplicate +names: the first JSON has 272 main histories but 221 unique names. Literal +titles now preserve all 272 IDs. Exact failing and benign selectors each enter +one intended cell; even a passing selected cell fails mandatory missing-matrix +reach. Calibration-only selections enter no campaign, and unrelated seeds do +not waive reach. O26 separately inventories similar title-identification gaps +in older accepted campaigns without retracting their normal reach evidence. + +All **29 hostile wrong-observation controls and four lawful controls** pass. +These are checker calibrations, not production mutants. Six independent +scratch controls isolate facade layout, previous event values, startup callback +freedom, complete ready inputs, mixed/routing-field corruption and continuation +of an actual failing singleton. They verify intended violation names and +unchanged unrelated public facets. The reviewer source-audited all shipped +attacks for calibration alias leakage. Strict authored-program TypeScript +compilation has zero diagnostics; targeted lint, formatting and scoped diff +checks pass. The existing installed-dependency mismatch remains unchanged. + +ORC-001–006: public authority, independent literal/materialization judgment, +literate responsibilities, exact legal grammar, actual receiving paths and +isolated negative/positive controls. ORC-007 is inapplicable to exhaustive +finite syntax; exact selection/reach is verified. ORC-008: stateless +recomputation plus the existing facade interval model. ORC-009: explicit +data-description and routing-field mappings. ORC-010: eager captures, all-cut +continuation and distinct cleanup diagnostics; cleanup-fault injection remains +unclaimed. ORC-011: no concrete remaining shared-semantic hypothesis requires a +second formulation. ORC-012: this exact frozen record and independent audit. +ORC-013: empty/full, ordered/reversed, literal/carrier and startup/ready neighbors +distinguish the wrong rules. ORC-014: controlled eager sources only. ORC-015: +all 3,264 states execute once and partial selections fail. ORC-016: all histories +and calibrations use shared judgments, without standalone expected product rows. + +Limits: finite dense values, fixed routes, eager controlled sources and two +atomic child updates. Providers, framework schedules, arbitrary recursion, +descriptors, metadata attribution, optimistic histories and route churn remain +outside this slice. Public inferred-type accuracy is the next gate. All 72 +production REDs remain open; instrument acceptance is not a GREEN repair or +PR readiness claim. + +Evidence under `/private/tmp/rc2171-oracle-challenge/`: `chunk20-freeze.json`, +`chunk20-preedit{,-v2,-v3}-inventory.json`, `chunk20-native-first.{log,json}`, +`chunk20-native-final.{log,json}`, `chunk20-root-native.{log,json}`, +`chunk20-independent-*`, `chunk20-exact-selection.{log,json}`, +`chunk20-unrelated-seed.log`, `chunk20-startup-prediction-before.json`, +`chunk20-startup-control-{before,after}.log`, retained +`chunk20-startup-control-aliased-{before,after}.log`, +`chunk20-root-title-collision-before.json`, `chunk20-root-order-control-after.log`, +`chunk20-runner-registration.diff`, and `chunk20-types-final.log`. +Frozen artifact hashes: + +- Model: `87b11a0f33f7d6da215470c0a1e2d331484463735a8e974698e5d95ca9bf954d`. +- Factories: `c0dc046789b30179a43d3e7bb9405b232709a47611e7c214826e14ac4d60ca70`. +- Driver: `36dacf502763f264a57e9e3e7c4d9a241c450437e2d13088b8c135a9d8cc0b86`. +- Campaign: `17c701f2c31bfa74888777e817e5357d87b16413228c8ebc30adc3fac3153f7c`. +- Package input: `8838fe02c99c58b51535823e3f3e565fa61e2a2433d89d239dde7636ae8ffb57`. + +### Work remaining after chunk 20 + +Next is the generated public-type slice for the exact accepted standalone and +correlated syntax. Actual example UI, diagnostics/guidance consistency, +P14's swallowed source-health call outcome, clean installed/packed consumers, +source/work/artifact checks, and O26 finite-reach/replay/input-manifest/CI work +remain. The O21 plan preserves historical diagnostic-baseline reconstruction: +today's errors cannot be automatically adopted through old file/hash waivers. +Library and guidance repairs remain deferred until all oracle chunks finish. + + +## Chunk 21: exact source-public literal type receivers + +Authored and frozen for independent review at unchanged executable-library HEAD +`858b174a65f160d13d01f0324c7481cc8e4eba3a`. This entry does not claim independent +acceptance. Three new test files supply an independent recursive type law, +compiler/source driver and finite campaign. Accepted chunk 19/20 runtime inputs +and DB's package manifest remain unchanged. + +**Law and authority.** The public `ResultTypeFromSelect` documentation at +`builder/types.ts:402–437` promises exact null/undefined, extracted reference +values and recursive selected records. The live-query guide's select and virtual +properties sections distinguish selected ordinary values from the separate +public-output virtual layer. Architecture's nested-materialization and callback +input laws supply array, absent singleton, Collection and strict own-shape +callback boundaries. The model imports authored history descriptions, with no +production conditional-type, IR-classifier or projection helper. Primitive +literal widening remains permitted within its kind; `{}` means an empty +declared static surface, not an exact empty runtime inhabitant set. + +**Paths and reach.** The campaign extracts the accepted runtime AST expressions +into 68 standalone and 272 correlated concrete modules. Query syntax is guarded +against semantic drift. Actual callback inputs are marked before the real +consume call and routing-field destructuring. Each module receives actual +QueryResult and inferred Collection-row types; correlated paths add child, +inline/singleton or public facade/get/toArray/downstream markers. There are +1,870 marker judgments and 2,550 declared syntax/admission/marker states. +TypeScript 5.9.3 compiles every source without diagnostics against this +checkout's public `src/index.ts`; strict null checks are enabled. All 2,550 +states execute exactly once. All 387 test names and 340 native IDs are unique. +The exact 340 sources and their fingerprints are retained in scratch evidence. + +**RED.** The corrected native run and unrelated seed-1 run agree: 387 tests, +107 passes and 280 intended assertion failures. Of 340 native histories, +60 pass and 280 fail. Forty histories infer raw null/undefined as unknown; +272 expose forbidden contextual-input metadata or its propagation into nested +inline values, with 32 overlapping. No other native mismatch category remains. +Every marker comparison executes before the history reports its mismatches. +These are pre-repair observations; no production change is included. + +**Formulation correction.** The initial admission-clean run judged public +QueryResult roots too strictly. Before editing that model, a retained authority +note recorded old/new predictions and followed the guide's public-output +promise to its source. Root and the independent reviewer approved the bounded +correction before editing: exclude exactly four virtual fields at named public +query-output and actual published/source Collection row roots from this +ordinary-value judgment. Callback inputs and nested inline shapes remain +strict. Ordinary `routeGroup` is required on the actual routed input and omitted +only by the authored rest destructuring. Raw-nullish unknown remains RED. No +metadata correctness follows from the separate-layer exclusion. Full static +callback metadata coverage belongs to existing +`virtual-row-fields-types-oracle.test.ts`; this slice adds receiving evidence +for that accepted law, rather than changing product policy. + +**Controls and selection.** All 47 controls pass: 34 compiler-recorded wrong +static observations, eight AST/actual-marker-binding attacks, two lawful +formatting freedoms and three native marker-isolation controls. Wrong answers +include any/unknown/never, lost nullish alternatives, optional selected members, +wrong containers/array mutability, facade/get loss, routing-field corruption and +reserved-name leakage at strict inputs/inline children. These are checker +calibrations, not production mutants. Checker-only selection passes 47 tests +and leaves native reach unentered. Selecting the positive standalone zero cell +passes its actual comparison but fails the mandatory missing-matrix reach, +even with an unrelated shared seed; 386 tests are skipped. Exhaustive finite +syntax has no seed replay waiver. + +**Setup failures kept separate.** The first exported synthetic receiver hit +TS4058 declaration portability before any intended law comparison. The current +source-public inference module has an unexported synthetic wrapper; native +query inference is unchanged. This repairs the test path but does not establish +that exported inferred factories are unsupported. The exact repro and a +handoff preserve exported factory, exported Collection, explicit declaration +settings, and source versus built/installed controls for O22. An early AST +printer also treated trailing commas as drift; semantic canonicalization now +permits formatting while preserving ordered node kinds, names and children. +Neither setup failure is counted as intended product RED. + +**ORC review.** ORC-001–006: independent authority-derived recursive judgment, +literate responsibilities, exact legal inputs, actual compiler receivers, +per-marker observations and executed wrong-result controls are recorded above. +ORC-004 reconstruction/ablation/range: 17 accepted forms cross root/nested +positions, direct/QueryRef and all four correlated materializers. Removing each +axis loses its matching callback/container obligation. AST controls reject +nearby cast, carrier, routing and marker substitutions. ORC-007 is inapplicable +to finite exhaustive syntax; selection/reach remains enforced. ORC-008–009: +stateless static surfaces, explicit separate virtual-layer and authored +routing-field abstractions, with no lifecycle state model. ORC-010: compiler +diagnostics retain file/span/code and remain separate from all type judgments; +no runtime cleanup or shrink path is involved. ORC-011: direct public compiler +facts avoid DB helper-alias self-agreement; no remaining concrete shared-semantic +hypothesis requires another model in this slice. ORC-012: this frozen entry and +the pending independent audit. ORC-013: exact nullish/omission, required/optional, +raw/explicit/reference, mutable/readonly/tuple and facade/inline neighbors reject +wrong boundaries. ORC-014: actual installed TypeScript source-public compiler, +with packed/npm receiving work explicitly deferred. ORC-015: every declared +state reaches once; a partial native selection fails. ORC-016: all native and +calibration judgments reuse the recursive type law, with no standalone +concrete type assertions. + +**Limits.** This checks selected declared value surfaces, property optionality, +nullish alternatives and array mutability, not ordinary-record readonly +modifiers or metadata correctness at excluded output roots. Empty-array element +precision is unclaimed beyond no any. Broader mixed raw-nullish/IR.Value array +admission remains unresolved; no rejected form is silently blessed. Runtime +publication, provider/framework scheduling, arbitrary recursion and packed +exports are separate owners. The 280 product REDs remain open. Production +repair, GREEN result, production-mutant kill and PR readiness are unclaimed. + +Evidence under `/private/tmp/rc2171-oracle-challenge/`: +`chunk21-native-r3.{json,log}`, `chunk21-native-seed.{json,log}`, +`chunk21-corrected-marker-failures.json`, `chunk21-{compiler,reach}.json`, +`chunk21-controls-only.{json,log}`, `chunk21-partial.{json,log}`, +`chunk21-consumer-inventory.json` and `chunk21-consumers/`, +`chunk21-output-vs-input-authority.md`, +`chunk21-exported-receiver-{repro.mts,o22-handoff.md}`, +`chunk21-metadata-type-owner-next-plan.md`, `chunk21-types-r3.log` and +`chunk21-eslint-r2.log`. The final freeze manifest records source and evidence +hashes after formatting and changed-file lint. + + +### Chunk 21 R2: declared record capabilities + +Independent frozen review reproduced the original native/seed/reach evidence +and found a recorder defect. A callable or constructable record with no named +members was recorded as an empty record. The model therefore accepted an +inferred `value: () => void` or `value: new () => {}` for authored `value: {}`. +This concerns declared static capabilities, not the empty-record inhabitant +limit: the wrong type permits invocation that the selected object does not. + +The original manifest, logs and exact three source files are preserved in +`chunk21-freeze.json` and `chunk21-r1-source/`. Before changing the recorder or +comparison, eight calibration histories add call/construct signatures to the +existing empty/nonempty selected record forms at existing root/nested positions. +All eight fail because the old shared judgment returns zero violations; lawful +companions admit. R2 records call/construct presence with public TypeScript APIs +and rejects invented signatures in the existing recursive record rule. + +The R2 native campaign has **395 tests, 115 passes and the same 280 intended +native REDs**; its failed native test-name set exactly matches R1. All 340 +consumers still admit without diagnostics, all 2,550 states execute once and +all 395 titles are unique. All **55 controls** pass, including 42 compiler +recorder controls. Package TypeScript compilation and targeted ESLint pass. +This repairs an instrument only. It changes no product behavior or expected +native law, and supplies no production GREEN or closure claim. The R1 +partial-selection and unrelated-seed evidence remains unchanged. + +R2 evidence uses the same scratch directory: `chunk21-r2-signatures-red.*`, +`chunk21-r2-native.*`, `chunk21-r2-{compiler,reach}.json`, +`chunk21-r2-{types,eslint}.log`, and `chunk21-r2-freeze.json`. Independent R2 +acceptance remains pending. All prior limits and deferred owners still apply. + +Independent R2 review accepted the bounded instrument at the exact frozen +sources. Owned files, accepted inputs and preserved R1 snapshots match their +hashes. The independent native run reproduces 395 tests: 115 pass and the same +280 native assertion REDs, with all 2,550 states once and 395 unique titles. +Twenty independent compiler/AST controls pass, including both original erased +signature witnesses. Strict three-file authored-program typechecking has zero +diagnostics. The eight retained pre-repair controls demonstrate the old +checker failed at its intended comparison. Independent evidence: +`chunk21-independent-r2-native.{log,json}`, +`chunk21-independent-r2-controls.log` and +`chunk21-independent-r2-typecheck.log`. R1 full-seed, checker-only and partial +selection evidence remains preserved. No supported finding remains within this +bounded slice; native product REDs and all stated limits remain open. Final R2 +freeze manifest SHA256: +`da237233542b78a0de9eb836ecbeed12b6b14c2e80f91837eb577c8813bcf0a7`. + +## Chunk 22: publication eligibility without dist-tags + +Accepted at merged HEAD `5fa6eca05530bb9e1f4748b7c00ae60976da2808` with the +publisher unchanged. The independent release ledger still defines eligibility +by exact registry-version membership. Candidate uploads use rc and stable +uploads use latest; partial successful uploads survive retry. Missing tags do +not permit another upload. Repairing historical wrong tags is unclaimed. + +The existing HTTP receiver retains all 14 histories and adds 15. New histories +cross absent/stale tags with present/missing exact versions, reversed +heterogeneous candidate versions and mixed current/missing packages. Later +candidate failure occurs only after observing an actual successful candidate +upload, independent of package order. Independent review added the exact P01 +aftermath (older rc / current latest) and stable current versions without a +latest tag. Invalid tags pointing at nonexistent versions remain excluded. + +The real default publisher runs through npm 11.17.0, pnpm 11.1.0 and Changesets +2.29.8. The isolated HTTP registry checks first-GET facts, raw attempts, actual +packed identity/digest and final registry state. Exact local restoration covers +pre.json and four public fixture manifests before subsequent judgments. An +early stable duplicate is compared before requiring candidate metadata; that +history records candidate reach zero. + +Native result: 35 tests, 21 passes / 14 intended assertion REDs. Seven reject +wrong tags (P01); seven reject real duplicate attempts (P05). The old histories +retain eight native passes / six REDs. Stable/candidate metadata reach is +29/28, restoration 29, completed comparison 15, packed upload 19, and controlled +failure/partial success/retry 11 each. Heterogeneous versions reach eight, +mixed presence four, and actual candidate-success-before-failure two. The +retained raw per-history records include all unreached zeros. Sibling version, +seed and command campaigns give 120 passes / 12 original tag REDs; ordinary +fixture/publisher controls give 29 passes. Scoped and branch oracle lint pass. + +Fresh-tool scratch hosts use an explicit node_modules link to Gate A's installed +Vitest 4.1.12 tools; source/config hashes match the worktree. This is test-tool +reuse, not another clean install or installed-package consumer. Gate A's 3,400 +frozen source hashes remain unchanged. The initial socket EPERM is a setup +failure and supplies no product evidence. + +Independent reviewer `facade_metadata_review` accepted v2 after independently +running seven new neighbors across v1/v2. They observed the stable-path pair of +409 responses before any candidate GET and the exact wrong-tag aftermath's +zero-upload pass. Root verified all final source/evidence hashes and parsed +35 unique titles with the authored 21/14 split; root did not rerun the full +HTTP campaign. Exact-title replay selected one stable-path RED with 34 tests +unselected. Production remains unrepaired. + +ORC-001–006: authority, independent ledger, literate layers, reconstruction, +real HTTP comparisons and calibrated raw multiplicity are retained. ORC-007 +random campaigns do not apply to this finite enumeration; actual title replay +is verified. ORC-008–009: no reference state-machine change; versions, tags and +uploads retain distinct meanings. ORC-010: raw primary failures and existing +cleanup controls are preserved. ORC-011: no additional shared-semantic fault +requiring a second model was identified. ORC-012: exact record and frozen +independent audit are retained. ORC-013: target presence, absent/stale tags, +stable/candidate paths, reversed versions and retained-success neighbors +distinguish wrong rules. ORC-014: actual tools receive controlled HTTP facts; +public npm infrastructure/authentication is unclaimed. ORC-015: mandatory +per-history/path reach, including zero later reach, is measured. ORC-016: +histories and calibrations reuse the shared judgment; ordinary controls remain +separate. + +Evidence under `/private/tmp/rc2171-oracle-challenge/`: +`chunk22-author-evidence-v2.md`, `chunk22-http-v2-{traces,evidence}.json`, +`chunk22-command-neighbors-v2.json`, `chunk22-controls-v2.log`, +`chunk22-independent-review-v2.md`, and `chunk22-root-validation.json`. +Final freeze SHA256: +`62d27c1e152e5ce77d09ba4d863b0685370df15342fac69f2fc132db49a09a66`. +Independent review SHA256: +`e4d4a9cabdc4e8961a4785d67b948694a3384ad2e90d707d827efdc83cba8fc8`. +Limits remain four fixture packages, bounded versions/histories, POSIX process +cleanup and controlled registry. Arbitrary topologies, public credentials, +Actions, installed APIs/declarations and production GREEN/mutant closure remain +unclaimed. + +## Chunk 23: constructive deferred-row order reach + +Accepted at merged HEAD `5fa6eca05530bb9e1f4748b7c00ae60976da2808`. +The SortedMap law and independent Map/full-sort model are unchanged: a row +changed while still deferred sorts by its current value at the restoring read. +The CI failure was reach four against minimum five, without a row mismatch. + +The generated prefix places a peer, defers a distinct target, moves the target +across the peer and compares the restoring read. A clear then restores the +original empty starting condition before executing the entire original random +suffix. Both comparator directions distinguish stale placement. All four +fixed examples, random domains, seed 20260912, both 100-run budgets and the +minimum five remain. Reach is credited only at a successful promised-order +comparison; equal ranks, replacements, deletions, clear and unpromised order +receive no credit. + +Native and independent runs pass 48/48 tests: 26 oracle/calibration tests and +22 supplemental ordinary examples. Each comparator campaign reaches the +changed-deferred comparison 96 times. Positive read/prune controls reach 16; +negative controls reach zero. An omitted-observation challenge fails the +unchanged reach obligation, separately from the product-mutant result. Exact +native and in-memory eager-placement-mutant replays each execute one actual +history. Native passes; the mutant fails the shared ordering comparison with +current row values in stale order. Full DB test-project typechecking and direct +lint pass. A pnpm freshness-triggered install attempt aborted and supplies no +lint evidence. + +Independent reviewer `metadata_timeline_review` accepted the frozen oracle +SHA256 `0c56bb680efba1fa1c66ec5d0da02716324f12d9383159a2321adced2a375754`. +Root verified the saved source, evidence and scratch-config hashes and the +48-pass native report; root did not rerun the campaign. Production is unchanged. +The 3,400 Gate A source files remain intact. Evidence under +`/private/tmp/rc2171-oracle-challenge/`: `chunk23-author-evidence.md`, +`chunk23-independent-review.md`, `chunk23-freeze.json` and +`chunk23-root-validation.json`. Freeze SHA256: +`9f9df6df1cd9da3e592d3aa6e44c2bc9674f2f4138fa4366a1ef8c1e3c670406`. +Independent review SHA256: +`fe53302e71ef0d1f6e9d902295eb3f56c1aceccf2e3faf2ada66b38dbd7a5bd5`. + +ORC-001–006 retain the accepted authority, independent model, literate layers, +unchanged suffix grammar, actual read and mutant comparison. ORC-007 retains +fixed/random parity and verified direct replay. ORC-008–010 concern reach-only +bookkeeping and preserve primary failure identity. ORC-011 has no new named +shared semantic fault requiring another formulation. This exact record supplies +ORC-012. ORC-013–016 have the crossing-rank distinction, unchanged internal +receiving scope, measured reach and separate ordinary examples. No replay of +the original CI random history is claimed because its seed/path was not saved. +Other reach states remain sampled. Existing key, comparator and mutation-during- +iteration limits remain; no public Collection or provider expansion follows. + +## Chunk 24 — declared ordinary metadata type receivers + +Author freeze on merged head `5fa6eca05530bb9e1f4748b7c00ae60976da2808`. +Independent review is pending. Production is unchanged. + +The existing public metadata type owner now imports the receiver-types oracle +and native compiler support. Its original 24 source-description campaigns remain. +The separate chunk-21 owner and its 374 strict callback/parent markers remain +byte-identical. This extension enumerates 34 context families over those same +24 source descriptions: 14 recursive callbacks, 16 direct/QueryRef child +materializers and four source-row positive neighbors, for 816 receiving pairs. + +The declared ordinary shape has required id, scalar, selected payload and +routeGroup members. An optional source profile yields a required selected value +that permits undefined. The nested ordinary record deliberately uses all four +reserved field names with ordinary source-derived types. The child callback +observes its real input before removing routeGroup with rest destructuring. +Inline parents observe the resulting child values before publication. Compiled +Collection-valued includes stay outside parent functional callbacks. Their +published facade row, get, downstream callback, QueryResult and Collection +receivers supply separate positive observations. + +- **Law:** ARCHITECTURE.md's reserved-public-field callback law requires own-shape + selected inputs to remain ordinary before user JavaScript. Its nested + materialization law preserves inline shape and publishes Collection metadata + at facade boundaries. The existing public type law supplies readonly virtual + fields, union members, ordinary property modifiers and nullable get results. +- **Reach:** all 816 consumers admit with zero diagnostics. The independent + signature law judges 4,608 actual type markers. All 6,240 declared + syntax/admission/marker states execute exactly once. The finite policy is + explicit: an entered subset owes the entire receiving matrix; unrelated seed + settings waive none; controls-only selection enters no native matrix. +- **RED:** unchanged production has 684 failing new histories: 672 expose the + ordinary callback/parent/inline metadata leak, and 12 expose loss of + discriminant-specific ordinary fields through structured source spreads. + The other 132 histories pass. The original 24 native campaigns remain RED. + A full run has 882 distinct titles: 174 pass and 708 intended native REDs. +- **GREEN:** all 42 compiler/checker/AST controls pass, as does strict checking + of the three authored files and targeted oracle lint. Targeted ESLint has + zero errors and one existing require-await warning in the legacy construction + helper. No repaired-production GREEN is claimed. +- **Mutant:** wrong observations pass source admission but fail the same + independent judgment for fabricated input metadata, lost source metadata, + nested ordinary reserved fields, routing fields, readonly/optional modifiers, + wrong singleton/array/facade containers and missing absence. AST controls + reject model-rebound or late markers, wrong rest bindings and forced callback, + query or result inference. These are checker/syntax controls, not a + production mutation campaign. +- **Limits:** the new functions are compiled rather than invoked. Runtime + callback ownership remains with the existing metadata input owner. This does + not establish built declarations, npm resolution, provider/framework timing, + arbitrary recursion or exact allocation provenance for opaque equal-typed + callbacks. No production repair or bug-class closure is claimed. + +The static allocation limitation was executed separately before this edit. +A cast-free annotated callback returning its source row and one returning +`Object.assign({}, row)` have equal callback signatures, returned public query +and output types, downstream input types and downstream query types under +TypeScript 5.9.3, with zero diagnostics. That evidence prevents opposite static +expectations based only on syntax known to the generator. It grants no exception +for the 374 existing knowable ordinary-shape markers, and changes no runtime +law. Top-level reserved-name collisions also retain their explicitly recorded +static provenance limit. + +Author tools are borrowed from the fresh Gate-A installation through symlinks, +without installs or dependency/version changes. Initial author runs inherited +Vite's package-local `.vite/vitest` cache, and configuration bundling could use +`.vite-temp` through those symlinks. That prevents a no-cache-write claim. +All 3,400 frozen Gate-A source files and critical module, lock and executable +digests remain unchanged. The final author configuration uses a separate scratch +cacheDir and the runner config loader. Shared caches were not cleaned up. + +The reproducible host is `/private/tmp/rc2171-oracle-challenge/chunk24-host`. +From its `packages/db` directory, the full native command is: + +```sh +TANSTACK_DB_ORACLE_STATISTICS=1 node ../../node_modules/vitest/vitest.mjs run --config ../../chunk24.config.mts --configLoader runner tests/query/virtual-row-fields-types-oracle.test.ts +``` + +The source/host/input/result hashes and exact reporter commands are preserved in +`/private/tmp/rc2171-oracle-challenge/chunk24-freeze.json`. The actual 24 generated +modules and 816 marker descriptions are retained alongside that manifest. +The prior rejected optional-source-root fixture and its TS2769 diagnostics remain +in the R2 artifacts. A legal LEFT-joined callback receives undefined at absence; +it now returns an ordinary record containing that optional selected value, +rather than attempting to publish undefined as a root row. Those admission +errors are separate from the intended native type-law REDs above. + +ORC-001–006: authority, independent declared type signatures, literate layers, +bounded grammar and admission, actual unannotated markers and sensitive controls +are present. Each source/API/receiver dimension supplies a named distinction; +removing union sources loses the 12 spread counterexamples, and removing nested +reserved fields loses the recursive ordinary-name boundary. ORC-007 and ORC-008 +are inapplicable to this exhaustive stateless grammar. ORC-009 uses Collection, +QueryRef, facade, input and publication as defined in the glossary; context-family +is only a source-grammar grouping. ORC-010 preserves admission diagnostics before +law comparisons and does not allocate runtime resources; the original runtime +construction cleanup remains intact. ORC-011 adds no speculative second semantic +model; the equal-type experiment records the concrete inference limitation. +ORC-012 uses this versioned record and exact frozen artifacts. ORC-013 pairs +ordinary pre-publication input with genuine source and published-facade metadata, +and checks nested ordinary names and rest removal. ORC-014 makes no provider or +runtime handoff claim. ORC-015 measures every finite receiving state, including +partial/unrelated-seed rejection. ORC-016 keeps all native inputs and hostile +observations on the shared generated signature judgment; supplemental concrete +helper tests remain in their ordinary files. + +### Chunk 24 R2 — retain the actual functional callback boundary + +Independent review found a recorder gap in the R1 syntax guard. Moving the whole +callbackInput marker stanza into an unannotated map helper could reconstruct a +clean ordinary record from the real input. The real fn.select input still had +unwanted metadata, but the displaced source admitted without diagnostics and +all four type judgments passed. The syntax guard also accepted it. The retained +reviewer script and result are `chunk24-independent-bindings.mjs` and +`chunk24-independent-bindings-result.json` in the scratch evidence directory. + +The new shared-grammar calibration first failed against the unchanged R1 guard: +`chunk24-r2-helper-red.json` records its single intended assertion RED. Source +admission and all type judgments succeeded first; this is an observation-boundary +failure rather than a type-law or setup failure. The guard now requires the +containing arrow to be the actual fn.select argument. A rest declaration must +also be direct in that same block. The calibration then passes, retaining its +zero-diagnostic, all-type-judgments-pass premise. + +The 816 native pairs, 4,608 marker laws and 6,240 finite reach states are +unchanged. R1 sources were reconstructed and verified byte-for-byte against +`chunk24-freeze.json`; `chunk24-r1-source/` preserves them. The RED calibration +sources are separately retained in `chunk24-r2-prerepair-source/`. R1 review and +author evidence remain unchanged. R2 adds one checker calibration, for 43 total +controls; it changes no production code or source-type expectation. The native +672 metadata-leak and 12 union-spread failures remain open. R2 hashes and final +comparison results are in `chunk24-r2-freeze.json`; independent re-review is +pending. + +R2 author result: 883 unique tests, 175 pass and the same 708 intended native +REDs. All 882 prior title/status pairs remain unchanged. The 43 checker/AST +controls pass, all 6,240 receiving states still count one, and strict authored +TypeScript plus targeted lint pass. A passing positive-only selection with an +unrelated seed still fails the missing finite reach obligation. The R2 freeze +SHA256 is `3ccfabf4cd7a94f7e39a2438740d9c0c01fcbde1aebceeb945d85e4b97c285bf`. + +### Chunk 24 R2 independent acceptance + +Reviewer `/root/handler_types_review` independently accepted the bounded R2 +scope. Its full native result has the same 883 unique titles and statuses: +175 pass, 708 intended native REDs. All 6,240 reach states count one. A +controls-only run passes 43 controls with 840 native tests skipped and no +unentered-matrix reach failure. A valid unrelated-seed positive-only selection +passes one native history, skips 882 tests and fails the 6,234 missing-state +reach obligation. An earlier malformed replay setup remains recorded without +evidence credit. Strict checking of the three authored files passes. + +The reviewer's exact helper relocation retains zero diagnostics and four true +type judgments, but the repaired observation guard rejects its map callback. +Ten additional independent compiler controls reject one wrong facet each, +covering required selected and nested fields, nested null, call/construct +capabilities, union fields, readonly modifiers, array any and Date extras. +There is no remaining supported oracle gap within this bounded static slice. +Production defects remain RED; this acceptance authorizes no runtime ownership, +opaque-allocation provenance, built declaration, npm or provider/framework claim. + +The final reviewer report is +`/private/tmp/rc2171-oracle-challenge/chunk24-independent-review.md`, SHA256 +`8b79a8ab77f9cc5c5722b0958909cb166fcdc91288524e073310ad5b008d1ab5`. +The report's artifact index is `chunk24-independent-review-manifest.json` in +that directory. It indexes independent native, controls, valid partial, reach, +strict-type, helper-boundary and facet-control results. R1's accepted helper +false green and R2's rejection remain separately reproducible. + +The original-worktree `pnpm lint:changed` closeout attempt invoked pnpm's +freshness install and aborted before module removal with +`ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY`. No install completed. The exact +underlying `node scripts/agent-lint-feedback.mjs` ran directly instead; scoped +and whole-branch direct oracle lint pass. No pnpm command ran in the borrowed-tool +hosts. No dependency/version workaround or generated API documentation changed. +## Chunk 25: native Solid publication recording and CI import census + +Independently accepted at production HEAD +`5fa6eca05530bb9e1f4748b7c00ae60976da2808`, freeze SHA256 +`96059399e2365b02b83e2cac2e73fb792f2215c4b31df9c10f412243ee66620f`. +The four owned paths change only test machinery: Solid's conformance driver, +the shared suite and its small publication checker, and the ordinary operator +import census. No library, release-tool, dependency or generated-doc edit ran. + +**Law and authority.** Solid's public result is a callable Accessor. The shared +conformance contract and publication-log cut require settled selected rows to +match the independent source/history model. Empty loading turns remain legal; +earlier turns may not contain the previous source/group's IDs. The old recorder +read removed `.data` fields, while the old subset check silently accepted the +resulting undefined. This repairs the observation, without changing scheduling +or query-identity policy. + +**RED and GREEN.** Before fixing either recorder site, the stronger shared +comparison gives 60 passes and two intended Solid assertion failures. Full +Solid source typechecking also rejects `.data` and broad fixture indexing. +The ordinary import census gives one obsolete-import failure and 18 passes. +After invoking both Accessors, retaining literal fixture keys and removing the +obsolete census entry, native Solid has 62 passes with typechecking enabled; +whole-package tsc has zero diagnostics. Census/operator cases give 19 passes. +The affected actual React, Preact, Vue, Svelte and Angular selections give +10, 5, 5, 5 and 3 passes respectively. Unselected scenarios receive no credit. + +**Reach and hostile checks.** Every one of seven receiving registrations keeps +all 32 source-ID histories; React registers twice. Reactive drivers each reach +32 initial and nine swapped nonempty comparisons. Pooled counters deduplicate +within their one history, so independent per-target observations additionally +verify both unconditional cuts in every registration: b has exact final +data/state IDs [2], and a has [1,3]. Eight checker controls preserve allowed +empty/loading prefixes and empty modeled results, while rejecting omitted, +undefined, dropped and stale final publications and stale prefixes. + +Independent in-memory effect-reader and first-turn-reader mutants fail at the +shared selected-row comparison. The effect mutant fails both stories; the +first-turn mutant fails source-ID swapping while pooled publication is repaired +within the permitted turn. That pooled survival is not counted as a kill. +Returned mounts dispose exactly once under both failures: 3/3 and 8/8. The +original assertion remains primary through ScenarioLifetime. An early typed +scratch observer failed transformation before tests and receives no kill credit. + +Root verified all 233 source, 15 config and 50 evidence hashes before this +append. The independent reviewer verified the same frozen inputs and all +3,400 Gate A source hashes plus critical tool hashes/mtimes. Exact pre-fix +Solid bytes are retained; one formatting-only blank-line difference in a later +reconstruction was reconciled against that original hash. Neither that +reconstruction nor root's hash check substitutes for independent execution. + +ORC-001–006 retain public/shared authority, independent source rows, literate +layers, unchanged grammar, real receiving paths and calibrated comparisons. +ORC-007–008 are inapplicable to this finite grammar with unchanged model state. +ORC-009 preserves canonical binding and publication vocabulary. ORC-010 has +measured primary-error and disposal fidelity. ORC-011 has no new named shared +semantic fault requiring another model. This record supplies ORC-012. +ORC-013 distinguishes missing/stale output from legal loading/model-empty +neighbors. ORC-014 has actual framework receiving hosts with controlled source +sync, without a backend/device claim. ORC-015 measures each registration and +both pooled targets without lowering budgets. ORC-016 keeps the declared +checker separate from ordinary driver controls. + +Evidence under `/private/tmp/rc2171-oracle-challenge/`: +`chunk25-author-evidence.md`, `chunk25-independent-review.md`, +`chunk25-independent-evidence.json`, `chunk25-independent-final-integrity.json` +and `chunk25-root-validation.json`. Independent report SHA256: +`1400b016e492c5e65aa84f1622292df76a98bade26386d52e07b98dc03a5dd20`. +The claim remains bounded to the existing list-query cuts: no arbitrary +scheduler interleavings, within-turn transients, findOne, complete status or +keyed-state traces, backend/provider behavior or native-device scheduling. + + +## Chunk 26: generated error catalog observation and command ownership + +Reviewed merged HEAD: `5fa6eca05530bb9e1f4748b7c00ae60976da2808` plus the four frozen test/support paths in `chunk26-r3-freeze.json`. Reviewer: `/root/facade_metadata_review`. This is an independent review under `.agents/skills/oracle-review/SKILL.md`; the rubric, glossary, coverage-map Production error messages owner, source oracle, fixture, CLI controls, command controls, actual old generator, and erasure boundary were read. No production/tooling repairs, generated-page edits, installations, commits, or pushes were performed by this reviewer. + +### Verdict + +ACCEPT the R3 oracle/control chunk as a bounded pre-repair gate. The named class-link comparison gap is repaired. No further finding remains inside that named gap. The old CLI and root commands still produce twelve intended assertion failures; this verdict does not claim product GREEN or authorize broader closure. Root controls repair authorization and the versioned coverage/review record. + +### Independent execution and provenance + +The reviewer checked670 recorded source/host/config/protected/artifact hashes against the R3 freeze: zero mismatches. The four active and host test/support paths match. All154 DB/IVM source paths match their captured bytes, both old root command bodies remain unchanged, and the protected generator, erasure implementation, full catalogs, sample arguments, census, formatter, and tracked generated errors page remain unchanged. An additional fresh check of the immutable GateA source manifest found3400 matching records and zero mismatches. GateA supplies installed test tools only; no clean-install, packed-consumer, or fresh-build credit is claimed. + +From `/private/tmp/rc2171-oracle-challenge/chunk26-host/packages/db`, the reviewer independently ran: + +```sh +TANSTACK_DB_ORACLE_STATISTICS=1 node ../../node_modules/vitest/vitest.mjs run --config ../../chunk26-db.config.mts --configLoader runner --coverage.enabled=false tests/production-error-messages-oracle.test.ts tests/generate-error-docs.test.ts tests/error-docs-commands.test.ts --reporter=default --reporter=json --outputFile=/private/tmp/rc2171-oracle-challenge/chunk26-r3-independent-native.json +``` + +Result:39tests,27PASS,12intended assertionRED. Every status matches the author's R3 run by full test name; all prior38statuses are unchanged. The17 existing runtime/class/source-census checks pass. Four shared-checker calibrations pass. CLI ordinary controls retain6PASS/9RED; controlled root invocation controls retain2RED. The actual copied old CLI exits0 and emits empty stdout, which reaches the catalog comparison and fails the missing DuplicateDbInstanceError heading. Declared reach is cli-stdout-received1/catalog-compared0; completed-comparison0 follows from the intended assertion mismatch, rather than a fixture setup failure. The test process exits1 for these assertions. Vite also prints missing Expo tsconfig warnings for unrelated example directories; all selected39tests execute, so those warnings are not credited as RED evidence. + +### Concrete comparison challenge and correction + +The class-link law requires an HTML ID matching each coded production-message URL. The R2 checker parsed raw Markdown as HTML. In a separate disposable copy, the reviewer moved only the first required class anchor, error-2, into the actual generator's canonical fenced text-message form. The unchanged shared checker accepted that wrong observation: the negative calibration failed with `expected [Function] to throw an error`. No frozen source was changed. The installed marked17 renderer independently renders that fenced anchor as escaped code with zero HTML IDs, whereas raw DOM parsing reports error-2. This is an enforcement/model-observation defect under the accepted link law, rather than a proposal for a new renderer contract. + +The author retained the failing old-checker calibration before repairing the oracle. R3 excludes canonical generator text fences before class-ID extraction, retains HTML parsing for element/quote/attribute-spacing freedom, and states the bounded extraction limit beside the checker. The reviewer executed the repaired negative calibration through the same checker: PASS. The existing class-anchor calibration also executes the alternate span/single-quoted/spaced-ID positive observation and then rejects the deleted ID: PASS. Neither calibration derives expected catalog facts from renderer output. + +The original missing-class-anchor acceptance is separately preserved in the author's earlier old-checker negative calibration. Both oracle repairs change only comparison/calibration logic, with original runtime/source/census laws and old tooling unchanged. + +### Rubric outcomes + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | PASS within scope. Frozen catalogs and established coded-link contract supply catalog authority; user decision supplies generated-doc ownership. Runtime, bundle and host limits remain distinct. | +| ORC-002 | PASS. Expected codes, names, files and targets come from frozen facts; production generator supplies observations only. The synthetic calibration page supplies minimal facts, not copied rendering machinery. | +| ORC-003 | PASS. Opening prose and adjacent checker/fixture prose distinguish authority, full-catalog input, copied production CLI, stdout checkpoint, comparison and limits. | +| ORC-004 | Not newly triggered: full frozen catalog census is fixed exhaustive input, not a new generated-history claim. Ordinary CLI/control tables remain bounded examples. | +| ORC-005 | PASS as pre-repair evidence. Actual copied old source/full catalogs reach stdout and the real comparison. Empty stdout is representable and rejected. No completed-comparison/product-GREEN claim is made. | +| ORC-006 | PASS. Missing heading, missing class ID, missing site anchor and fenced-only class ID are rejected by the shared comparison; the old fenced-ID checker demonstrably accepted the wrong observation. Native old-tooling failures occur at assertions. | +| ORC-007 | No new important generated property: no new random/replay obligation for this fixed catalog census. Existing unchanged campaigns are not credited as rerun here. | +| ORC-008 | Not triggered: no stateful reference-model change. | +| ORC-009 | PASS. No new model-only subsystem state or lifecycle vocabulary; catalog/stdout/checkpoint retain their direct meanings. | +| ORC-010 | PASS by inspection and native execution. Disposable synchronous fixture preserves primary body failure, including thrown undefined, retains cleanup diagnostics through AggregateError, and releases temporary directories. | +| ORC-011 | PASS for the concrete named shared-semantics hypothesis. Actual installed Markdown rendering distinguishes code text from HTML IDs; R3 adds the same wrong-observation calibration. No general Markdown-equivalence claim is made. | +| ORC-012 | This exact-head/freeze report records all outcomes, execution and limits; root must retain the report/hash in its versioned review record. This is an oracle gate, not bug-class/product closure. | +| ORC-013 | PASS within bounded controls. Valid HTML-ID variants versus absent/fenced-only ID distinguish target semantics; missing/stale pages and invalid-argument controls distinguish the named CLI boundaries. | +| ORC-014 | PASS with explicit limits. Real Node runs the copied source CLI. Controlled erasure/Typedoc recipients prove root-command wiring only; real bundle erasure, actual Typedoc and release Actions remain later receiving gates. | +| ORC-015 | PASS for reported reach. The actual stdout boundary is reached1; completed comparison0 is honestly recorded during intended RED. No generated-history reach claim is added. | +| ORC-016 | PASS. New oracle tests are shared-checker calibrations and the production receiver. Ordinary CLI expectations and root invocation examples stay in ordinary test files. | + +### Limits and next evidence + +This acceptance is limited to the actual frozen catalogs, current generator source and canonical fenced-message class-ID distinction. It does not establish arbitrary Markdown layouts, full rendered-heading validation, duplicate-heading multiplicity, every prose/template byte, all possible CLI argument lists, asynchronous fixture bodies, pnpm lifecycle execution, installed-package consumers, browser host behavior, or CI/Actions execution. Existing runtime/source/census checks remain supplemental to their declared frozen-input scope. The erasure script itself remains unchanged and must later run against freshly admitted ESM/CommonJS dist while generated docs are stale. The real docs-generation/Typedoc path must later run in an isolated generation workspace, followed by generator --check. Later tooling repair still needs native GREEN and restored-old-source hostile-mutant evidence before product closure. The user prohibition on committing generated docs remains in force. + +### Evidence hashes + +- `chunk26-r3-freeze.json`: `2baf81b3904bf1f8005dee31aef2b0c7b2d46bb2b758825ee59ecffaa3fbce7f` +- `chunk26-r3-independent-native.json`: `e53c6525a97ffb73f00f7881fb9abff5ea56160a35ebd970ee616ba6168924c0` +- `chunk26-r3-independent-native.log`: `8033eae6254b1bb62f9f333de2bee63cbf6b842d55a0f4978f59728b6304d805` +- `chunk26-independent-fenced-id.json`: `e19d6c3b0f5117f6a6740d5e1ad17930dd64670a41649a0693beb541743e0774` +- `chunk26-independent-fenced-id.log`: `8a86ccb9dd59201e722036ee701b9fe69d072588db4eeb158504fed6d64d48fa` +- `chunk26-independent-fenced-renderer.json`: `f85c7153269d4c2bfd08641d56ea40c7150143f84ad07c6fe7df386ca3c7ffcf` + +Root verified 658 frozen scope/host/config/artifact hashes and 12 protected files before repair. No mismatches. The 26-chunk oracle phase is independently accepted; accepted REDs remain release repair gates or explicitly recorded follow-up scope. No executable production repair preceded this checkpoint. diff --git a/docs/contributing/oracle-reviews/2026-10-28-pr-2171-review-coverage-matrix.md b/docs/contributing/oracle-reviews/2026-10-28-pr-2171-review-coverage-matrix.md new file mode 100644 index 0000000000..4d39958bd5 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-28-pr-2171-review-coverage-matrix.md @@ -0,0 +1,296 @@ +# PR #2171: review-to-check coverage matrix + +Audit head: `858b174a65f160d13d01f0324c7481cc8e4eba3a`. +This is a self-review of the proposed checks, with temporary executable probes. +No production repair or permanent oracle extension was made in this audit. + +The answer to “would the original 15 catch everything?” is **no**. The +[expanded plan](2026-10-28-pr-2171-oracle-challenge.md) has 26 checks. This +matrix accounts for 53 findings and four evidence caveats. Accounting is not +proof that all checks intercept their targets. Known current failures keep +those laws open, even though their existing narrower oracles pass. + +## Laws, experiments, and remaining enforcement + +No law change needs a new maintainer decision. Independent cached callers +reject conflicting local row options. Mounted hooks retain inline options +until identity changes. Metadata follows authoring shape, with the last present +structured spread winning. New functional objects receive output metadata; +direct source returns retain source metadata. Inline values keep authored +shape. Query and Electric ignore legacy runtime returns, and their types reject +them. Query does no automatic refetch. The maintainer explicitly selected a +stable Query minor and coordinated RC DB peers for the accidentally stable +Query/Offline packages. + +| Law and authority | Existing evidence and missing boundary | Experiment on current unchanged head | Inference and enforcement destination | +| ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Independent same-identity callers reject conflicting getKey/schema; explicit maintainer decision | Existing admission model/drivers check preload, observer preload, subscription, and client render. Cached SSR before subscription is missing. | Core server read: three conflict assertion failures, three same-option controls pass. Actual React server render: the same three failures/three controls. | G09 is confirmed-open. O16 must extend the shared model's receiving paths before production repair. A subscription-only check is insufficient. | +| A reusable local option claim survives transport replacement; same decision, hydration transports no function references | Source claims have hydration histories, but local row-option claims do not receive that lifecycle. | Newer hydration replaces a one-row preloaded result; three conflicting declarations are accepted, three compatible controls pass. | G10 is confirmed-open. O04 needs claim lifecycle histories and post-replacement admission, with local claims preserved independently of transport. | +| The guide's successful write example confirms its row before optimistic settlement; explicit-confirmation migration contract | Query's generic handler owner checks confirmation, but does not execute this fenced example. Typechecking cannot detect its omitted confirmation. | Extracted successful POST handler writes one remote row; Collection has zero rows after `when('settled')`. | G01 is confirmed-open. O21 must execute the actual guidance through O17's law. No production adapter failure is alleged: its reversion follows the approved behavior. | +| Rejected cache admission cannot corrupt the first claim; same cache contract | Current grammar changes both options together and ends at first admission. | Previous pass: two wrong guards survive all 144 cases; independent-option and repeated-admission comparisons reject them with 12 and 24 failures. | O01/O02 remain unimplemented test gaps. Current correct behavior passes these distinguishing probes; that does not supply SSR/hydration coverage. | + +The probe inputs, checkpoint construction, commands, setup failure, and logs +are described in the expanded plan's follow-up experiment section. These are +RED witnesses, with compatible controls. They are not repair GREEN results. + +## Source inventory and evidence notation + +- **A01–A15:** first pasted review, preserved in the task attachment `Pasted text.txt`. Its reviewer read the worktree diff and Changesets 6.0.9, queried published manifests, and ran no tests. Original line references are retained below; that review supplied no commit SHA. +- **B01–B10:** next supplied review, evaluated from `7c53f271a` in the [first versioned evaluation](2026-10-28-pr-2171-review.md). B10 has two subclaims, both retained here. +- **C01–C15:** latest supplied review, targeting `0a60ad85b`; evaluated from `66454803b` with repairs in `68228aaf3`. The [second versioned evaluation](2026-10-28-pr-2171-second-review.md) records original claims, repros, mutants, and limits. C01 includes core and React paths. +- **G01–G11:** seven inline comments, three outside-diff findings, and one additional prose suggestion in GitHub's two reviews. First review covers through `66454803b`; second through the audit head. Stable links identify inline comments. Outside-diff findings are in the corresponding review body. +- **W01–W02:** the two warnings in the bot's [mutable walkthrough](https://github.com/TanStack/db/pull/2171#issuecomment-6196397964), inspected at the audit head. Its merge-risk summary repeats G05/G08/G09/G10; it is not another independent finding. + +`O01`–`O26` refer to the numbered checks in the expanded plan. Their owner, +independent judgment, legal histories, cut, and distinguishing experiment are +specified there. They are proposals except where the evidence explicitly says +an existing owner already executes them. + +**Historical** means the linked evaluation records the experiment at its own +head; this audit did not replay every original fault. **Source** means direct +source inspection, not an executed behavior/build failure. **Current RED** +means an intended assertion failed at this audit head. **Proposed** means the +broader check has not run. `already-fixed` and `stale` describe the reported +trace; they do not close the broader unimplemented improvements. + +## A: first supplied review + +The source calls A01–A04 high severity. A05–A08 are release follow-ons, +A09–A11 correctness findings, and A12–A15 design/cleanup/docs findings. + +| ID | Original claim and proposed correction | Technical disposition/evidence | Concrete check and distinguishing cut | Remaining work/durable destination | +| --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| A01 | `compiler/index.ts:2062`: joined source spread uses FROM metadata; right-join missing-left rows fall back to output metadata. Honor selected source. | `already-fixed` for the reported trace; historical metadata attribution repairs. | O06/O08/O10/O11: right-side source with pending write, matched/unmatched join, full four-field comparison at publication. | Complete multi-row/join-transition/facade receiving grammar; metadata owner. | +| A02 | `useLiveQuery.ts:674`: getKey/schema in derived identity causes documented config to throw without queryKey; restore supported initial render and useful diagnostics. | `already-fixed`; historical real React derived-identity and retention witnesses. | O05/O19/O25/O26: initial config render without queryKey succeeds, actual identity replacement applies options, preparation errors remain coded. | Other applicable framework receivers and exact-head execution. O16 is distinct SSR admission, not evidence for this initial browser path. | +| A03 | `version-packages.mjs:58`: first RC ignores stable adapters, leaving exact old DB pins and duplicate DB copies. Republish compatible adapters. | `stale` exact-pin mechanism; stable adapters now declare RC peers. Original reviewer supplied source evidence only. | O13/O22: actual first-RC inventory, pack both adapters, resolve one DB in consumer and exercise required APIs. | Real package/consumer receiving fixture; source-only manifest strings are insufficient. | +| A04 | `version-packages.mjs:145`: each RC patch bumps stable adapters on latest and pins an exact RC DB; fixture omits those dependencies. | `stale` exact dependency pins; stable 1.x RC peers are now deliberate. | O13/O22: repeated patch/minor releases, packed manifests/dist-tags, no invented adapter bump, supported consumer install. | Generated release histories and actual registry-command fixture. Preserve stable-tag policy separately. | +| A05 | `version-packages.mjs:208`: RC exit drops planned dependent bumps, leaving stable adapter pinned to RC. Preserve dependency updates at exit. | `stale` old exact pins; historical release-script repairs/fixed fixtures. | O13: exit after candidate/patch work; compare every packed dependency and required bump with independent plan. | Generated dependency graph, including stable dependents; release owner. | +| A06 | `version-packages.mjs:194`: exit reconsumes released changesets; consumed minor plus pending patch yields wrong version and duplicate changelog. | `already-fixed` reported release path; current baseline release fixtures pass. Original-fault replay not run here. | O13: minor consumed in RC, pending patch, exit, repeated no-op; version and changelog entries exactly once. | Generated consumption ledger and changelog comparison. | +| A07 | `version-packages.mjs:110`: pending mixed changeset aborts; stable-only changeset marked consumed without release. | `already-fixed` reported path; existing release fixtures. Broader sequence unrun. | O13: enter RC with mixed and stable-only work; compare releases and consumption independently. | Generalize fixed workspaces into generated release histories. | +| A08 | `version-packages.mjs:87`: failure leaves rc.0 rewrites/pre.json; rerun skips setup and bumps wrong packages. Restore files on failure. | `already-fixed` reported path; release failure/retry fixture. | O13: inject failure at each mutation boundary, compare workspace restoration, retry and publish once. | Extend failure-stage grammar and registry-command receiving path. | +| A09 | `useLiveQuery.ts:248`, infinite hook: failed hash after descriptor deferral strands later direct reader. Resume all deferred starts. | `already-fixed`; existing source-ID owner has rejection/release and multiple throwing-start witnesses. | O19: rejected live/Suspense/infinite preparation, then direct shared descriptor preload reaches ready/row. | Inventory partial-construction stages; preserve real-host failure/retry receiving evidence. | +| A10 | `electric.ts:1210`: ignoring legacy `{ txid }` stops waiting and can flicker; reviewer favors loud rejection or old waiting. | `accepted-design` for ignored runtime returns, explicitly chosen later. Explicit acknowledgement remains required. | O17/O21: handler awaits actual acknowledgement, pending row persists until confirmation; no-ack control follows settlement law. | Adapter histories plus actual migration snippets. Never restore post-write rejection to satisfy this obsolete recommendation. | +| A11 | `query.ts:4368`: retained 1.x auto-refetch contract warns removal in v1. Correct obsolete warning. | `stale`; contract intentionally changed and automatic-refetch path removed. | O21: changed API/warning/migration consistency check, successful-write behavior. | Authoritative explicit-refetch guide and removed-symbol check. | +| A12 | `compiler/index.ts:409`: metadata protection only at root flag; Effects/include Collections allow reserved-name override. Enforce promised boundaries consistently. | `stale` as a blanket rule: Collection-row traces were repaired; inline values keep authored shape. The current Effect output contract needs separate inspection before imposing Collection metadata. | O10/O11/O12: source/projection collision through root, Effect, facade, callback and inline control at public boundary. | Receiving grammar must preserve the distinct inline/Effect/Collection contracts; no blanket sanitizer. | +| A13 | `useLiveSuspenseQuery.ts:288`: always-string hash makes SSR throw, truthy checks and optional type dead. Remove unreachable code. | `stale`; source cleanup recorded. | O23: narrowed hash type plus actual caller/branch inventory. | Static check; runtime green cannot prove absence of dead code. | +| A14 | `compiler/index.ts:2068`: attachVirtualPropsToSelected creates per-row copy closure even without copy. Hoist/remove allocation. | `stale` reported implementation; source allocation concern retained, no timing claim. | O24: compile once/process N rows; count closure creation versus needed copies. | Deterministic work/AST placement probe, semantics owner unchanged. | +| A15 | `live-queries.md:37` misstates projection collectionId; migration names unexported enrichRowWithVirtualProps. Correct attribution and exported guidance. | `already-fixed` reported docs; historical evaluation/source. | O21/O25: compile actual snippet via public export; compare documented source/output cases. | Docs exports and behavior receivers. Keep internal helper out of public migration instructions. | + +## B: next supplied review + +Original paths/lines refer to its reviewed tree. B01/B03–B08 are behavioral or +compatibility findings; B09/B10 include cleanup and production-error concerns. + +| ID | Original claim and proposed correction | Technical disposition/evidence | Concrete check and distinguishing cut | Remaining work/durable destination | +| --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | +| B01 | `OfflineExecutor.ts:551`: stable adapter calls RC-only completeWithoutCommit while accepting DB ^0.15; restored work can remain held. Narrow peer or avoid absent API. | `already-fixed`; historical real DB 0.15.2 capability evidence and manifest change. | O13/O18/O22: packed consumer restore under each supported peer, exact outbox/caller settlement. | Workspace dependency and packed consumer checks are separate; G05 remains open. | +| B02 | `first-rc.md:17`: removing Query automatic refetch/return type in a stable minor/latest breaks ^1.4 users; use major. | `accepted-design`; maintainer explicitly changed major request to minor. | O13/O17/O21: verify requested minor/tag and explicit migration cost; prove new handler behavior. | Preserve deliberate compatibility warning; a test must not override this release decision. | +| B03 | `live-query-options.ts:184`: inline key/schema reference rebuilds each render, possibly loops. Retain options by query identity. | `already-fixed`; historical wrong identity mutant fails six React derived cases. | O05/O19: repeated native hook render, source publication, actual identity change, render budget. | Other identity-comparing host witnesses, without imposing React semantics on reactive bindings. | +| B04 | `electric.ts:1291`: rejectReturnedHandlerValue throws uncoded Error after server commit, falsely fails caller and invites duplicate retry. | `already-fixed`; historical original wrapper fails three handler-outcome histories. | O17/O25: server success plus legacy return must fulfill with confirmed row; genuine handler rejection retains exact error. | Keep 12-history owner and error-artifact checks in required validation. | +| B05 | `compiler/index.ts:2119`: functional spread/collision guesses mainSource instead of selected joined source. | `already-fixed` under approved authorship rule; historical metadata grammar rejects source guessing. | O06/O08/O09/O12: direct source versus fresh functional object from either joined source, complete rows/four fields. | New-object ownership is output; do not infer provenance from value equality. | +| B06 | `compiler/index.ts:2129`: authored `$collectionId` changes ordinary projection ownership contrary to docs. | `already-fixed`; historical collision grammar. | O06/O10/O11/O12: toggle one reserved field without changing ordinary fields; same output attribution at every boundary. | Callback/downstream/facade receiving and full-result comparisons. | +| B07 | `first-rc.md:6`: Convex/Preact absent on branch, version command throws; merged packages still need API migration. Merge main and include them. | `already-fixed` missing-package trace after merge; historical actual-inventory fixture. | O13/O14/O22/O26: actual non-private inventory, run version/build/types and receiving contracts for merged packages. | Exact-head clean workspace checks; an older package list is insufficient. | +| B08 | `query.ts:4491`: Query ignores legacy return while Electric throws; stale UI versus caller failure. Align adapters. | `already-fixed`; approved ignore-at-runtime/reject-in-types policy. | O14/O17: same CRUD/outcome/result histories through both adapters and public type input compiler. | Keep explicit confirmation controls and generated type admission. | +| B09 | `query.ts:4491`: three identical pass-through wrappers and four repeated QueryCollectionUtils types. Consolidate wrapper/type alias. | `already-fixed` reported duplication; historical source cleanup. | O23/O24 with O17 semantics: source inventory and shipped bytes, unchanged mutation outcomes. | Static/size evidence, not an invented behavioral oracle. | +| B10 | `useLiveInfiniteQuery.ts:282`: React always false leaves shared equality branch dead; `useLiveQuery.ts:249` uncoded Error ships full text. | `stale` older React shared-policy concern; other framework callers prohibit blanket deletion. `already-fixed` coded identity-error subclaim. | O23 checks all framework callers; O25 executes diagnostic census and production artifact erasure. | Preserve both subclaims. C14 later identified a genuinely unused mode; same claim cannot be inferred from React alone. | + +## C: latest supplied review + +The reviewer reported executed repros for C01–C05 and source-only packaging +claims C06–C08. The second evaluation distinguishes observed results from +specific TypeError/ERESOLVE predictions. + +| ID | Original claim and proposed correction | Technical disposition/evidence | Concrete check and distinguishing cut | Remaining work/durable destination | +| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | +| C01 | `live-query-options.ts:270`, core test 411 and React 3569: own tests fail after retention change. Fix assertions without restoring render churn. | `already-fixed`; historical two core-file failures and real React failure, then GREEN. Core option tests pass again in this audit. | O05/O26: execute both changed files on exact head, preserve real identity replacement controls. | Test selection and build fingerprints; no new runtime model was needed to detect red tests. | +| C02 | `index-suggestion.test.ts:225`: JS `!row.$hasPendingWrites` is false, so expected warning never occurs. Use query expression directly. | `already-fixed`; historical failing diagnostic, current index tests pass. | O20/O26: true/false pending source variation and virtual-field diagnostic at actual query construction. | Actual UI receiver and proxy-expression check. | +| C03 | `compiler/index.ts:1227`: flat union alias loses pending/origin metadata. | `already-fixed`; historical final metadata grammar against old compiler has 220 failures/189 controls. | O06/O07/O09/O10: both union branches nonempty, full rows/four fields at pending/settled/publication cuts. | Existing fixtures cover one populated branch only; simultaneous contributions remain a gap. | +| C04 | `compiler/index.ts:2117`: only outer root blocks selected reserved names; subquery spoofs metadata. | `already-fixed` recursive publication trace; same historical grammar. | O06/O10/O11/O12: colliding QueryRef projection, downstream callback reads ordinary scalar before publication. | Detect input-time spoofing, not just final reserved-field sanitization. | +| C05 | `compiler/index.ts:2079`: later unmatched spread deletes earlier source metadata. | `already-fixed`; historical first-present mutant fails four matched controls. | O08/O09/O10: unmatched→matched→unmatched, both spread orders, callback observations. | Current match grammar is static; transition/rollback coverage remains proposed. | +| C06 | Query peer ^0.15 accepts DB lacking fail/reportError/reportHealthy. Narrow peer to APIs actually used. | `already-fixed`; historical old DB boundary has undefined methods and stuck preload, not an executed uncaught TypeError. | O13/O22: execute health failure/recovery in installed supported consumer; reject unsupported combinations. | Manifest string tests alone do not establish capabilities. | +| C07 | Query breaking minor/latest should be major. | `duplicate` of B02; original compatibility concern preserved, approved minor choice remains. | O13/O17/O21: requested version/tag, confirmation and migration behavior. | No major bump without a new maintainer decision. | +| C08 | Offline stable 1.2/latest with RC-only peer causes old DB peer-resolution failures. | `accepted-design`; coordinated upgrade deliberately required. Specific ERESOLVE wording not run. | O13/O21/O22: actual consumer resolution and documented upgrade/retain-old-adapter alternatives. | Stable tag does not imply 0.15 support. Keep resolver limits honest. | +| C09 | Same hash plus changed key/schema borrows other caller's Collection. Reject independent conflicting callers. | `already-fixed` preload/client-render traces; historical guard-removal RED core 36/React 6. Current G09/G10 expose additional paths. | O01/O02/O03/O04/O05/O16: every admission path, independent declarations, replacement and post-rejection reuse. | Cache owner remains open for the demonstrated SSR/hydration counterexamples. | +| C10 | RN ListsScreen 118 uses removed `$synced`, JS negation and reserved name; Saving never shows. Select ordinary pending field and consume it. | `already-fixed` source wiring plus historical core pending-field observation; native component execution unproven. | O06/O10/O20/O21: actual example projection/component across held/settled write. | Real RN receiving witness; do not credit a copied core query as a device run. | +| C11 | `completeWithoutCommit` can complete failed work; internal API is cross-package and publicly documented. Guard state, remove public instructions for helper. | `already-fixed`; historical missing-guard mutant fails four state histories. Current outcomes suite passes. | O18/O21: repeated attempts from all four states, public rows/outcome unchanged on refusal; guide checks. | Packaged Offline restore still needs its capability receiver. | +| C12 | `local-storage-collection.md:118` retains removed isPersisted. Replace with when('settled'). | `already-fixed` targeted guide; G07 finds other active coverage references. | O21: whole authored-doc migration inventory with historical/before examples explicitly marked. | No generated-doc edits; do not constrain search to one changed guide. | +| C13 | `db/src/index.ts:54`: warnOnce/resetWarnings unused. Remove implementation, exports and obsolete tests/resets. | `already-fixed`; historical caller inventory and cleanup. | O23: production export/caller census, fixtures distinguished from actual API uses. | Static absence and size evidence. | +| C14 | `live-query-window-binding.ts:75`: dependencies recompile mode has no callers. Remove only unused mode. | `already-fixed`; historical source check, framework runtime neighbors pass. | O23/O26: all adapters' supplied modes and normal test graph. | Preserve active structural-equality policy and reactive callers. | +| C15 | `compiler/index.ts:1063`: alias array allocated for every functional row; hoist to compilation. | `already-fixed` source placement; no measured speedup claimed. | O24: alias discovery once per compile for varying rows; output owner unchanged. | Deterministic work probe and shipped-byte measurement. | + +## G: GitHub review comments + +Inline comments link to their stable source. G08 is outside the first review's +diff; G09/G10 are outside the second review's diff. G11 is the second review's +separate “additional/unverified” wording suggestion. The reviewers' current +line references below are checked against the audit head, not assumed from +old inline positions. + +| ID | Original claim and proposed correction | Technical disposition/evidence | Concrete check and distinguishing cut | Remaining work/durable destination | +| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | +| G01 | [Error-handling guide 705](https://github.com/TanStack/db/pull/2171#discussion_r4404724417): successful POST lacks refetch/write confirmation. | `confirmed-open`, current RED: server has row, Collection empty at settlement. | O17/O21: extract actual snippet and run successful/failing write histories with explicit-confirmation judgment. | Correct authored guide and retain a behavior receiver; compilation alone is insufficient. | +| G02 | [RN pending badge](https://github.com/TanStack/db/pull/2171#discussion_r4404724429): select source pending field under ordinary name. | `duplicate` of C10; source correction present. | O20/O21: actual component receives pending/settled source states. | Native execution still unproven; preserve this detail despite duplicate status. | +| G03 | [Index warning 225](https://github.com/TanStack/db/pull/2171#discussion_r4404724438): remove JS negation. | `duplicate` of C02; historical RED/GREEN. | O20/O26: run diagnostic and verify source-state-sensitive expression. | Retain proxy-expression regression. | +| G04 | [Core identity 408](https://github.com/TanStack/db/pull/2171#discussion_r4404724523): identity/hash assertions contradict retention. | `duplicate` of C01 core subclaim; current file passes. | O05/O26: affected test execution and actual identity-change control. | No reference-sensitive render identity restoration. | +| G05 | [Offline package 64](https://github.com/TanStack/db/pull/2171#discussion_r4404724536): RC peer cannot resolve local 0.15.2 in clean workspace, reported TS2307; add workspace devDependency. | `confirmed-open` source/config finding: RC peer, no DB workspace devDependency. Reported fresh-install build failure not replayed here. | O22: disposable frozen install/build before versioning; verify resolved local package and declaration imports. | Workspace dependency repair/build witness remains open. Existing local symlink is not counterevidence. | +| G06 | [React identity 3542](https://github.com/TanStack/db/pull/2171#discussion_r4404724550): expect retained Collection/first key. | `duplicate` of C01 React subclaim; historical real-host RED/GREEN. | O05/O26: real rerender plus identity replacement; execute changed test. | Preserve both assertions, not merely isReady. | +| G07 | [Coverage text 720](https://github.com/TanStack/db/pull/2171#discussion_r4405108728): two remaining active isPersisted references, actual source lines 547/1560 at head; replace with when('settled'). | `confirmed-open` source documentation residue; added audit prose shifts the second line. Original comment's displayed lines were stale. | O21: authored API-reference search with explicit historical exemptions. | Update active coverage prose; distinct from G11 wording. No production behavior repro needed. | +| G08 | `persisted-source-receipts.test.ts:957–960`: async onInsert returns `{}`, violating Promise; remove returned object. | `confirmed-open` source/type mismatch; existing return is present. Whole SQLite typecheck not executed here. | O14/O22: generated result admission plus actual package/test declaration build. | Migrate test handler and run its package typecheck; runtime-only runs cannot prove this. | +| G09 | `live-query-observer-client.ts:137`: cached seed early return skips admission; SSR never subscribes. Validate before returning snapshot. | `confirmed-open`, current RED core 3/React 3 with six compatible controls total. | O16: existing cache model through getServerSnapshot and actual renderToString. | Extend primary core/React receiving drivers before repair; post-rejection non-interference remains O02. | +| G10 | `client.ts:1095`: hydrateLiveQuery copies source claims but drops local rowOptions when replacing record. Carry local claim forward. | `confirmed-open`, current RED 3/compatible 3 after asserted newer replacement. | O04: preload claim, newer hydrate, conflicting/compatible preload, then claim retirement controls. | Extend claim lifecycle grammar before repair; do not serialize function references. | +| G11 | Additional prose comment, coverage 711: clarify “requires joined and single pending-source observations”; bot labels it unverified. | `confirmed-open` editorial suggestion, source only. This is not evidence of a broken reach counter. | O21: readable prose plus O26's per-path reach report; no fabricated runtime RED. | Preserve intent as “observations from joined and single pending sources”; keep separate from G07. | + +## W: bot walkthrough warnings + +| ID | Original claim and proposed correction | Technical disposition/evidence | Concrete check and distinguishing cut | Remaining work/durable destination | +| --- | ------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | +| W01 | Scope warning: metadata/cache laws and RC release automation exceed issue #1319; split work or link coding requirements. | `refuted` as unauthorized scope: user explicitly requested RC automation and approved metadata/cache rules in this task. Issue text alone omits that authority. | O21/O26: requirement→change inventory and PR-body review, including maintainer decisions. | Keep scope rationale visible; no product oracle should reject authorized scope. | +| W02 | Docstring score 17.14% versus 80%, 35 functions/69 files/8 unsupported; generate missing docstrings. | `refuted` as a repository requirement: no such threshold in AGENTS/config/workflows. Metric not independently recomputed; it names no missing contract. | O21/O23: public contract/literate oracle prose review proportional to law, source/config authority check. | Preserve useful documentation review; do not manufacture comments merely to satisfy an unconfigured vendor ratio. | + +## Four evidence caveats + +| ID | Original caveat | Required check and disposition | +| --- | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| N01 | A reviewer ran no tests; traced installed Changesets 6.0.9 and published exact pins. | O13/O22/O26: preserve source evidence as such. A source finding does not become an executed release failure. | +| N02 | C reviewer lacked Changesets dependencies, so release tests did not run. | O26: dependency/setup gate. Prior repair record and initial audit both ran11 fixtures successfully; broader registry/packed receiving work remains proposed. | +| N03 | C Query run loaded a stale second DB; no valid runtime finding resulted. | O22/O26: rebuild/fingerprint local DB and verify runtime resolution. Prior evaluation ran 978 Query runtime tests after repair; that result has its own head/scope. | +| N04 | C review branch moved 0a60ad85b→66454803b; about 40 later files, 44 by recorded diff, unreviewed; reviewer detached its clean old worktree. | O26: exact-head inventory and affected revalidation. Neither a clean reviewer tree nor unchanged cited files proves those 44 files reviewed. Leave other worktrees alone. | + +## Loss accounting, reviewer quality, and closeout + +The initial scratch ledger was created before probes. Final raw-source +reconciliation corrected G07's conflation with the distinct G11 suggestion and +added the two walkthrough warnings. No review finding was removed to make the +count fit. Multiple subclaims remain within B10/C01 and are explicitly mapped. +The four caveats are additional to the 53 finding IDs. + +Numbered-row accounting is **53 = 29 already-fixed + seven stale + three +accepted-design + five duplicate + seven confirmed-open + two refuted**. +B10 is counted once as stale for its older shared-policy claim; its distinct +coded-error subclaim is already fixed. C01 counts once but retains both core +and React paths. No row is deferred. No new product policy is adopted here; A12 requires contract inspection before extending assertions to Effects. +These totals classify reported traces, not the completion of all proposed +checks. In particular, the seven confirmed-open rows are G01, G05, G07–G11; +G07/G11 are editorial, G05/G08 have source evidence, and G01/G09/G10 have +executed behavioral failures. + +The first reviewer had useful source-based boundary analysis but no runtime +confirmation. Later reviewers supplied strong executable counterexamples and +honest setup/scope disclosures. The GitHub review identified two real cache +paths missed by the current driver. Policy recommendations still need the +maintainer decisions: a major Query bump, post-write rejection, and scope +splitting do not follow from those defects. These reviewers are useful for +defect discovery; their proposed fixes and coverage claims still need checking. + +Recommendation: **hire for defect discovery, with policy and evidence +calibration**. Their reproducible boundary failures are the strongest evidence. +An unconfigured metric or a scope judgment that omits maintainer instructions +does not establish a required repair. + +The expanded plan accounts for every identified finding in these sources. +It does **not** establish that its proposed checks have caught every original +implementation. Each behavioral row still needs its original failure or a +plausible mutant to fail at the named comparison before that claim is earned. +Static findings need source/build/declaration evidence instead. O16 and the +hydration part of O04 have current RED evidence; G01 has an executed docs +receiver. Those three confirmed failures remain unfixed in this audit. G05/G08 +need their clean build/typecheck witnesses. Broader proposals remain open in +[the coverage map](../oracle-coverage.md). + +The PR is **not ready on this evidence**: G09/G10 violate the accepted cache +law, and G01 teaches an unconfirmed successful mutation. This audit identifies +repairs and checks; it does not postpone them by agreement, retire those laws, +or claim completed implementation. No guarantee of zero future findings is +justified by a complete matrix or a passing random campaign. + +Final inventory assertions pass: 53 unique finding IDs, four caveats, 26 check +definitions, and a mapped check for every row. The final read-only GitHub query +returned the same head, two review bodies, and five general comment bodies, +all unchanged from the first snapshot. Changed-file lint, report formatting, +and `git diff --check` pass. pnpm reports the existing lockfile/installed +override mismatch; no dependency installation or override change was made. + +## Additional findings during oracle construction + +These append to the original inventory; they do not replace or renumber its 53 +findings. Production remains at `858b174a65f160d13d01f0324c7481cc8e4eba3a`. + +| ID | Finding and evidence | Owner and next witness | +| --- | --- | --- | +| P01 | The actual installed Changesets CLI selects `latest` for candidate packages whose registry history contains only prior RC versions. The chunk 13 command receiver has 12 intended tag-comparison failures and 28 passing neighbors. Independent reviewers observed the raw publish commands; strict shims select no tag. | O13 release/publish history owner. Preserve this RED, add real local-registry HTTP reception separately, and defer production repair until the oracle campaign finishes. Command evidence does not establish npm HTTP or installed-consumer behavior. | +| P02 | `packages/db/skills/db-core/custom-adapter/SKILL.md:222` still recommends returning `{ txid }`. Source inspection contradicts the approved explicit-await mutation contract; this is authored guidance, not generated API documentation. | O21 active-guidance consistency and actual snippet receiving checks. Preserve before/historical examples explicitly; repair current guidance after the oracle work. | +| P04 | `docs/guides/mutations.md:521` says Query Collection currently refetches automatically after persisting changes. The adjacent example explicitly awaits refetch. The current prose contradicts the approved removal of automatic refetch; this is source-inspected evidence, not a behavior-test failure. | O21 authored-guidance consistency and actual fenced mutation receivers. Check current prose separately from historical/before examples, then repair the guide after the oracle campaign. | +| P05 | The real npm/pnpm HTTP receiver attempts to upload an already-published RC when its packument has an `rc` tag and no `latest` tag. Three histories fail the raw no-republication comparison: retry after candidate-phase failure from prior-RC and never-published starting states, plus an already-current no-op. Duplicate PUTs receive HTTP 409; exact local manifest/pre-state restoration is checked first. | O13 actual HTTP receiving owner, `scripts/publish-http-oracle.property.test.js`. Preserve the assertion RED at unchanged production, including raw attempts and registry state, and defer publisher repair until the oracle campaign finishes. Installed consumers remain O22. | +| P06 | `packages/db/skills/db-core/custom-adapter/SKILL.md:209` says core will not call `unloadSubset` when `loadSubset` throws synchronously. This current guidance contradicts `query/live/ARCHITECTURE.md:771`: a synchronous throw is a failed load whose owner retains an unload opportunity on release. This is a source-inspected guidance conflict, not a newly executed runtime failure. | O21 active-guidance consistency, receiving the existing subscription lifecycle contract. Check the surrounding ownership guidance as well as deprecated handler return values; do not invent a second acquisition model or repair the skill before the oracle campaign finishes. | +| P07 | The same custom-adapter skill at lines 213–216 says the previous acquisition releases when its replacement returns. Architecture lines 783–786 require detaching and releasing the old lease before reacquisition, and reacquiring only while its demand and replay remain current. Source inspection establishes a guidance-order contradiction, not a new runtime defect. | O21 current-guidance consistency and the existing subscription lifecycle owner's replacement/reentrant-release histories. Preserve the separate ordering claim instead of treating P06's synchronous-throw witness as evidence for it. | +| P08 | The custom-adapter `loadSubset` fence at lines 170–198 ignores the establishing `commit()` receipt before its async callback returns. This is a candidate applied-settlement defect in executable guidance. No held-receipt receiving witness has run; it is not yet classified as a confirmed behavior failure. | O21 actual fenced callback receiver. Hold an establishing commit behind an optimistic transaction, invoke the actual documented callback externally, and compare load settlement with visibility under the existing applied-settlement law. Avoid awaiting this load from its own mutation handler. | +| P09 | Actual LEFT-join Effect startup emits an update for an unmatched occurrence that never entered its callback-visible result. The independent trace has one callback containing that update (identical previous/current values), an unrelated peer enter and the actual matched enter; its literal-true lazy load finished first. This is an executed callback-delta failure, not merely a disputed count of startup batches. | O11 Effect receiving expansion, chunk 16. Preserve the initial RED and full ledger. Public Effect delta semantics and the architecture's absent-to-absent classification prohibit the phantom update; do not accept it as a legal startup prefix or erase initial callbacks. Evidence: `chunk16-startup-independent-trace-r1.log` in the scratch evidence directory. Production repair remains deferred. | +| P10 | Author-run correlated-child callback reception exposes reserved fields before downstream fn.select, while all 231 root receiving histories pass. Minimal new-shape witness selects only a fake $key: the callback sees present/own true and `spoof-$key` where the approved law requires absence; an ordinary nested observed output retains that wrong fact. Initial 575-test run has 203 callback-law REDs and 372 passes, including inline-preservation and Collection-input rejection controls. Independent review and final instrument calibration are pending. | O12 callback-input owner, chunk 17. Compare present inputs and derived ordinary outputs under the same model through actual correlated-child compilation; root GREEN supplies no child-boundary credit. Preserve source-spread spoofing and functional-new-spread leakage as adjacent witnesses, distinct from earlier illegal-correlation/order setup failures. Evidence: `chunk17-fourth.json` and `.log` in the scratch directory. Production remains unchanged. | +| P11 | Early metadata-fixture debugging observed a raw structured `optional: undefined` projection arrive as null. The author withdrew its initial normalization explanation: existing source-ref tests and public literal descriptions do not establish that raw-literal policy. This is an unresolved boundary observation, not a confirmed product defect or approved normalization. | Read-only literal-law/owner investigation distinguishes raw undefined select specification, explicit val(undefined), source-ref undefined, nested/array forms and null controls, checking callback and final own-property facts. The new O12 grammar retains strict source/spread/functional undefined coverage without claiming a structured-literal law. Preserve this question separately before any production repair. | + +P01 evidence: `chunk13-publish-independent-final.log` under +`/private/tmp/rc2171-oracle-challenge/`. P02 was independently identified during +read-only O20/O21 preparation. The existing typed live-query schema limitation +is already recorded in the coverage map; it is not relabeled as a newly +confirmed regression here. A scratch schema-role experiment establishes that +client mutation inputs, rather than synced rows, are the appropriate receiving +boundary for a future transforming-schema retention check. + +P03 (existing replay instrumentation): `recordOracleReplay` is documented as +never throwing, but a real `fc.check` RunDetails value and an unwritable witness +channel (a directory) produce `EISDIR` from its synchronous writer. The finite +comparison wrapper added in chunk 13 preserves both primary and writer errors; +that evidence does not establish the older standalone recorder's intended +policy. The guarded replay/standalone recorder owner must resolve this existing +comment/behavior conflict before claiming a broader writer-failure guarantee. +Independent probe: `chunk13-old-record-writer-probe.log` in the scratch evidence +directory. No library production repair was made. + +G08 clarification and current receiver: the unchanged bad handler at +`packages/db-sqlite-persistence-core/tests/persisted-source-receipts.test.ts:957` +is a plain persisted Collection handler, not Query-specific. Core Collection +insert/update/delete handlers all require `Promise`. Running that package's +actual `tsc --noEmit --project tsconfig.json` (which maps DB to current source) +produces the intended TS2769 handler diagnostic plus its cascading call-site +diagnostic. Evidence: `o22-persisted-core-source-types-baseline.log`. Offline's +missing workspace DB build edge is separately demonstrated by pnpm's filtered +project closure; its committed lock already contains a DB link, so this finding +does not by itself prove frozen installation fails. + +P10 final independent receiving evidence: chunk 17 now has 580 tests, +377 passes/203 intended correlated-child callback-law REDs, with all 2,153 +declared states reached. All 203 failures are the expected law exception, +not setup/runtime failures. Controls and valid replay-environment/partial +selection checks pass their intended judgments; production remains unfixed. +The earlier 575-test author run above is retained historical evidence. + +P11 runtime authority and receiving evidence are now resolved by independently +accepted chunk 19. The public builder explicitly promises literal null/undefined +pass-through and recursive record selection. Eight of its 68 exact typed +root/QueryRef histories fail because raw selected undefined becomes null, +including mixed arrays. All three ready cuts, public events and callback inputs +are retained. Public type accuracy and correlated receiving paths remain next +gates; a broader mixed-expression type-admission diagnostic remains unresolved. + +| ID | Finding and evidence | Owner and next witness | +| --- | --- | --- | +| P12 | Structured selection drops an authored empty-record property, including nested empty records and the empty box from a nested omitted field. Ten of chunk 19's 68 histories expose this through real root/QueryRef receivers. A minimal source with only id/version and select `{ id: row.id, version: row.version, value: {} }` loses value at public rows/events, with no nullish member involved. This is an executed recursive-container-law failure, independently reviewed. | Same literal model/registration owner as P11. Preserve the ten REDs and add same-model correlated array/singleton/facade receivers before repair. All 204 receiving and 102 callback states run once; exact typed factories compile. Evidence: `chunk19-r2-freeze-manifest.json` and `chunk19-minimal-empty-record-red.log`. | +| P13 | Public `LoadSubsetFn` prose at `packages/db/src/types.ts:384–389` describes resource ownership transferring only after returning true/Promise and cleanup of partial resources on synchronous throw. The newer architecture/lifecycle contract retains failed-load demand and an unload opportunity on release. This wording requires reconciliation; it does not literally make P06's stronger promise that core never unloads a synchronous throw. Source inspection only; no new runtime failure is claimed. | O21 authored-guidance consistency, under the accepted architecture/subscription lifecycle law. Keep this public source comment distinct from the stale skill text and generated API docs. Repair source guidance after all oracle work; leave generated docs to CI. | +| P14 | Architecture cleanup/restart prose at line 878 and custom-adapter skill lines 350–354 say markReady after an initial fail throws. Current public API prose, glossary and source-health oracle promise a same-run no-op. Source-health's lazy receiver also catches child-ready exceptions while discarding its model's predicted successful call outcome, so status-only checks can hide the wrong throw. This is source-inspected guidance conflict and an oracle observation gap, not an executed production defect. | O21 guidance plus the existing source-health primary oracle. Add legal generated fail→actual child-ready and fresh-run neighbors, compare call outcome with the existing shared model, declare actual callback reach, and reject a throwing in-memory overlay before guidance/production repair. An absent optional callback call must receive no reach credit. | + +Chunk 19's unchanged production run has 103 tests: 85 pass and 18 intended +assertion REDs (eight P11 plus ten P12), with all 306 states reached once. +Independent review repaired only a missing oracle declaration, verified the +semantic-neutral delta, strict driver admission and immutable capture controls. +This is accepted instrument coverage; it does not close these product defects +or change the original 53-finding accounting. P13/P14 read-only preparation and +exact-source evidence are retained in the O21 scratch plan. + +P11/P12 correlated receiving evidence: independently accepted chunk 20 runs +272 exact legal histories through both array spellings, singleton and facade +materialization. All 3,264 declared states execute once; 200 histories pass and +72 preserve the same literal-law REDs, with no driver-execution failures. The +33 checker controls and six independent facet-isolation controls pass. Public +inferred types remain the next gate. Original QueryRef fixtures missing their +own correlation WHERE are retained as documented missing-premise evidence, +not value-law failures. No production or guidance repair has occurred. diff --git a/docs/contributing/oracle-reviews/2026-10-28-pr-2171-review.md b/docs/contributing/oracle-reviews/2026-10-28-pr-2171-review.md new file mode 100644 index 0000000000..edb9b5d12a --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-28-pr-2171-review.md @@ -0,0 +1,306 @@ +# PR 2171: review evaluation, October 28 + +Checked code head: `751f9a74500a947050ddba0048a93c99d87339b7`. +Starting review head: `7c53f271acd4be62d40475b509d2751d27479866`. +Merged main: `b305fb2dcfbc296fd1980ceb6e611e0993fec740`. + +This record evaluates the user's ten numbered findings. Finding 10 has two +independent claims, retained as R10a and R10b. The ledger below accounts for all +11 claims. No finding was deferred. The task-local raw ledger and command logs +are under `/private/tmp/rc2171-review`; the conclusions and reproduction commands +needed after that scratch directory disappears are recorded here. + +## Laws, experiments, and inference + +The authority for the three policy choices is the maintainer's explicit answers +in this review. Query and Electric discard legacy handler values at runtime. +Inline `getKey` and `schema` bind until query identity changes. Structured source +selections and spreads retain source metadata; a functional selection retains it +only when it returns a source row directly. New functional objects receive the +output Collection's metadata. Colliding reserved names cannot change that rule. +The migration guide and executable owners now state those decisions. + +### Handler settlement and refresh + +A fulfilled handler must not become a failed transaction merely because it +returned a discarded value. A rejected handler must retain its exact rejection. +The earlier Electric histories did not cross handler verdict with a legacy +return value. The Query owner covered confirmation paths but omitted that +return-value dimension. Those gaps let the adapters disagree at settlement. +The neighboring law is explicit synchronization: completing a Query handler does +not fetch or replace authoritative rows. The handler must await refetch or a +direct write if settlement must expose its confirmed result. + +The Electric owner is `electric-oracle.property.test.ts`. Its finite grammar +crosses insert/update/delete, fulfillment/rejection, and absent/legacy returns: +12 histories. Each history holds the handler, checks optimistic rows, then +compares the exact rejection identity, simulated server-write count, and settled +public rows. Successful handlers explicitly deliver ShapeStream confirmation and +await its txid. Each history requires one optimistic and one settled reach. +The driver uses real Collection transactions and controlled SDK event delivery. +It does not claim a live Electric-server result. + +The reviewed runtime-rejection wrapper fails three histories at the public +Promise-verdict assertion. The other nine pass. Passing those neighbors rejects +a repair that merely suppresses every handler error. With the rejection wrapper +removed, all 12 pass; the surrounding Electric selection passes 334 runtime tests. + +The Query ownership owner crosses CRUD, no confirmation/refetch/direct write, +and absent/legacy return: 18 histories. It checks optimistic rows while held and +exact fetch counts and public rows at settlement. Each history requires both +reach states once. A real QueryClient supplies the fetch boundary. External +focus, reconnect, polling, and invalidation are separate triggers, outside this +grammar. The earlier automatic-refetch calibration failed all nine original +confirmation histories at fetch counts; its evidence remains in +`pr-2171-explicit-query-refresh.md`. The final complete Query runtime suite +passes all 978 tests in 29 files, including both ordered-refetch campaigns. +Moved main tests now initiate refetch explicitly without changing their public +assertions or observation checkpoints. + +### Query identity + +The earlier identity owner did not vary inline option references across host +rerenders. It could stay green while ordinary render inputs rebuilt a Collection. +The React source-ID owner now crosses live/Suspense hooks, getKey/schema/both, +and derived/explicit-key identity: 12 histories. Each starts a ready source, +rerenders three times with new closures, applies a source update, then changes +query identity. Collection and configured option references must persist before +the change and be replaced afterward. Every history requires rerender, +source-update, and identity-change reach once. + +The earlier three derived-identity regressions fail before the repair. The final +matrix with the previous option-reference identity rule fails six derived cases +at Collection identity; the six explicit-key neighbors pass. The repaired source +identity file passes all 92 tests. The broader React selection passes 442 tests. + +`schema` currently has a separate public declaration limitation: its hook config +type is `never`. The runtime histories supply schema with `Object.assign` at the +JavaScript boundary. They do not establish a new schema typing contract. Infinite +hooks do not accept inline Collection options; their existing identity/window +histories run as neighboring guarantees. The shared implementation also serves +other adapters, but this new inline-option matrix supplies a real React-host +witness, not a witness for every framework scheduler. Preact's existing complete +runtime conformance suite passes 98 tests. + +### Metadata at Collection publication + +The earlier metadata owner did not cross selection form, joined source choice, +and individual reserved-name collisions at pending and settled checkpoints. +A source-guessing implementation therefore escaped its narrower comparisons. +The primary virtual-row-fields owner now enumerates 50 legal histories: +single/joined sources, structured projection/spread, functional +projection/spread/direct return, and none/each/all reserved-name collisions. +Direct returns cannot author a collision, so that combination is excluded. +The source's label changes while an optimistic transaction is held. Each history +checks all four fields before mutation, during the hold, and after settlement. +The model decides ownership from the authored selection form, never by comparing +selected values. A clean sibling supplies only the output-generated key. + +Declared reach is 25 single-source pending histories, 25 joined-source pending +histories, 40 colliding histories, and two direct functional returns. The minimum +for each declared state is one. Individual `$collectionId` collisions matter: +a fix for only `$key` or all-fields collisions would miss them. + +The initial expanded tests failed on the reviewed implementation. The final +50-history grammar, run with the previous compiler restored, fails 26 histories +at metadata assertions and passes 24. This is assertion failure, not setup failure. +The repaired primary and supporting metadata tests pass 57 tests. The broader +metadata/retention selection passes 164. Supplemental tests preserve two spread +orders, an unmatched RIGHT join, direct functional returns, facade projections, +and inline-value shape. The new grammar does not establish every join kind, +arbitrary nested histories, custom-key projections, provider behavior, or every +framework receiving path. Those limits remain explicit in the coverage map. + +### Compatibility and integration + +A probe against the actual DB 0.15.2 package confirms that `when` exists and +`completeWithoutCommit` does not. Calling the latter throws a TypeError. +Offline Transactions now peers only on `^1.0.0-rc.0` and receives a stable minor +bump. The release inventory test enforces that manifest boundary. No runtime +fallback preserves a false compatibility claim. Existing restore, serializer, +and settlement owners pass 89 tests. This is package compatibility metadata, +so a manifest/version fixture is the appropriate new instrument. + +The main merge provides Convex and Preact. Convex's obsolete BaseCollectionConfig +return generic is migrated. All 22 Convex runtime files pass, with 108 tests. +Both adapters build and typecheck. The release fixture checks every actual public +workspace manifest against the first-RC inventory; version and publish fixtures +pass 11 tests. Query and Offline Transactions remain stable minors, as requested. +The inventory/version checks are configuration evidence rather than a state model. + +## Reproduction entry points + +Run each command from the named package directory at the checked code head. +These commands isolate the three new finite grammars. Omit `-t` to run each +complete owner file. + +```sh +# packages/db +../../node_modules/.bin/vitest run tests/query/virtual-row-fields-oracle.test.ts -t 'attributes root metadata' --coverage.enabled=false --typecheck.enabled=false + +# packages/react-db +../../node_modules/.bin/vitest run tests/source-id-reuse-oracle.test.tsx -t 'retains inline' --coverage.enabled=false --typecheck.enabled=false + +# packages/electric-db-collection +../../node_modules/.bin/vitest run tests/electric-oracle.property.test.ts -t 'settles a handler independently' --coverage.enabled=false --typecheck.enabled=false +``` + +Calibration used those same commands with one previous production file restored +at a time, while keeping the final tests. For metadata and Electric, restore +`packages/db/src/query/compiler/index.ts` or +`packages/electric-db-collection/src/electric.ts` from the starting review head. +For identity, restore `packages/db/src/live-query-options.ts` from merged main +`b305fb2dcfbc296fd1980ceb6e611e0993fec740`. Restore the checked code after each +experiment. The expected failures and passing neighbors are recorded above. + +## Lossless finding dispositions + +| ID | Original claim and technical verdict | Action and durable value | +| ---- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| R1 | OfflineExecutor calls RC-only `completeWithoutCommit()` while its peer accepts DB 0.15. Confirmed against published 0.15.2; restoration can throw and remain held. | **fixed-now**: RC-only peer, stable minor bump, migration warning, actual-manifest release check. Keep the restore/settlement owners as the behavioral boundary. | +| R2 | Query's automatic-refetch and handler-type removal breaks callers despite a minor bump to stable 1.x/latest. Technically a compatibility break. | **accepted-design**: the maintainer explicitly corrected the requested major to a minor. Preserve prominent before/after migration instructions, explicit refetch/direct-write examples, removed `{ refetch: false }`, and stable version/tag fixtures. Do not describe this as semver-compatible. | +| R3 | Inline getKey/schema references recreate a live query on render, potentially looping on a ready source. Confirmed Collection identity churn. | **fixed-now**: bind options to query identity; extend primary React owner across both hook paths and both identity forms. Keep schema declaration and cross-framework evidence limits explicit. | +| R4 | Electric throws after a legacy-returning handler already commits, so successful work appears failed and a retry may duplicate it. The error is also uncoded. Confirmed false rejection. | **fixed-now**: remove the post-handler rejection and wrappers. The 12-history owner checks exact fulfillment/rejection and confirmed rows. No replacement error needs a code. | +| R5 | A functional spread overriding virtual metadata can fall back to the wrong joined source. Confirmed source guessing is unreliable; callback result equality cannot identify authorship. | **fixed-now** under the approved rule: direct functional source returns retain metadata; new objects receive output metadata. Remove value-matching provenance guesses. The 50-history owner covers source choice and each collision. | +| R6 | Selecting `$collectionId` changes ordinary projection attribution from the output Collection to its source. Confirmed collision-sensitive attribution. | **fixed-now**: ordinary projections receive output metadata regardless of selected virtual names. Preserve this distinct claim and its single-field collision histories alongside R5. | +| R7 | First-RC changeset lists missing Convex/Preact packages and versioning throws; merging main alone leaves their API migrations unchecked. Confirmed on the starting branch. | **fixed-now**: merge main, migrate Convex generics, build/typecheck both adapters, run both runtime suites, and test actual workspace inventory. | +| R8 | Query silently discards returns while Electric throws; Query migration failures can appear only as stale UI. Confirmed inconsistent behavior. | **fixed-now**, sharing R4's repair: both discard runtime values and reject them in types. The maintainer explicitly chose this policy. The Query oracle still requires explicit confirmation and checks reversion without it. | +| R9 | Three identical Query wrappers and repeated utility types add duplication. Confirmed maintainability issue. | **fixed-now**: one generic wrapper preserves active-handler try/finally accounting and discards returns; one handler-utils alias removes repeated types. Existing CRUD/refetch histories protect the unchanged counter boundary. | +| R10a | React always supplies `dependenciesStructurallyEqual: false`, leaving unused shared-policy logic. Accurate for the older branch, stale after main integration. | **stale**: main uses the shared window-binding implementation. Other frameworks still use that shared policy; deleting it globally would be wrong. Preserve the identity/window/render tests as the useful review idea. | +| R10b | prepareDerivedQuery throws an uncoded Error and ships its message. Accurate for the older branch, stale after main integration. | **stale**: current preparation propagates coded `UnhashableQueryIRError`; no replacement plain Error remains. Identity tests and minified/production-error tests preserve the useful bundle concern. | + +Accounting: 11 claims = eight fixed-now + one accepted-design + two stale. +No refuted, duplicate, deferred, or unresolved product-decision entries. +The original line references belong to the starting review head and moved during +the main merge. No source-order ID was merged away. + +## Adjacent integration repairs + +Main's split ordinary/oracle tests remain split. Mechanical migrations replace +`isPersisted.promise` waits and `$synced` reads without relaxing law assertions. +The virtual-field type oracle retains its declaration and shared type fixture. +A malformed generated `readonly` line is removed from the persistence type +instrument; both fixed and random campaigns pass (four tests total). +The minified-export inventory drops the removed `DuplicateAliasInSubqueryError`. +Published error-code documentation and its frozen code registry remain available +for old errors. The production-error oracle passes 18 tests, and the consumer +bundle check passes for 263 distinctive literals. The minified API checks 113 +error names plus index metadata, rows, and live updates. + +Authored guide/skill examples now pass without adding failing snippets to the +baseline. Twenty obsolete baseline entries were removed. Existing 252 failing +examples remain acknowledged. Pseudo-code fragments explicitly declare nocheck; +current API examples receive concrete row types and required configuration. +The 25 relative migration-guide links resolve. The repository-wide link checker +still reports 51 existing failures, including code links outside its docs root. +Generated API references match main exactly and are absent from the PR diff, +as the maintainer requested. Their CI workflow owns regeneration. + +## Oracle conformance and evidence limits + +- **ORC-001/002/003/005/009:** authority, independent judgments, legal forms, + drivers, and public cuts are stated above and beside their executable owners. + No concrete missing-source-information hypothesis remains for these controlled + checkpoints. Metadata authorship is now an explicit policy, not inferred data. +- **ORC-004/008/013:** removing CRUD, confirmation, verdict, return-value, identity, + selection-shape, or collision axes loses a distinguishing case named above. + The finite bounds are exhaustive for their declared grammar. A direct return + with authored overrides and inline infinite-hook config are excluded invalid + forms. No new production-like state machine was introduced in the models. +- **ORC-006:** previous production designs fail at Collection identity, metadata, + Promise verdict, or fetch-count comparisons. The passing neighboring cases + reject blanket failure suppression, source-always attribution, and + collision-only fixes. Calibration is not a universal proof. +- **ORC-007:** the new matrices enumerate finite grammars rather than claiming + random-campaign coverage. Existing ordered-refetch and persistence type owners + retain their fixed/random campaigns and replay interface. +- **ORC-010:** controlled gates release and Collections clean up after histories; + the metadata owner preserves primary/secondary cleanup failures. The evidence + includes clean green process exits, not merely some passing assertions. +- **ORC-011:** source metadata and output metadata are compared through distinct + source-preserving and clean projection forms. No additional named shared-model + semantic fault required another independent model in this review. +- **ORC-012/015/016:** this versioned record identifies the checked code head. + New reach requirements and counts appear above. Oracle declarations remain + intact; supplemental handwritten examples stay in ordinary files. +- **ORC-014:** React uses the real host. Query uses real QueryClient with controlled + rows. Electric controls SDK delivery. No claim about an actual provider commit + or every receiving framework follows from those premises. + +The full repository CI and native provider suites did not run locally. Early +Vitest combined runtime/type invocations reported cross-package rootDir setup +errors. Clean runtime runs and separate `tsc --rootDir .` checks replace that +invalid combined evidence. Initial all-files ESLint exceeded Node's default +heap; package batches and the normal chunked commit hook supply lint evidence. +These setup failures are not RED or GREEN product evidence. + +## Code weight and review judgment + +`pnpm size:compare --exact-base --base origin/main` measured production app +bundles against main at `b305fb2dc`. Measured packages save 750 gzip bytes and 528 brotli bytes together. +The typical app saves 302 gzip and 230 brotli bytes. DB grows 130 gzip bytes for +explicit metadata-source rules. Deleting value-matching guesses and consolidating +copy-on-write attachment reduces the repair's cost; deleting Electric matching +wrappers, Query auto-refetch, and deprecated APIs makes the total negative. +Tests and contract documentation grow separately and do not enter those figures. + +Reviewer recommendation: **hire for defect discovery, with maintainer review of +policy proposals**. The review found real compatibility, identity, settlement, +and metadata hazards. Its strongest observations crossed package and async +boundaries. It did not supply oracle or test-gap evidence, treated an explicitly +chosen minor as a new decision, and included two claims superseded by main. +Those limits reduce prioritization quality without erasing the useful findings. + +Readiness: **unproven pending current-head CI and broader integration evidence**. +The reviewed repairs and finite-law evidence are complete; this record does not +claim that all release, provider, platform, or repository checks passed. + +## Follow-up main integration + +Checked merge head: `ade3920bf67d804f3d5bbfb30c757be3ec75ba0b`. +Merged main: `2dc655f55fee13bd8f37252c592022ce3848b9bf`. + +The maintainer requested another main merge after the review fixes were pushed. +The merge applied without conflicts. It imports the lazy-demand replay repair, +a persistence guide correction, and Angular/UUID dependency updates. No imported +test required a deprecated-API migration. The authored migration guide now notes +that a synchronous `loadSubset` throw retains demand and that releasing this +failed demand calls `unloadSubset`. + +The combined tree passes 804 DB runtime tests in 11 files, including replay, +ordered loading, virtual-row metadata, and Collection/include publication. +DB TypeScript checking and DB/Query builds pass. All 978 Query runtime tests +in 29 files pass against the rebuilt DB, including both ordered-refetch +campaigns. React's 92-case source identity owner also passes after that build. +All 11 release fixtures pass. Offline frozen-lockfile verification passes +without an installation workaround. The normal merge commit hook passes. + +The first Query run used the previous DB dist and failed the two newly imported +bounded-refetch witnesses. Rebuilding DB makes both pass, followed by the complete +green Query run. That stale-build run is setup evidence, not a product RED. +The broad persisted-session run also started before that rebuild; its 70 passes +are not credited as a current-build integration witness. After rebuilding, both +new persisted-session and browser multi-tab replay witnesses pass their existing +`it.fails` checks. They still reproduce the known source/persistence gaps in +[#2281](https://github.com/TanStack/db/issues/2281), rather than proving those +product behaviors correct. Those sync metadata/durability gaps are separate +from the virtual-row attribution contract evaluated above. + +This integration changes none of the 11 finding dispositions. The original +calibration remains evidence for the unchanged compiler attribution, option +identity, and handler-return policies; the affected comparisons pass on the +combined tree. Generated references still match main. The earlier byte figures +retain their stated `b305fb2dc` base and were not remeasured for this merge. +Angular/TrailBase runtime suites and current-head CI remain unverified locally. +Readiness remains unproven, separate from the completed review accounting. + +### Dependency and CI formatting follow-up + +Integration head: `e46d67b2d` includes main `bc77952e5` and the PR's automated +formatting commit `b05e4052c`. The latter changes quote style in three project +example files. The main follow-up updates Vite dependencies. A direct diff +confirms that DB, Query, React, Electric, and Offline Transactions production +sources are unchanged since the runtime-checked merge `ade3920bf`. +Frozen offline lockfile verification and all 11 release fixtures pass again. +The updated Vite toolchain was not installed or runtime-tested locally; CI must +validate it. No generated reference diff or installation workaround is included. diff --git a/docs/contributing/oracle-reviews/2026-10-28-pr-2171-second-review.md b/docs/contributing/oracle-reviews/2026-10-28-pr-2171-second-review.md new file mode 100644 index 0000000000..4dae0454ab --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-28-pr-2171-second-review.md @@ -0,0 +1,138 @@ +# PR #2171: second review evaluation, 2026-10-28 + +This record evaluates the 15-item review supplied by the maintainer. The reviewer targeted `0a60ad85b`; this evaluation started at `66454803bcd200bf56fcf569179c3007a51e859e`. Repairs and executable coverage are in `68228aaf3a878075af3dc04636133aef95480a09`. This report is a subsequent documentation commit. The earlier [review record](2026-10-28-pr-2171-review.md) remains historical evidence for its own commits. + +## Laws, experiments, and inference + +No product decision remains pending. The maintainer explicitly chose these rules: + +- Mounted hooks retain inline `getKey` and `schema` until query identity changes. Independent DbClient callers must reject conflicting option references under one cached identity. Distinct queryKeys permit different behavior. +- Structured whole-row selections and spreads retain source metadata. The last **present** spread supplies it. Direct functional source returns retain it. New functional objects receive output Collection metadata. Selected reserved names cannot choose attribution. Inline include values keep their authored shape. +- Query and Offline Transactions remain stable 1.x packages because both accidentally reached 1.0. Query's automatic-refetch removal ships as a **minor**, as explicitly requested. Both new stable minors require the RC DB APIs. + +The scratch ledger inventoried the whole review before any production repair. The pre-edit gate ran the expanded metadata, cache, React receiving, and transaction comparisons against unchanged production. It also checked existing React retention histories, Offline restoration callers, release fixtures, and DB 0.15 capabilities. Later metadata composition additions were calibrated separately against the original compiler; that later experiment is not evidence of their presence in the initial gate. + +| Law and authority | Earlier coverage and missing distinction | Decisive experiment | Repair and enforced boundary | +| --- | --- | --- | --- | +| Published Collection metadata follows authoring shape, through query composition. Authority: `VirtualRowProps`, the architecture's facade boundary, issue #1319's reserved-name concern, and the approved functional-selection rule. | The primary virtual-row-fields owner compared source/output attribution directly, without QueryRef/union composition or a null later spread. The model did not require a new rule for reserved names; its driver failed to reach recursive publication. | Initial 209-case grammar: 92 assertion failures, 117 controls pass. Final 409-case grammar against the original compiler: 220 failures, 189 pass. A first-present-spread mutant fails four comparisons, although it fixes the reported null-last example. Repaired grammar: all 409 pass. | Propagate the Collection-row boundary through recursive query compilation, preserve flat union branch metadata, and choose the last non-null source spread. Compare all four public fields after preload, during a held source update, and after settlement. New ordinary `hasPendingWrites` projections also match the source at those cuts. | +| A reusable DbClient result cannot substitute incompatible local row behavior. Authority: the maintainer's same-identity conflict decision. Mounted retention remains a separate approved law. | Source-object identity and mounted option retention already had tests. Neither generated independent callers differing only in row options. Portable hashes correctly omit runtime function references. | Core gate: 36 failures/108 controls across 144 cases. Real React gate: 6 failures/18 controls across 24 cases. Removing only the new option guard reproduces those exact failure counts through both drivers. Repaired core and React matrices pass. Existing React retention/source-ID owner: 92 pass before and after. | Keep local option-reference claims with the reusable client record, outside the transport hash. Check preload, observer preload, subscription, and real React initial-render admission. Hooks compare the retained Collection's options, preserving inline render behavior. | +| External completion admits only pending transactions and cannot revise another transaction verdict. Authority: commit admission and terminal settlement outcomes. | The optimistic-history model already distinguishes pending/persisting/completed/failed but had no external-completion action. No state observation caught verdict resurrection. | The extended primary model fails on the unguarded helper. Removing the repair again fails all four state histories, including the second completion of initially pending work. The repaired outcomes suite passes 27 tests. Offline baseline restoration/interruption callers pass 16; full repaired Offline suite passes 302. | Admit pending work only, using the existing coded admission error. Compare transaction state, request outcome, and public rows after each model action. The finite addition crosses four states with 1–3 completion attempts. | +| A declared DB peer provides the APIs the adapter calls. Authority: Query's actual sync boundary and honest package compatibility. | The release fixture still expected the broad Query peer. The old DB capability path was absent from the reviewer's experiments. | DB 0.15.2 tag source, instantiated through its real Collection sync boundary, supplies `undefined` for `fail`, `reportError`, and `reportHealthy`. A provider failure leaves preload unresolved until cleanup. The strengthened release fixture rejects the old range; all 11 repaired release fixtures pass. | Query now peers only on `^1.0.0-rc.0`, as Offline already does. The guide requires coordinated upgrades and tells DB 0.15 applications to keep earlier adapter versions. The probe does **not** establish an observed TypeError or a particular package manager's ERESOLVE wording. | + +### Grammar, reach, and public observations + +The metadata model judges source ownership from authoring shape rather than field values. Its 50 shape/source/collision inputs cross eight compositions: direct; QueryRef depths one/two; joined and functional QueryRef consumers; union first/last branch; and union through QueryRef. Eight further histories cross matched/unmatched later sources, spread order, and the source with an optimistic update. One existing classification history protects nested values, Dates, arrays, whole-row children, and inline projection shapes. Projection keys use a clean sibling as a trusted comparison for compiler-generated key spelling; this is an anti-spoofing judgment, not an independent key-generation model. + +Normal-run declared reach was: single pending source 200; joined pending source 200; reserved collision 320; functional direct return 16; QueryRef pending 250; union pending 150; null later spread 2. The same field comparison runs at preload, held-update visibility, and fulfilled settlement. Removing composition loses the subquery/union counterexamples. Removing match/spread-order controls permits a first-spread shortcut. Removing collision controls permits authored reserved names to choose attribution. + +The cache model is the independent relation `different options && same identity => reject`. Its 24 histories cross getKey/schema/both, same/different references, same/distinct queryKey, and derived/keyed identity. Each crosses pending/settled reuse and three core admission paths, for 144 cases. Each path has 48 histories, each reaching its declared admission once. React supplies 24 real mounted initial-render histories, each reaching render admission once, using the **same** model and comparison. Same-reference and distinct-key controls reject an always-throwing policy. The existing 92-case receiving owner separately checks retained options and real query-identity replacement. + +The transaction addition reaches three histories per starting state (four states, 1–3 attempts). It uses the primary optimistic-history driver and row/outcome/state comparison rather than a replacement expected result. Pending and persisting cannot merge in the model: completion is legal only in the former. Completed and failed cannot merge because their settled outcomes differ. The normal broader optimistic history campaigns retain their original budgets. + +These are finite boundaries. They do not prove arbitrary nesting depth, all join kinds and retractions, every framework, actual remote-provider timing, or arbitrary SSR hydration orders. Cache option coverage judges local result reuse; it does not claim semantic equality of distinct functions. Source-identity hydration histories remain separate evidence, not a claim that every option/hydration combination ran. Metadata's held update fulfills; the existing optimistic owners separately cover rollback/queued publication. No known reachable counterexample remains within these declared repair boundaries. + +## Lossless finding ledger + +Line references below are from the supplied review. They are not current line numbers. + +| ID | Original claim | Technical verdict and evidence | PR action | Durable value/destination | +| --- | --- | --- | --- | --- | +| R1a | `live-query-options.ts:270`; core identity test at 411 fails after removing getKey/schema from identity. | Confirmed test failure at starting head. Its replacement expectation contradicted the approved retention rule. | `fixed-now`: assert identity equality, without restoring reference-sensitive render identity. | Core identity test, cache conflict oracle, and receiving retention owner distinguish local claims from query identity. | +| R1b | `useLiveQuery.test.tsx:3569` retains the old Collection and fails the PR's test. | Confirmed on the real React path. Retention is approved behavior, so the stale expectation was the defect. | `fixed-now`: expect the original Collection/key after an option-only rerender. | The existing 92-case React oracle preserves replacement on actual identity changes. | +| R2 | `index-suggestion.test.ts:225` negates a query proxy, yielding constant false and no expected virtual-field warning. | Confirmed failing warning test. JavaScript negation does not construct a query expression. | `fixed-now`: order directly by `$hasPendingWrites`; index diagnostics and the corrected test pass. | Keep proxy-expression construction distinct from JavaScript evaluation. No production warning waiver was added. | +| R3 | `compiler/index.ts:1227`; unionAll uses the empty alias as source and loses pending/origin metadata. | Confirmed by the composed metadata comparison, including both branch positions and union through QueryRef. | `fixed-now`: use the flat branch row as union metadata source. | Primary metadata owner now generates union compositions and checks all four fields at three cuts. | +| R4 | `compiler/index.ts:2117`; a subquery can spoof reserved metadata because protection runs only at the outer root. | Confirmed. The gap also affected new functional objects behind recursive queries. | `fixed-now`: propagate the Collection publication boundary through FROM, JOIN, and union branches. | QueryRef depth, joined/functional consumers, and collision axes stay in the primary owner. Inline values retain authored shapes. | +| R5 | `compiler/index.ts:2079`; a later null outer-join spread deletes earlier metadata and hides pending state. | Confirmed. A simplistic first-spread repair would break matched two-source histories. | `fixed-now`: choose the last present whole-row spread. | Eight primary-owner histories and the four-failure first-spread mutant distinguish the wider law. | +| R6 | Query accepts DB ^0.15 but calls absent fail/reportError/reportHealthy; users can hit TypeError. | API incompatibility confirmed. The actual old-boundary probe observed missing functions and stuck preload, not an uncaught TypeError. | `fixed-now`: require RC DB; update importer and release fixture. | Capability probe, release fixture, and coordinated migration instructions prevent false peer claims. | +| R7 | `.changeset/first-rc.md:17` publishes removal of implicit refetch/Promise handlers as stable minor/latest. | Factual semver concern. The maintainer explicitly selected a minor after retracting the major request. | `accepted-design`: retain the requested minor and explicit breaking-change guide. | Preserve the deliberate compatibility cost and major-version alternative here; do not silently override the release decision. | +| R8 | Offline stable 1.2/latest requires prerelease DB and older DB applications can fail peer resolution. | Incompatible-peer fact is correct. Resolver-specific ERESOLVE behavior was not executed. The narrow peer prevents the previously identified missing-method runtime path. | `accepted-design`: stable 1.x release policy with mandatory coordinated DB upgrade. | Guide and changeset now explain both adapters' RC requirement and the option to retain earlier adapters. | +| R9 | Same hash plus different getKey/schema can return another caller's cached Collection rather than reject. | Confirmed through all three core paths and real React initial render. | `fixed-now`: store/check local option references on the reusable result. | Shared cache model and core/React drivers preserve the newly approved conflict rule. | +| R10 | RN `ListsScreen.tsx:118` uses removed `$synced`, proxy negation, and a selected reserved field, breaking Saving. | Confirmed source defect. The metadata owner now compares an ordinary selected pending field with actual source updates. | `fixed-now`: select `hasPendingWrites: list.$hasPendingWrites` and read that ordinary property in the card. | Query-runtime pending-field witness plus static component wiring. No native device run is claimed. | +| R11 | `completeWithoutCommit()` can complete failed work, yet is @internal, cross-package used, and documented publicly. | Confirmed state-admission failure in the primary transaction model. Cross-package use is intentional; public migration instructions should not name the internal helper. | `fixed-now`: pending-only guard; guide describes restoration capability without advertising the method. | External completion action, repeated-attempt grammar, state/outcome assertions, and Offline caller tests. | +| R12 | `local-storage-collection.md:118` still uses removed `isPersisted`. | Confirmed authored-doc residue. | `fixed-now`: use `when('settled')`. | Source check and migration guide; generated references remain CI-owned. | +| R13 | `db/src/index.ts:54` exports unused warnOnce/resetWarnings. | Confirmed no remaining production callers. Some Query tests still reset the obsolete helper. | `fixed-now`: remove implementation, Set, exports, direct tests, and obsolete Query resets. | Generic error-census fixtures remain: they test census behavior, not this removed API. | +| R14 | `live-query-window-binding.ts:75` retains unused dependencies recompile mode. | Confirmed no active caller. The structural-equality policy still serves real identity/reactive paths. | `fixed-now`: remove only the unused mode and comment. | Full React runtime and source-ID owners preserve window/identity behavior. | +| R15 | `compiler/index.ts:1063` allocates functional source aliases for every row. | Confirmed source placement. No timing or throughput improvement is claimed. | `fixed-now`: compute once per compiled select. | Existing functional and metadata comparisons enforce unchanged semantics; size measurement records shipped cost. | +| U1 | Reviewer could not run release fixtures because dependencies were absent. | Local installed fixture path works. | `fixed-now` (evidence gap): 11 release fixtures pass. | Commands and outputs below; no publishing command ran. | +| U2 | Reviewer Query run loaded an old second DB, so supplied no valid runtime evidence. | Setup failure did not establish product RED. | `fixed-now` (evidence gap): rebuild local DB, then all 29 Query runtime files/978 tests pass. | Preserve the single-runtime requirement and build-before-adapter-test workflow. | +| C1 | Branch moved from reviewed 0a60ad85b to 66454803b; roughly 40 later files unreviewed; detached review worktree preserves clean old tree. | Scope caveat confirmed: 44 files changed. Designated implementation worktree remained the only edited checkout. | `fixed-now` (scope accounting): reevaluated all findings at 66454803b, inspected changed production replay/ordered code, and ran its five receiving suites, 534 tests. This is not a claim to have independently reviewed every changed dependency/example. | Record exact heads, the integration evidence, and the separate readiness limit. The detached review checkout remains untouched. | + +The 15 numbered items account for **13 fixed-now and two accepted-design**. Splitting R1 into its two receiving paths produces 16 numbered subclaims: 14 fixed-now and two accepted-design. Including U1, U2, and C1 gives **19 ledger rows = 17 fixed-now + two accepted-design**. No confirmed-open, deferred, refuted, stale, duplicate, or unanswered design-decision item remains in this review's authorized repair scope. + +A follow-up census check exposed a stale frozen development message for error 234 after the approved cache-conflict expansion. It failed exactly on that text. The fixture now records the same code with its expanded getKey/schema diagnostic; the oracle prose states the authority. No generated API reference or error documentation was edited. This was a contract-fixture update, not a relaxed classifier. + +## Verification and calibration + +Commands use the implementation worktree. Package runtime commands run from the named package; `../../node_modules/.bin/vitest` is the installed root binary. `--coverage.enabled=false --typecheck.enabled=false` separates runtime checks from the independent TypeScript checks below. + +- DB final law run: metadata 409, cache 144, external outcomes 27, core identity and index warning tests: **611 pass**. Metadata reach counts are above. +- DB wider neighbors: **1293 tests/12 files**, plus **210/5**, passed. These include source identity, union, inline values, functional/context materialization, transaction and publication owners. Results overlap; these counts are not a unique-test total. +- Main integration: `vitest run tests/query/load-subset-replay-refinement-oracle.test.ts tests/query/ordered-source-loader-state-oracle.test.ts tests/query/ordered-work-oracle.property.test.ts tests/query/live-query-collection.test.ts tests/collection-subscription-replay.test.ts`: **534 pass/5 files**, fixed and random campaigns intact. +- Query: rebuild DB, then `vitest run --config vite.config.ts --coverage.enabled=false --typecheck.enabled=false`: **978 pass/29 files**, including both ordered-refetch campaigns. +- React: `vitest run --coverage.enabled=false --typecheck.enabled=false`: **765 pass/28 runtime files**. The final selected cache/source-ID/useLiveQuery run passes **172/3** after restoring the mutant and rebuilding DB. +- Offline: `vitest run --coverage.enabled=false`: **302 pass/24 files**. The pre-edit restoration/interruption witness was **16 pass**. +- Release: `node --test scripts/version-packages.test.mjs scripts/publish-packages.test.mjs`: **11 pass**. +- TypeScript: `tsc --noEmit -p packages/db/tsconfig.test.json`; `tsc --noEmit --rootDir . -p packages/react-db/tsconfig.json`; the same rootDir command for Query: **all pass**. The workspace rootDir includes the shared source/test owners legitimately imported across packages. +- DB build, frozen offline lockfile-only verification, changed-file oracle lint, ordinary ESLint, and Prettier pass. ESLint retains existing Query async/no-shadow warnings in unchanged lines. +- Consumer bundles: minified public API passes (113 error names, index metadata, rows, live updates). Production error erasure keeps development text and erases 263 literals, including 22 shared literals, in ESM and CommonJS. +- Production error census: **19 pass/2 files** after the approved error-234 fixture update. Its initial single text mismatch is preserved as an intermediate failure, not product compatibility evidence. + +Calibration reinstates one wrong implementation at a time, runs the same comparison, and restores source in a `finally` block. Cross-package calibration rebuilds DB before the React witness, then restores and rebuilds the repaired DB: + +| Wrong implementation | Comparison result | Classification | +| --- | --- | --- | +| Original compiler at 66454803b, final 409-case grammar | 220 failures /189 pass | Metadata assertion failures; expanded compositions calibrated after the initial gate. | +| First-present rather than last-present structured spread | Four failures /405 pass | Field comparison rejects matched two-source cases; distinguishes an incomplete R5 repair. | +| Remove only local option-conflict guard, core | 36 failures /108 pass | Admission throw/rejection comparisons. | +| Same missing guard, rebuilt DB and real React mount | Six failures /18 pass | Initial-render admission comparisons. | +| Remove pending-state external-completion guard | Four failing state tests /23 pass | Primary state/outcome law; persisting cleanup diagnostics preserve the original admission failure as cause. | + +Initial failed package-local binary paths and an attempted nonexistent old DB dist import were setup failures. They supplied no product evidence. The first broad Query/React commands also enabled Vitest's typecheck under a package-only rootDir: they emitted 211/212 cross-package source diagnostics. These are not passing commands. Runtime-only commands and explicit workspace-root TypeScript commands above resolve that test setup ambiguity without changing production or suppressing an assertion. + +The initial stale-test reproduction ran `vitest run --coverage.enabled=false tests/live-query-options.test.ts tests/index-suggestion.test.ts` from DB (29 pass/two fail), and `vitest run --coverage.enabled=false tests/useLiveQuery.test.tsx -t 'accepts getKey without queryKey'` from React (one fail). The oracle comparisons use these replay entry points from DB: + +```sh +../../node_modules/.bin/vitest run --coverage.enabled=false --typecheck.enabled=false \ + tests/query/virtual-row-fields-oracle.test.ts \ + tests/live-query-cache-options-oracle.test.ts \ + tests/optimistic-history-outcomes-oracle.test.ts +``` + +The React receiving calibration runs `vitest run --coverage.enabled=false --typecheck.enabled=false tests/cache-options-oracle.test.tsx` from React after rebuilding DB. No run-count reduction, failed-history waiver, or assertion relaxation supplies a GREEN result. + +## Oracle audit + +This is a self-review, not an independent second reviewer. It follows the oracle-authoring and oracle-review skills. + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Authorities and finite limits are explicit above and beside the models. | +| ORC-002 | Metadata uses authoring shape, cache uses caller declarations, and completion uses primary transaction state. None imports the production semantic classifier for its expected answer. Compiler-generated projection key spelling is the declared trusted comparison. | +| ORC-003 | Laws, models, grammars, drivers, and comparisons remain distinct in the executable owners and named shared model. | +| ORC-004 | Known witnesses reconstruct from the enumerated dimensions. Removing composition, match/order, option, or transaction-state axes loses the corresponding counterexamples. Bounds are 8 compositions, QueryRef depth 2, one held source update, 24 cache inputs, and 1–3 completion attempts. Invalid forms such as a spoofed direct functional row are not fabricated as legal direct returns. | +| ORC-005 | Public Collection fields at three cuts; public admission throws/rejections; transaction state/outcome/rows after each action. Held-source state is observed before the metadata comparison. No concrete missing-input hypothesis remains within those cuts. | +| ORC-006 | Original and narrow hostile implementations fail at the intended comparisons, with counts above. | +| ORC-007 | The new metadata/cache/completion additions are exhaustive finite grammars, not sampled generated properties. Existing important random optimistic, identity, and replay campaigns keep their fixed/random parity and original replay controls/budgets. | +| ORC-008 | Cache/metadata judgments are stateless. The transaction model reuses its existing four states; legal completion and terminal outcomes distinguish them. | +| ORC-009 | Collection, query identity, publication, pending/persisting, and settlement follow the project glossary. Local option claims are runtime references; transport hashes carry none. | +| ORC-010 | Metadata uses existing checkWithCleanup. Transaction cleanup retains the primary failure as AggregateError cause. Cache drivers release observers, client state, and source Collections in finally blocks. | +| ORC-011 | No unaddressed shared-fault hypothesis was identified. The clean sibling supplies key spelling only. Independent authoring rules and hostile mutants challenge the semantic judgments without cloning compiler control flow. | +| ORC-012 | This versioned record identifies the exact repaired commit, evidence, process order, all outcomes, and finite limits. | +| ORC-013 | Nullable/matched spread-order neighbors, same/distinct cache-key controls, and pending/persisting/completed/failed attempt histories distinguish the conditional laws. | +| ORC-014 | Real React reuses the shared cache model/comparison. Offline callers exercise pending external completion in restoration. Metadata is explicitly a real-core Collection boundary claim, not an assertion about a remote SDK or native host. | +| ORC-015 | Metadata's measured counts are above. Every core/React cache case asserts its path's admission reach. Every completion starting state reaches three histories. | +| ORC-016 | New regressions are inputs to the shared model/driver/checker. Ordinary identity/warning tests remain outside oracle files. | + +The complete declared laws are encoded and asserted at their named cuts for these finite grammars. No in-scope cell with a known counterexample is being relabeled as an external limit. Coverage ownership and bounds are recorded in [oracle-coverage.md](../oracle-coverage.md). + +## Code weight + +`pnpm size:compare --packages db,react-db,query-db-collection --files` compares production bundles with merge-base `bc77952e5`. DB grows 199 gzip/139 brotli bytes; React saves 199/190; Query saves 340/224. Total: **340 gzip and 275 brotli bytes saved**. The typical app saves **276 gzip/134 brotli bytes**. + +Against starting PR head 66454803b with `--base 66454803b --exact-base`, these repairs add **84 gzip/91 brotli bytes** to DB and **39 gzip/100 brotli bytes** to the typical app. React/Query package runtime bundles are unchanged. Added local claims distinguish two incompatible callers that portable hashes cannot distinguish; recursive metadata enforcement and transaction admission also need checks. Refactors removed the unused warning Set/API and recompile mode, hoisted per-row alias construction, reused error 234 and the existing transaction admission error, and avoided a new metadata registry or lifecycle. Those savings do not offset every guard byte. Tests and contract documentation grow separately and do not ship in these measurements. + +## Reviewer assessment and readiness + +**Hire, with calibration guidance.** The reviewer found reproducible failing tests, composition bugs, a real missing cache boundary, an honest peer mismatch, and inexpensive cleanup. The runtime repros had high value. Prioritization correctly put failing tests and metadata above code weight. The proposed direction needed context: restoring option-reference identity would reintroduce render churn, a major release would contradict the maintainer's explicit minor decision, and a broader Offline peer would claim nonexistent APIs. The downstream TypeError/ERESOLVE wording also exceeded the experiments performed. The reviewer clearly disclosed their setup failures and moved-head scope, which improves trust. + +Item accounting and authorized repairs are complete for this review. **Whole-PR readiness remains unproven.** CI on the starting pushed head was red in several DB, adapter, framework, SQLite, and browser jobs. This evaluation did not run native-provider E2E or independently review all 44 intervening files. Existing main-imported expected-failure persistence witnesses in #2281 remain separately documented product gaps, not evidence that these repairs failed. Later origin/main commits were fetched for context but are not merged by this repair. The new pushed head needs its own CI result; local green comparisons cannot supply it. diff --git a/docs/contributing/oracle-reviews/2026-10-29-pr-2171-deferred-oracles.md b/docs/contributing/oracle-reviews/2026-10-29-pr-2171-deferred-oracles.md new file mode 100644 index 0000000000..fa6afe5e13 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-29-pr-2171-deferred-oracles.md @@ -0,0 +1,251 @@ +# RC release scope: retained broader oracle failures + +This release deliberately defers the broader production discoveries below. +Their laws remain accepted and their failing comparisons remain preserved. +No GREEN, retirement, or complete metadata/literal closure is claimed. + +The exact original sources and deletion status are recoverable from the full +campaign snapshot `accepted-campaign-20261029T065509Z` (manifest SHA256 `7eb801592ddc3f5041f7db862052800e86523403784827552ad683c8eb40f0aa`). Its tracked patch, complete new/modified source +archive and per-file manifest are retained together. The accepted chunk records +and independent evidence remain in the original campaign evidence directory. +The complete accepted source tree is also preserved on local branch +`codex/rc-oracle-followup`, commit `db38b4e009146af53e46aeeef3206269f5968af8`. +No follow-up PR is claimed until one is actually created. + +- P09: Effect LEFT-join startup emits a phantom update. The accepted owner has + 152 passes / 64 intended REDs and 5,512 reached cells; its concrete witness + also fails frozen main. +- P10: reserved metadata leaks or is spoofed before correlated-child functional + callbacks. The approved callback law remains normative. The accepted runtime + owner has 377 passes / 203 intended REDs and all 2,153 cells; the bounded + concrete lane has the same seven failures and one compatible pass on main. +- P11/P12: raw undefined becomes null and selected empty records disappear. + Their root/QueryRef runtime owner retains 18 REDs, correlated receiver 72 REDs, + and source-public type receiver 280 REDs. Every declared receiving cell and + accepted calibration remains preserved. These paths are source-unchanged; + a full main-baseline campaign was not executed. +- The appended chunk-24 type receiver retains 672 ordinary-metadata leak REDs + and 12 discriminant-specific union-spread REDs, all 6,240 declared states and + its corrected actual-callback observation/43-control evidence. Static and + runtime observations remain distinct. + +The original 24 nullable-facade/public-output type campaigns and nine controls +remain active on the release branch, as do root/union/join/timeline/facade +metadata regressions, their shared grammar/source/interval models, and the +already-green descriptor/schema/source-ID improvements. The release's cache, +Offline, mutation, publisher, CI and packed-artifact gates remain required. + +The following complete files travel together to follow-up. The appended literal +slice of `subquery-user-value-oracle.test.ts` and appended receiving slice of +`virtual-row-fields-types-oracle.test.ts` are also preserved in that snapshot. +No skipped tests or narrowed expected comparisons implement this partition. + +- `packages/db/tests/query/virtual-row-fields-effect-oracle.test.ts` +- `packages/db/tests/query/virtual-row-fields-input-oracle.ts` +- `packages/db/tests/query/virtual-row-fields-input-driver.ts` +- `packages/db/tests/query/virtual-row-fields-input-oracle.test.ts` +- `packages/db/tests/query/subquery-literal-value-oracle.ts` +- `packages/db/tests/query/subquery-literal-value-driver.ts` +- `packages/db/tests/query/subquery-literal-correlated-oracle.ts` +- `packages/db/tests/query/subquery-literal-correlated-driver.ts` +- `packages/db/tests/query/subquery-literal-correlated-factories.ts` +- `packages/db/tests/query/subquery-literal-correlated-oracle.test.ts` +- `packages/db/tests/query/subquery-literal-types-oracle.ts` +- `packages/db/tests/query/subquery-literal-types-support.ts` +- `packages/db/tests/query/subquery-literal-types-oracle.test.ts` +- `packages/db/tests/query/virtual-row-fields-receiver-types-oracle.ts` +- `packages/db/tests/query/virtual-row-fields-receiver-types-support.ts` + +The coverage map and architecture point here so historical full-campaign claims +below those entries cannot be mistaken for current release registrations. +Before follow-up repair, restore the exact dependent owner/driver/model slices, +reproduce their retained RED and reach, and then supply GREEN/mutant evidence. + +## Further retained receiving and guidance gaps + +P06/P07/P13/P14 prose conflicts are corrected on the release branch. Two +coverage obligations remain separate from that correction: P08 needs the actual +custom-adapter fenced callback to hold an establishing applied receipt before +load settlement; P14 needs the actual lazy child-ready call outcome compared +with the existing source-health model, instead of catching and discarding a +throw. Preserve the accepted owner, fresh-run neighbors and omitted-callback +reach controls before any follow-up production repair. + +Installed declaration emission also exposes an unchanged-main portability gap. +The exact initial RC artifacts and strict installed main `95460bc875` both fail +in ESM and CommonJS for an exported inferred live-query factory (TS2742 twice, +TS4058) and an exported inferred Collection value (TS2742 twice, TS4023). +Private compiler/live types and `LIVE_QUERY_INTERNAL` cannot be named. This +affects libraries emitting declarations for these inferred exports, rather than +ordinary runtime use. Handler admission passes in both formats; it cannot prove +this separate portability law. The release does not repair this broader gap. + +The following original factory witness is preserved verbatim so follow-up does +not depend only on temporary artifacts. Compile its `.mts` and `.cts` forms +with strict NodeNext declaration emission against actual installed tarballs. +Keep the original export unannotated. A separate named public view containing +only readonly `toArray` emits on both cuts, but is not a full-Collection +annotation workaround. Add legal generated row/key shapes and both factory and +inferred-value emission paths to the public-type owner before production repair. + +```text +import { IR, Query, createLiveQueryCollection, eq, include, materialize, toArray } from '@tanstack/db'; +import type { Collection, QueryResult } from '@tanstack/db'; +type LiteralSourceRow = { + id: number; + version: number; + optional?: string | null; +}; +declare const source: Collection; +declare const parents: Collection<{ + id: number; + group: number; +}>; +declare function consume(row: object): void; +declare function consumeChild(row: object): void; +declare function consumeParent(row: object): void; +type __ArrayBase = Array; +export function receiver() { + const selected = new Query() + .from({ row: source }) + .select(({ row }) => ({ id: row.id, version: row.version, value: 0 })); + const __observedQuery = selected; + const __observedCollection = createLiveQueryCollection({ + query: __observedQuery, + getKey: (row) => row.id + }); + type __Observe_finalQuery = QueryResult; + type __Observe_finalCollection = typeof __observedCollection.toArray[number]; + return __observedCollection; +} + +``` + +Original source SHA256 is +`c6c8c1da415901f4935f611387e7f6a3234b5f9753544256f0680038dcc1b50d`. +Current/main final receiving receipts are +`eb157fdc5f567fc04647d206762d794455a998620adc3d9383abf74e0bfc503c` / +`2987feb2620ba1699869b12dbe51c811b6b0e6c389d06a176bf9a417d1827495`. +The original strict emitted factory/value REDs remain required follow-up +receivers. An agent-added narrow `Collection` annotation was an +invalid control on both cuts and supplies no product-failure credit. + +## Live-query mutation input and full-handler receiving types + +The release also defers a public read/write type conflation. Independent source +checking compared current `180b920edd6c88a7024ed407cd04934678c7d5bf` with main +`95460bc875a2259b34eea534e5592a9d0394e73f` using TypeScript 5.9.3. Both already +carry required virtual metadata from a no-select query's published result into +its default mutation input and invariant full handler Transaction/Collection. +The nullable-facade and union-brand repairs do not change this path. Required +readonly metadata on published read rows remains the accepted law; this record +does not authorize removing it. + +A stable public witness configures `onInsert`, then calls +`collection.insert({ id: 'one', label: 'new', rank: 1 })` on a live-query +Collection of that ordinary row. Both revisions reject the business row with +TS2345 because computed virtual fields are missing. Separately, a reusable +`UpdateMutationFn` on explicit live-query configs, core creation and React +config hooks passes when a fresh Program requests diagnostics first, but fails +with TS2322/TS2769 after observing its context/published aliases. This checking +order sensitivity reproduces on both cuts; it is not a stable CLI RED. Ordinary +source handlers, contextual live handlers and the free-result options factory +are positive controls. All 26 final independent Programs have no setup errors. + +Root explicitly retained this public defect for follow-up while permitting a +type-only structural recorder annotation in the JavaScript runtime schema +receiver. That receiver still checks every ordinary handler field and makes no +public mutation-type claim. Its green histories cannot close this separate gap. + +Extend the public metadata type owner and mutation-handler admission owner with +the same authored business row through live-query factories and framework +configs. Compare plain mutation input and reusable full-handler admission while +read results retain required metadata. Add source-public, built/installed and +framework receiving checkpoints; retain ordinary diagnostics and observed-type +checking orders, or a stable source discriminator, before production repair. + +The exact source/diagnostic matrix SHA256 is +`3aac69adf834870f402e3334cdfe372f6f07b924ff5f67fd54e406eba69ef14d`; +the classification report is +`2e60e015e5f42b24027eb53d13863d76c2b2530c01a49616cc746c70952906ab`. +Both remain under the original campaign evidence directory as +`metadata-mutation-classification-matrix.json` and +`metadata-mutation-type-classification.md`. No production repair or bug-class +closure is claimed. + +## Shared-source replay with late include demand + +The release defers a replay publication defect confirmed on release head +`d9911f17b5e9de872cac5c9fd745c656ff50d3ad` and exact main +`95460bc875a2259b34eea534e5592a9d0394e73f`. The primary owner is +`packages/db/tests/query/load-subset-replay-refinement-oracle.test.ts`. +Its native named property `replay.ordered-failed-replay`, seed `521312968`, +path `255:3:3:8:11:9` fails on both cuts at `checkRetention`, after the final +settle: row1 is public while the retained complete window is `[]`. An +independent reviewer reproduced both failures and accepted this classification. +The owner is byte-identical on both cuts (SHA256 +`27f76be612a3058235953e5b984b77ba9274f1aab073035543161b71d4d14e48`). + +The accepted authority is `packages/db/src/query/live/ARCHITECTURE.md`, +“Replay participants and failure,” and normative law 9. A live query's join +and include subscriptions to one source share one publication barrier; demand +started while that source replay remains open must hold the prior complete +result until its current acquisition settles or aborts. Here the include has +no demand at truncate. It acquires and replaces marker demand while the join +replay remains open. Its failed acquisition retires, but its replacement for +key1 is still pending and nonaborted when the final join load settles. The +model has no retained failure at that cut. Publishing row1 therefore violates +the shared-source barrier; a stale model failure does not explain the RED. + +The preserved seven-command witness is: + +```json +[ + { "type": "hold" }, + { "type": "truncateMarkers" }, + { "type": "settle", "ok": true, "keepHolding": true, "newest": false }, + { "type": "settle", "ok": false, "newest": true, "keepHolding": true }, + { "type": "settle", "ok": true, "keepHolding": true, "newest": false }, + { "type": "mark", "id": 1, "on": false }, + { "type": "settle", "ok": true, "keepHolding": false, "newest": false } +] +``` + +Its scenario is descending, offset1, limit2, no ties, no peer, `joined: none` +(anti-join), on-demand markers, includes enabled, aborted marker loads resolve, +and history starts after preload. From each frozen snapshot's `packages/db`, +the actual noninstrumented replay command was: + +```sh +TANSTACK_DB_ORACLE_PROPERTY=replay.ordered-failed-replay \ +TANSTACK_DB_ORACLE_SEED=521312968 \ +TANSTACK_DB_ORACLE_PATH=255:3:3:8:11:9 \ +../../node_modules/.bin/vitest run --config ../../replay-db.config.mts \ + --coverage.enabled=false --typecheck.enabled=false \ + tests/query/load-subset-replay-refinement-oracle.test.ts \ + -t 'keeps the last publication through a failed replay' +``` + +Both runs used Node 24.19.0, Vitest 4.1.12 and fast-check 3.23.2. The scratch +config merges each cut's DB config and aliases DB/IVM to that cut's own source; +tools alone are borrowed. Independent verification compared all 154 DB/IVM +source files with their Git objects, with no differences or foreign source. +This reproduces the named assertion failure, not CI's entire environment. +The original CI log remains under the campaign evidence directory at +`ci-head-d9911f17b/failed-123338231520.log`; the frozen cuts, exact logs, +diagnostic logs and independent review remain in `ci-replay-521312968/`. +The independent report +`independent-retention-review.md` has SHA256 +`a36d5793c87495efb5516ca42ee4adeede867da462bc2e869e6c3698a44b63db`; +its source verification receipt has SHA256 +`7385d6a029caa6daeab3b1d285164df7a8dd3c4598c1e892b4bba0f944b51bde`. + +Before production repair, preserve this replay and add measured reach for a +receiving subscription with no demand at truncate, late demand while a sibling +replay is loading, failure retirement followed by a held replacement, and +success/abort neighbors across multiple subscriptions to the same source. +Then demonstrate the retained-publication comparison before replacement +settlement and after it. The original oracle remains active and unchanged; +there is no skip, narrowed comparison, retry-away, GREEN, or bug-class closure +claim. Its CI RED remains an open baseline defect under this explicit release +scope deferral. diff --git a/docs/contributing/oracle-reviews/2026-10-29-pr-2171-release-gates.md b/docs/contributing/oracle-reviews/2026-10-29-pr-2171-release-gates.md new file mode 100644 index 0000000000..f4c4dba77e --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-29-pr-2171-release-gates.md @@ -0,0 +1,265 @@ +# PR #2171 release gates and deferred investigation + +Recorded on 2026-10-29 after chunk 21 R2 acceptance. Production baseline: +`858b174a65f160d13d01f0324c7481cc8e4eba3a`. The maintainer requires a merge +in about eight hours from 04:50 UTC: approximately 12:50 UTC / 06:50 Denver. +This is a prioritization record, not release approval or a green test report. + +The existing Version Packages action is connected to the custom version and +publish commands. The independent version owner passes 86 histories. The +first-RC changeset inventories 23 candidate packages and the two accidentally +stable adapters, Query and Offline, with their approved minor releases. +Generated API docs remain owned by the separate CI-created PR. + +## Required before merge + +- Integrate current main. At this audit, GitHub reports merge conflicts and + has no full test CI on the current published head. Latest main includes the + Vitest and Zod security upgrades. A read-only merge forecast identifies + conflicts in the root manifest, lockfile and React source-ID test. +- Repair demonstrated publisher failures using the accepted release owners: + P01 permits the wrong candidate dist-tag; P05 attempts duplicate uploads on + retry or an already-current release. These are concrete defects in the + release process, not undefined product behavior. +- Repair PR-introduced cached-server admission and hydration replacement + claim gaps, and restored-sibling transaction cleanup. Their accepted oracles + already demonstrate the failures on unchanged production. +- Complete the Query mutation migration at the documented confirmation path + (G01) and the persisted test handler returning an object (G08). +- Check clean Offline installation and build ordering (G05). Its DB workspace + build edge is absent; an actual fresh-build failure remains unmeasured. +- Validate clean installation/build, initial packed exports and same-release + Query/Offline consumers, then run required CI on the final intended head. + Current workspace links and source-public type tests do not supply packed + consumer evidence. A dependency fetch/setup failure is not a product RED. + +No executable production repair has begun during this campaign. Relevant +oracle checks and real receiving paths remain the gate before each repair. +This deadline does not authorize weakening comparisons to obtain green CI. + +## Preserve separately from the release gate + +Chunks 1–21 are independently accepted as instruments; many deliberately +expose product defects. Acceptance does not mean production passes. The chunk +record and coverage map retain each exact law, boundary, evidence and limit. + +P11 (raw selected undefined becoming null) and P12 (selected empty records +disappearing) have current failing witnesses and source paths unchanged from +the merge-base. Their broader literal-value work is a follow-up candidate; +no baseline runtime campaign has yet established historical behavior. + +P09 (Effect LEFT-join startup publication) and P10 (callback reserved metadata) +are confirmed current failures. Their baseline causality still needs bounded +checking. P10 also spans an explicitly approved callback contract. Keep these +open until that check determines their release disposition; do not label them +undefined merely to defer them. + +Broader literal/type grammars, additional native UI histories, declaration +portability, diagnostic-baseline reconstruction, recorder improvements and +static/work investigations remain separately tracked. The immediate order is +main integration and release-critical gates. No test has been removed or +relaxed by this record, and no follow-up PR has yet been created. + +## Evidence at this checkpoint + +The versioned [chunk record](2026-10-28-pr-2171-oracle-chunks.md) and +[review matrix](2026-10-28-pr-2171-review-coverage-matrix.md) remain the detailed +inventory. Chunk 21 R2 manifest SHA256 is +`da237233542b78a0de9eb836ecbeed12b6b14c2e80f91837eb577c8813bcf0a7`. +Its independent result is 115 passes / 280 intended native type REDs, all +2,550 reach states once, 55 shipped controls and 20 independent controls. +Root independently verified the frozen input/evidence hashes and parsed the +395 unique test titles; root did not rerun that native campaign. + +Scratch audit details, without claims of additional execution: +`/private/tmp/rc2171-oracle-challenge/o22-release-readiness-audit.md` and +`/private/tmp/rc2171-oracle-challenge/release-critical-triage.md`. +All campaign work remains uncommitted at this checkpoint. + +### Main integration checkpoint + +Main `95460bc875a2259b34eea534e5592a9d0394e73f` is integrated in normal merge +commit `5fa6eca05530bb9e1f4748b7c00ae60976da2808`. The root manifest and +lockfile retain the release dependencies alongside Vitest 4 / Zod 4. The React +resolution preserves this branch's removal of legacy dependency-list tests. +The merge commit's lint hooks pass. That is not a full test-CI result. + +Before merging, all 82 dirty/deleted/untracked campaign paths were saved in +an independent hashed file archive and Git stash +`2570e0bd0980f6db12cbdfe58d9a13a0b743779c`. The stash was applied after the +merge and retained as a backup. Restoration checks find all 82 paths present +or deleted as intended. Eighty retain their exact saved bytes; DB's package +manifest takes main's coverage-tool upgrade and the Electric oracle takes +main's constructor-compatible mocks. No library source differs between the +pre-merge and merged commits. Campaign evidence from the older toolchain +remains labeled with its original provenance; it is not silently credited to +Vitest 4. A disposable clean-install/build check is the next receiving gate. + +### Executed clean setup and build gate + +The standalone stage is +`/private/tmp/rc2171-oracle-challenge/gate-a-merged-5fa6eca05/source`. +Its manifest freezes 3,400 source files at the merged commit plus the restored +campaign overlay. The independent reviewer verified every hash, the captured +tracked diff, the absent inherited build/install trees and the confined DB +workspace links. No source/config/lock input changed during execution. + +Two offline frozen attempts failed for missing cached +`@changesets/apply-release-plan@7.0.14`: first in the default temporary store, +then in the explicit existing source content store. These are setup failures. +The exact online frozen install then passed, adding 2,035 packages. It uses +Vitest 4.1.12, Zod 4.6.7, TypeScript 5.9.3 and Changesets 2.29.8. UUID 13.0.2 +installed; no dependency override or downgrade was needed. The original +worktree's installed dependencies remain untouched. Node 24.19.0 differs from +the repository's 24.8.0 declaration, so exact Node parity remains unclaimed. +An example postinstall intentionally treats a missing native build command as +nonfatal; this is not native-host validation. + +The actual `pnpm run build` is RED. Its first topological tier builds DB-IVM +and Offline together; Offline fails with TS2307 for DB's absent declarations. +The measured `ivm-offline` dependency closure contains only DB-IVM and Offline, +despite Offline's installed DB link resolving correctly inside staging. This +is G05's clean-build failure, rather than an installation failure. + +`typecheck:tests` also fails on the missing DB declarations and resulting +cascades. SQLite-core's actual source typecheck independently reproduces G08: +the persisted receipt test's handler returns `Promise<{}>` where +`Promise` is required. Convex's source typecheck and Svelte's native +source check pass; Svelte reports zero errors and warnings. Neither pass +establishes a complete successful build or packed declarations. + +Both executor and independent reviewer accept this evidence classification. +No production repair, versioning or packing ran. Final result and hash records +are in the stage's `evidence/gate-a-final-results.json` and +`evidence/gate-a-evidence-hashes.json`; the latter SHA256 is +`a52ef757bc31346f043f59fee11b72762f808f5bddf84535913c4126eceeab33`. + +### Bounded baseline attribution + +The retained accepted Effect history fails at the same initial phantom-update +comparison on current code and frozen main `95460bc875a2259b34eea534e5592a9d0394e73f`. +The bounded inline-child / QueryRef / structured-projection metadata lane also +produces the same seven assertion failures and one compatible control pass on +both graphs. The execution verifies coherent DB/DB-IVM source resolution and +all loaded source hashes. Whole-matrix reach errors from targeted selection are +recorded separately from the actual law failures. + +These witnesses establish that the concrete P09 and runtime P10 failures are +present on main; they do not attribute every related type or runtime path. +P10's explicitly approved callback law remains distinct from that historical +attribution. No comparison or product law changed. Exact commands, outputs, +source graphs and earlier setup failures are retained under +`/private/tmp/rc2171-oracle-challenge/bounded-main-disposition/`. + +### Deadline scope checkpoint, 2026-10-29 06:00 UTC + +The release is not ready yet. Current main is merged and pushed, all 25 public +packages are represented exactly once in the first-RC changeset, and the +version owner passes. The existing release action invokes the intended version +and publish commands. The migration guide is present in navigation. These +verified parts do not establish successful publication or final release CI. + +The remaining merge gates are the demonstrated publisher failures, introduced +cache/hydration and Offline settlement defects, incomplete mutation migration, +clean-build dependency ordering, CI migration/tooling failures, and final +packed-package and CI checks. Current CI run 40821046528 is red. Its seven +diagnosed groups are retained under +`/private/tmp/rc2171-oracle-challenge/ci-5fa6eca05-40821046528/diagnosis.md`. +Chunk 23 repairs the SortedMap reach sampling failure with independent review; +it has not yet been committed or tested in CI. The mobile deadline receiver's +failure still needs causal diagnosis. + +Finish the active bounded static-metadata chunk 24, then limit further oracle +work for this release to the receiving/recorder checks necessary for these +merge gates. Broader grammar expansion and investigations are follow-up scope +candidates. Preserve their owners, exact counterexamples and evidence; no +comparison is relaxed or silently excluded. In particular, baseline-present +P09/P10 and source-unchanged P11/P12 are not reclassified as undefined behavior. +P10 also implements an explicitly approved law, so any deferred implementation +must identify the omitted promise and preserve its failing receiving witnesses. + +No follow-up split has been applied yet. Before shipping, the final diff must +separate deferred evidence from the release's green comparisons and state the +remaining limitations. Reserve time before the approximately 12:50 UTC merge +deadline for a clean build, packed consumers and required final-head CI; +completion of the oracle work alone is not release readiness. + +The completed read-only audit and dependency-aware split proposal are retained +as `readiness-after-chunk23.md` and `release-followup-partition-proposal.md` +under `/private/tmp/rc2171-oracle-challenge/`. The latter classifies all 85 dirty +paths at its capture and explicitly holds the three actively authored chunk 24 +files. It is a proposed partition, not an applied split or accepted final freeze. +Keep the already-green descriptor/schema receiving improvements if splitting +their shared files would add integration risk. Current mutation-guidance P02 +and P04, plus editorial G07/G11, remain part of the authored migration cleanup. + +Pinned Changesets Action `e0145edc7d9d8679003495b11f87bd8ef63c0cba` source was +retrieved and independently inspected. It captures all custom-command stdout, +scans every `New tag:` line and rereads package manifests after the command +returns. The proposed split publisher preserves those source-level conditions. +No Action execution or final published-package output has been observed. +The proposal remains unexecuted production; the accepted HTTP REDs still block. + +One unchanged CI07 covered-demand history passes locally with exact merged +sources, Vitest 4.1.12, BetterSqlite 12.8.0 and Node 24.19.0 on macOS: one +selected pass, five unselected, 1,121 ms. This is non-reproduction on that host, +not a repair or closure of the original Linux/Node 22 CI assertion. The timing +premise remains unmeasured. Evidence is retained under +`ci07-unchanged-retry-5fa6eca05-kqu_7670/` in the same scratch directory. +All 3,400 Gate A source hashes, prior evidence and critical tool-package hashes +remain unchanged. Vite created an empty borrowed-tools `.vite-temp` directory; +the record explicitly withdraws any claim of zero tool-cache writes. + + +### Oracle phase checkpoint, 2026-10-29 06:55 UTC + +All 26 planned oracle chunks are independently accepted as instruments. This +includes the native Solid/census corrections and generated-error command +receivers; no executable production repair has begun. Chunk 26 R3 preserves +all previous results and adds a calibration rejecting anchors inside fenced +message examples. Its old-tooling result is 27 PASS / 12 assertion RED. The +freeze is `2baf81b3904bf1f8005dee31aef2b0c7b2d46bb2b758825ee59ecffaa3fbce7f`; +independent review is `cf7cc0ca73dd7805df42392213a8f1371fd95fd20b702525bc29eb1b78cb35b6`. +Root verified the frozen source and protected inputs before authorizing repair. + +GitHub still reports the published head mergeable with failing CI. The release +remains blocked by the concrete repair and receiving gates above. Preserve the +full accepted campaign before separating the broader baseline discoveries. +Keep already-green framework receiver improvements to avoid risky shared-file +splitting. Later four-cut artifact campaigns remain unexecuted; the immediate +artifact gate is the actual first-RC CLI and all 25 initial packed artifacts, +with installed Query/Offline consumers. No publication or final-CI success is +claimed by this checkpoint. + + +### Applied release/follow-up partition, before executable repairs + +The complete accepted 93-path campaign is preserved on local branch +`codex/rc-oracle-followup` at `db38b4e009146af53e46aeeef3206269f5968af8`. +The independent archive is `accepted-campaign-20261029T065509Z`, manifest +SHA256 `7eb801592ddc3f5041f7db862052800e86523403784827552ad683c8eb40f0aa`. +The archive includes tracked changes, new owners and deleted-path status. +No generated API docs or generated errors page entered this snapshot. + +Independent inspection verified every archived path against that Git tree, +all 21 proposed transformations and the inverse patch, and 1,701 retained +code paths for imports of deferred files. The applied partition moves 15 +complete broader RED files and their two appended shared-file slices. +Seventy-three dirty paths remain byte-identical; already-green framework +receivers stay intact. Forward patch SHA256 is +`85825e99a43a2d5d05a4e205d54c2893c2cfa2875375e883dca662f8b9743cc3`; +inverse is `aba2a751ffa2038f5e1826b427585461345e44c1cc9543fc35b3687cdd41b044`. + +The retained 505-line public-output type owner preserves all 24 generated +campaigns and nine controls, including existing structured-union-spread +precision laws. Deferring the appended receiver does not retire those laws +or establish that the nullable repair alone makes every retained check green. +The [deferral record](2026-10-29-pr-2171-deferred-oracles.md) identifies each +open class, accepted law and historical RED. The architecture states the +correlated callback limitation. No comparison was weakened or skipped. + +Only after the 26th independent acceptance and this recoverable partition +did executable production repairs begin. They are confined to demonstrated +publisher, cache/hydration, Offline caller/build, nullable facade type, +mutation-guidance and generated-doc ownership defects. Each repair still +requires its unchanged accepted comparisons, GREEN and hostile-mutant evidence. diff --git a/docs/contributing/oracle-reviews/2026-10-29-pr-2171-release-repairs.md b/docs/contributing/oracle-reviews/2026-10-29-pr-2171-release-repairs.md new file mode 100644 index 0000000000..02bc73e9d7 --- /dev/null +++ b/docs/contributing/oracle-reviews/2026-10-29-pr-2171-release-repairs.md @@ -0,0 +1,399 @@ +# RC release repairs after the oracle campaign + +All 26 instrument chunks received independent acceptance before executable +production repairs began. The parent is `5fa6eca05530bb9e1f4748b7c00ae60976da2808`. +The full pre-repair campaign remains recoverable at +`db38b4e009146af53e46aeeef3206269f5968af8`. The +[scope record](2026-10-29-pr-2171-deferred-oracles.md) preserves broader open +classes and their accepted laws. No generated documentation enters this change. + +## Law, reach, RED, GREEN, mutant and limits + +### Publication + +CONTRIBUTING requires stable versions on `latest` and candidates on `rc`. +The independent release ledger judges exact versions, tags, duplicate uploads, +retained successes and raw manifest/prerelease-state restoration. The old +publisher gives 14 HTTP assertion failures and 12 command-receiver tag +failures. Exact-version metadata selection and explicit upload tags make all +167 owner checks and 38 ordinary controls pass. Each of the 29 HTTP histories +reaches its declared metadata, comparison and restoration cuts. Original-code, +wrong-tag and default-selector mutants fail the intended public comparisons. +The commands select the release-history, publish-history and publish-http +oracle runners and ordinary version/publisher/release-fixture tests. This covers actual +npm/pnpm/Changesets against a loopback registry, not public publication or live +Actions authentication. The normal inherited tag output is preserved. + +### Cache admission and hydration + +The approved rule rejects conflicting source/getKey/schema claims under one +cached identity while mounted hooks retain their inline options. Portable +hydration accepts its first local claim, preserves active claims across newer +data, and retires them on failure or consumption. Two production lines enforce +server admission and preserve row options on non-error hydration replacement. +Fresh pre-edit runs reproduce 1,536 admission failures across core and real +React server receivers. Repaired results: 6,556 core and 1,440 React owner checks, +plus 258 neighboring checks, all pass. Declared core routes and real React +server cuts run. Omitting either line restores its intended admission failures; +retirement controls remain green. Commands select the cache options/lifecycle +owners and React cache options/first-registration owners. Arbitrary concurrent +rendering, hydrateRoot, Router transport and other-framework first registration +remain outside this bounded evidence. + +### Restored Offline transactions and the clean build + +A failed core restoration snapshot stays failed even if independent durable +outbox work later succeeds. The caller now avoids externally completing an +already settled snapshot. The core pending-only admission guard stays intact. +The unchanged Offline owner initially has 14 warning-comparison failures. The +repair passes all 87 Offline checks, all 287 core outcome checks and all 33 +persisted-receipt examples. All 220 receiving cuts run once. Restoring the old +caller reproduces exactly 14 intended failures. Three source type checks pass. +The modeled scope is controlled storage/provider delivery with bounded restored +sibling histories; it does not establish native durability or arbitrary retries. + +Offline now explicitly declares its DB workspace development dependency, and +the persisted receipt fixture drops its obsolete object return without changing +its awaited barrier. Lock generation attempts encountered unrelated registry +resolution failures. The final importer move is mechanical, not represented as +pnpm-generated. A separate fresh frozen-lockfile install succeeds against that +exact lock; the actual clean full build, root test typecheck and SQLite core +typecheck pass. No local override or dependency downgrade is included. + +### Published row types + +The retained generated public-output owner independently requires metadata on +an unmatched facade's empty row and discriminant-specific ordinary union fields. +The old implementation fails 24 campaigns: nullable observations and 96 union +spread comparisons. Optional reserved fields are removed only when required +metadata attaches, and brand removal distributes its existing rule per union +member. All 33 owner tests pass, with 70 states reached by each of 24 histories. +The paired runtime owner passes 1,604 checks and six declaration neighbors +compile. Independent review reruns the 33 checks and declaration neighbors. +An isolated original-intersection mutant fails 24 nullable comparisons; an +isolated nondistributive-brand mutant fails 12 union campaigns. Broader callback +type ownership and installed declaration portability remain separate receiving +boundaries. + +### Authored mutation guidance + +The accepted mutation law requires server confirmation before a successful +handler returns. The actual error-handling POST example now awaits explicit +refetch. Current prose removes automatic-refetch and returned-txid advice. +Pre-edit extraction reproduces two settled-row failures. Native Vitest 4 runs +pass 134 Query, 56 Electric and 85 shared-control/type checks. The exact original +POST handler mutant executes and fails the settled-row comparison. Independent +review repeats these results. A constructor-compatible Electric mock changes +only the receiving fixture, preserving its model, histories and comparisons; +its earlier Vitest 4 setup failure is retained separately. This evidence covers +selected actual handlers and controlled collaborators, not live services. + +### Generated error docs and production messages + +Source PRs judge the actual generator against frozen catalogs. The separate +docs PR owns generated-page freshness. Strict read-only stdout and command +wiring repair the old tooling's 12 assertion failures. Exact final-source +results are 39 passes, stdout reception once and catalog comparison once. +Restored-old-generator and command-body mutants produce 10 and two intended +failures respectively. No catalog or oracle expectation changes accompany the +repair. Actual stale-page checks first demonstrate the premise. The unchanged +real ESM/CommonJS erasure script then checks 263 literals, including 22 shared +literals, with development retention and production erasure. Actual Typedoc +generates all 14 output directories with no errors and 15 warnings; the final +generator check passes. All generated files remain confined to disposable +workspaces. This is not a live Actions execution claim. + +## Exact source and receiving status + +The integrated product/input cut freezes 3,393 entries at 07:06:43 UTC, SHA256 +`e3c7f444478fa49412d18f18d44058baf8c2c91cff090f975b5d430d60b49bc2`. +The normal precommit hook then reordered imports in ten test runners. An AST +audit at `6e6034dcb14d35012e1bf41eb035d3c49a52845f` verifies identical import +sets and test bodies: 3,382 entries remain byte-identical, one deletion remains +absent, and ten differ only in import order. Frozen receiving results therefore +do not claim byte equality for those ten runners. CI tests the pushed sources. +The actual initial version CLI succeeds with private existing +GitHub authentication for changelog reads. All 25 actual tarballs validate: 23 +versions at `1.0.0-rc.1`, Query `1.5.0` and Offline `1.2.0`; 156 export targets +and 50 wrong-version/missing-export controls run. Packed consumer runtime and +declaration checks, plus final-head CI, remain pending at this record's cut. +A pnpm direct-file receiving attempt cannot resolve unpublished transitive IVM +from the registry; no runtime/declaration comparison runs, so it supplies no +API GREEN. This is retained as a setup-premise limitation, not a product defect. + +A separate exact frozen stage runs the actual size command against +`origin/main` at `95460bc875a2259b34eea534e5592a9d0394e73f`. The default nine +package selection saves 665 gzip bytes and 499 Brotli bytes. DB grows 215 gzip +bytes while React, Query and Electric shrink. The typical app saves 276 gzip +bytes; all eight app scenarios shrink. Authoritative inputs still match after +execution. An earlier overlapping version/size attempt is discarded. Tests and +contract documentation grow separately from these shipped-byte measurements. + +## Evidence fingerprints + +Raw logs and source/tool manifests are retained under the session's +`rc2171-oracle-challenge` evidence directory. These report fingerprints bind +the recorded outcomes; the executable owners retain their contracts and limits. + +| Report | SHA256 | +| --- | --- | +| Publisher repair | `94fbcd4e3c77657b79ac1aea973d93d5dc5cab97f3529fd861c8c0a436487eca` | +| Cache repair | `f921b66ea01860b6a948ceec134f4dcb04cf45ebc705ae238ca57f4d78f15156` | +| Offline repair | `dac0746f32ce697fb7595dfbad1e1d3b881e765ad5815645e41c0e43f1219174` | +| Published type repair | `876b4f7620fe93fa59d470fe40066d58da4e95cef7cbebbeb4195501a48b7053` | +| Guidance repair | `7fa678d03b118b1e413568292a378255bcfdd7746b64a24c05970204f010ef15` | +| Error-doc receiving checks | `2c94a4b1388014a3329d80b9e8f852f5e6eb0c4e6cd555ca69ebe5948abb2c29` | + +Independent oracle review records all applicable ORC requirements per chunk. +The initial packed instrument has its own bounded independent review and wrong +observation calibrations. Passing package manifests or source-public types +cannot substitute for its still-pending installed API comparisons. + +## Post-push receiving checks and mechanical corrections + +At `6e6034dcb`, every core, Collection-adapter, SQLite and Offline CI test +group passes. Desktop and cloud/mobile E2E pass. The first pushed cut fails +three fixture gates: a local name conflicts with the private-member mangle +inventory; React's native source typecheck loses an inferred query context; +and relocating CI's pnpm shell launcher breaks its relative CLI lookup. +Browser E2E and the corrected final-head CI remain pending at this entry. + +The local array rename passes the actual 1,152-case React owner and the +441-name mangle check. Explicitly deriving the schema receiver's context from +its actual query passes the complete 2,956-case React package and the final +237-case native/focused selection, including 1,728 receiving cuts. Independent +transpilation proves identical emitted runtime code for the context annotation +and alpha-equivalent code for the array rename. Models, histories, comparisons, +cleanup assertions and run budgets stay unchanged. The CI inference failure +does not reproduce locally even before the annotation, so the environmental +cause remains unknown and only a successful actual CI rerun closes that gate. + +P06/P07/P13/P14 authored guidance now matches the existing logical-owner, +release-before-reacquisition and same-run `markReady()` no-op laws. Changes to +`src/types.ts` are confined to its `LoadSubsetFn` comment. Scoped validation and +an exact executable-source comparison pass. P08's actual held-receipt callback +witness and P14's discarded child-ready call-outcome observation remain open; +correct prose does not establish behavioral coverage. + +The posthook AST audit has SHA256 +`92a2a5c7ef45596f27bc669a20b991e7ec7d46529930b7607957fcc7c130223e`. +The schema fixture report is +`892d79f14ff46b2dd14ba8da6033f536d1e0a8ed6f750e68d0a793cd84267090`; +the guidance correction report is +`626e724401575f4149f9857ed886d2274f0c8ab390c60aae537b4de03eccb75c`. +Independent React fixture review retains the original CI RED and locally-green +unchanged baseline rather than claiming a reproduced production repair. + +The HTTP fixture now invokes PATH-selected pnpm at its installed path through a +quoted POSIX `exec` forwarder. Nine ordinary controls pass. The unchanged HTTP +owner passes 35 cases with native pnpm and 35 through a relative shell launcher; +all 29 histories reach each declared state once. Restoring the original +relocation fails the actual relative-CLI setup control. This is setup evidence, +not a new product-law RED. Publisher, models, histories and semantic assertions +stay unchanged. Independent replay accepts the bounded correction. Report SHA256: +`219dc2db851d73af211d377d46165f9233f9435ee96e73a1cc0b0c2b8cbbdb60`. + +### Initial installed package reception and baseline declaration attribution + +The initial candidate artifact cut passes eight installed runtime cells: ESM/CJS × Query/Offline × fulfilled/rejected. The shared mutation-confirmation and restored-settlement comparisons ran through real installed package imports, with 68 checkpoint/control receipts. Both installed declaration formats also pass the shared handler-admission law: 117 candidates and 72 expected negative diagnostics per format, including missing-negative calibration. + +The native npm install used a controlled loopback registry serving unchanged actual release tarballs plus seven explicitly cached external repacks. All 11 installed packages have actual metadata/archive HTTP receipts and exact lock version/origin/SRI checks; selected installed exports match the tarballs. This is native installed reception under a controlled registry premise, without a claim about public-registry archive provenance for external repacks. + +The original exported-inference witness remains RED in both formats. Exporting the inferred live-query factory produces TS2742 twice (private `query/compiler/types` and `query/live/internal`) and TS4058 (`LIVE_QUERY_INTERNAL`); exporting its inferred Collection value produces TS2742 twice and TS4023. A strict installed same-path baseline from exact fetched main `95460bc875a2259b34eea534e5592a9d0394e73f` reproduces the same diagnostics. This broader unchanged-main declaration-portability defect is preserved for followup, with no production repair or full declaration-compatibility claim. An agent-added narrow `Collection` annotation separately produces TS2322 on both cuts and is an invalid control, not the original product defect. + +A separately executed minimal public-view annotation emits without diagnostics in both formats on both cuts: + +```ts +type PublicRows = { + readonly toArray: ReadonlyArray<{ id: number; version: number; value: number }> +} +// receiver is private in this separate control only. +export function annotated(): PublicRows { return receiver() } +``` + +This preserves only the declared `toArray` view; it does not establish a full-Collection annotation workaround. The original factory remains exported and unannotated in its own preserved witness. The independently inferred Offline executor also emits in both candidate formats. + +One supplemental runtime assertion initially expected cleared health slots to be `undefined`; the public `HealthNode` contract requires `null`. That instrument error and original run remain preserved. The authority-corrected assertion received independent review and negative controls before all eight cells were rerun. Initial failed public/direct-file install attempts and a rejected npm update-notifier request in the first baseline lane also remain separate setup evidence, with no product-comparison credit. + +Artifacts: `/private/tmp/rc2171-oracle-challenge/o22-initial-frozen-070643/evidence/final-receiving-classification.json` (SHA256 `b8e2e501b3ca87b90ba76f61772d99b9e7272620efa886df8a82ec67bed4d13d`). Original factory source: `/private/tmp/rc2171-oracle-challenge/o22-initial-packed-support/exported-receiver-repro.mts` (SHA256 `c6c8c1da415901f4935f611387e7f6a3234b5f9753544256f0680038dcc1b50d`). Current/main final probe receipts have SHA256 `eb157fdc5f567fc04647d206762d794455a998620adc3d9383abf74e0bfc503c` / `2987feb2620ba1699869b12dbe51c811b6b0e6c389d06a176bf9a417d1827495`; each includes exact factory, const and control source paths. + +The tarballs derive from frozen input HEAD `5fa6eca05530bb9e1f4748b7c00ae60976da2808`. All 300 frozen production-source files matched committed `6e6034dcb14d35012e1bf41eb035d3c49a52845f` at the recorded continuity check. Later uncommitted LoadSubsetFn guidance comments and fixture-only edits were not rebuilt in this artifact cut. The scope is the initial candidate cut, not the full four-cut release matrix, hosted Actions, production authentication, browser/native hosts or live providers. Offline receiving seeds durable outbox data through the public OutboxManager, then starts a fresh executor; it does not exercise two actual executors creating durable state. + + +## Second CI schema-fixture correction + +At pushed head `180b920edd6c88a7024ed407cd04934678c7d5bf`, the framework +job still rejects the two actual React schema-hook calls with TS2769. The +previous context annotation did not close that gate. Independent source +checking now reproduces both diagnostics after observing the context and hook +return types before requesting diagnostics. The identical cold program passes. +This is a TypeScript comparison-order distinction, not evidence of different +runtime dependencies. The wider public mutation-input/full-handler problem +also reproduces on exact main; its separate follow-up remains open in the +[scope record](2026-10-29-pr-2171-deferred-oracles.md). + +The recorder now declares only the transaction mutation fields it consumes: +key, original, modified and changes. It still records every ordinary payload +field and compares against the unchanged independent model. Original and +corrected runtime JavaScript are byte-identical, SHA256 +`3a560ea22d9bbb4f683b695a245869c8b1e71996e97ca35fac4374b5ae549091`. +Actual corrected React and Preact programs pass cold and after type observation. +Injected source-local TS2322 controls still fail in each checked mode. These +controls establish compiler admission, not a new product-law mutant kill. + +Native React runs pass 237 checks in three files, including 21 typed cases; +Preact passes 80 in three files, including eight typed cases. React live, +ready-source Suspense and Preact each reach all 864 declared cells exactly once. +Both full raw package typechecks and focused lint pass. No model, grammar, +comparison, cleanup, reach requirement, runtime hook or production behavior +changes. Two independent reviewers accept the bounded correction. Actual +final-head CI remains required; these local results do not close public +mutation typing, typed-schema inference, initial suspension or concurrent/SSR +receiving paths. + +Report fingerprints, under the original campaign evidence directory: + +- `schema-180-structural-fixture-final-evidence.md`: + `a3a7710c5420f970b0b7105b780e50e24bebe354b07fd6301d4c4ee180ba3ed2`. +- `schema-structural-driver-independent-review.md`: + `00456d80b6e356a77c93cc2df67f57961d7dbee075413684b9e4c039c9aa2540`. +- `schema-structural-handler-second-review.md`: + `3c8a12ceada729cee932ff7090f486932c65175239fff76a371904cc800d2fd8`. + + +## SQLite generated reach correction + +At `180b920ed`, all 40 random histories complete their public comparisons, +but the campaign fails because none reaches an aborted-only demand. The +post-campaign guard did not preserve its seed. Unchanged native seed 18 +reproduces that reach failure after 40 comparisons; this is an honest grammar +gap, not a production mismatch. + +The original unrestricted history grammar and 40-run budget remain unchanged. +Each normal campaign adds two generated packs of nine legal histories. Every +pack independently requires all 15 existing reach states through the unchanged +production driver, independent model and comparison. The required packs have +their own registered seed/path replay coordinate. Seed-only full campaigns +still enforce reach; direct path replay runs only its selected property. The +native failure formatter now preserves the actual campaign seed and cause. + +The complete native owner passes five tests. Fixed and fresh-random campaigns +each run 40 unrestricted plus 18 required histories and pass in 90–92 seconds. +The formerly failing seed 18 now passes. Direct original and required-property +replays pass separately; independent review observes the required replay's +single guarded witness and all 15 states. Removing abort from the required +pack fails its own reach requirement. Removing the production abort fence in +an isolated host fails the actual offline status comparison: expected loading, +observed ready with a row. Independent review repeats both controls. + +Only the two outer campaign timeouts increase from 120 to 180 seconds to allow +for the additional histories. Internal deadlines, observations, comparisons, +minimum reach and cleanup remain unchanged. Scoped source types, full ESLint, +oracle admission and the run-budget audit pass. No executable production or +model changes enter this correction. Controlled Node SQLite observations do +not establish arbitrary-history, browser or live-provider behavior. + +The author report `demand-reach-final-evidence.md` has SHA256 +`cacc43eac3d276157606aefa09136a479e41e8be7db2b79dd805e60bee48ef68`. +The final source freeze is +`f2d80fbb4d1f2c2b82cbb0451b3d352b5f4cf1240017e63e45c9f6a0a96887ab`. +Both are retained under the original campaign evidence directory. Actual +final-head CI remains required. + +The independent verdict `demand-reach-independent-review.md` has SHA256 +`45c2e7d843a6ebd4c3898957c1b4d698450341beda8af8382b13e72959060e43`. +It binds the copied source hashes, direct replay, both intended control failures +and the two outer timeout literals. + + +## Named replay registration receiving correction + +CI on `d9911f17b` passes 40 of 41 replay controls but rejects the new +`persisted-demand-coverage.required` coordinate as `no-named-owner`. Earlier +direct Vitest replay proved the property and its guard could run but bypassed +the repository's manifest-to-command lookup. The existing independent manifest +check correctly catches this integration omission; its expectations stay intact. + +Adding `required` beside `history` in the existing owner entry repairs that +lookup. No production, model, grammar, guard, comparison or budget changes. +The author reproduces the manifest RED, then passes all 41 existing replay +controls. Author and reviewer each execute the actual repository named replay +CLI through the default SQLite package config at required/seed18/path0. Both +runs emit exactly one successful matching guarded witness with numRuns1. +The reviewer also independently reproduces pre-edit CLI rejection and passes +all six manifest controls. A custom runner alone no longer supplies the claimed +named-command receiving evidence. Hosted final-head CI remains required. + +Author evidence `demand-registration-author-evidence.md` has SHA256 +`3cd8bb335a916fc073a7ae2139aa104e9351ad4f75d22ee99a2c44b8a2c218d9`. +Independent verdict `demand-registration-independent-review.md` has SHA256 +`b22e5109bd08f0436d9fcc62d78de5d26da686584b9a2882e626621f66177385`. +The manifest source hash is +`3f1c724c48171b94413c787021d9d948212bdd28edcd84e783996b86722ac9eb`. +Evidence remains in the original campaign directory. + + +## Formatting continuity and later CI discoveries + +The formatting bot's `d9911f17b` changes only the schema recorder's callback +parameter layout and optional trailing comma. Its TypeScript output differs +only by that comma; minified runtime output is byte-identical. Strict raw-output +hash checks initially reject the formatting difference, and the retained audit +records both hashes and the exact normalized difference. Audit +`release-autofix-d9911f17b-audit.json` has SHA256 `16c36655accb99744919b7e4d97a9e035f6a85b85f13ac2ccf900d4ea9b87f6b`. +No product code changes in that bot commit. + +On this cut, framework and SQLite core CI pass. The replay manifest omission +above and source-health's missing random reach still fail separate gates. +DB core also finds a retained-publication mismatch that independently +reproduces on exact main. Its active oracle remains unchanged, and the +[scope record](2026-10-29-pr-2171-deferred-oracles.md#shared-source-replay-with-late-include-demand) +preserves that open baseline defect. A later passing random run cannot close +its recorded counterexample. Final-head CI and release readiness are separate +judgments from this deferred class. + + +## Source-health structural reach correction + +CI seed -2135407433 passes all 60 original source-health history comparisons +but never selects the opt-in snapshot prefix. The unchanged campaign reproduces +its afterAll RED: snapshot outside reload, markReady after fail and blind insert +of a kept key during reload each have zero reach. The established snapshot, +reload and source-health laws remain unchanged; this is missing sampling reach. + +The additive `source-health.required-history` lanes execute every one of the +nine existing structural prefixes, followed by 0–12 generated existing steps. +Each fixed/random lane runs six cases, independently enforces all 26 declared +states and reaches every state at least six times. Required cases use join or +nested contexts with direct subscribers. The unchanged original 60 random +histories retain all six shapes and both subscriber choices. Models, driver, +public comparisons, named histories, original grammar and budgets stay intact. +Both replay registries and selectors include the new coordinate, preserving +the separately accepted SQLite required mapping. + +Author and independent reviewer each pass the complete 506-test owner and +scoped TypeScript checks. Actual default-config named CLI replay selects the +new coordinate with one matching successful receipt at seed -2135407433/path0; +all four existing coordinates also execute in author checks. Global manifest +controls pass six tests. Prettier, scoped lint, changed-oracle lint and the +441-name mangle check pass. All 154 DB/IVM production source files match d991. +No executable production repair enters this change. + +Omitting a required prefix rejects its lane's missing reach. The fixed-only +ablation fails even when the other lane covers the aggregate. A scratch-only +snapshot mutant preserves begin but merges commit; the guarded CLI rejects +public Collection rows at step3 commit, actual [[1,1]] versus expected []. +Restoring production passes the captured seed/path0:0. The restored formerly +shrunk path reports one property receipt and ten fast-check runs, which are +preserved rather than reported as one history. These are assertion failures, +not setup failures. Independent review repeats both sensitivity checks. + +This closes the bounded sampling gap for the owner's declared premises, not +all source-health, provider, framework or concurrency behavior. Author evidence +`source-health-reach-repair-evidence.md` has SHA256 +`0a711cfeeb1f3e3ef5d6bd4270f2f74a3b3532edb08c4c9790df537cf9a2f62e`. +Independent verdict `source-health-independent-review.md` has SHA256 +`51693ba468e03c347988877d865faf0328bc8a637f2364eafa3a963dd6e80023`. +Both remain under the original campaign evidence directory. Actual final-head +CI remains required, and the independently confirmed baseline replay defect +above stays open under the documented release scope. diff --git a/docs/contributing/oracle-reviews/audit-gap-07-equality-identity.md b/docs/contributing/oracle-reviews/audit-gap-07-equality-identity.md new file mode 100644 index 0000000000..19b95c12cd --- /dev/null +++ b/docs/contributing/oracle-reviews/audit-gap-07-equality-identity.md @@ -0,0 +1,128 @@ +# Equality identity oracle review + +Reviewed executable commit: `7f8617a474a8fa94c30586869c4695e819ac069b`. +The review checked GAP-07 from the code-weight audit. Its sole claim was a +missing agreement test between D2 equality-value keys and query IR equality +operands. The code review also found that the first generated grammar lacked +unequal Date, Temporal, and byte pairs. Both coverage gaps are fixed in the +reviewed commit. No product mismatch appeared in the tested domain. + +## Law and evidence boundary + +Two values belong to the same D2 equality key exactly when their equality +operands have the same query IR identity. The pair grammar declares equality +before it calls either identity path. It checks both keys and the compiled +equality predicate when each key is constructed. Nullish predicate results are +`null`, although the two identity keys remain distinct. + +The fixed cases cover signed zero, NaN, invalid Date, Date/timestamp, nullish +values, BigInt, infinities, reserved-looking strings, empty binary values, +Temporal types and dates, typed-array constructors, symbols, and functions. +The generated cases vary primitive values, valid Date timestamps, Temporal +dates, byte content and length, and nested references. Fixed and sampled pairs +do not establish universal agreement for arbitrary JavaScript values or hash +collision freedom. Ref proxies are expressions, not literal values here. + +## Oracle guide audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 Contract | `getEqualityValueIdentity` promises the equality relation for keyed state. Query IR equality operands preserve that relation for query reuse. The bounded domain and omissions appear above and in the coverage map. | +| ORC-002 Independence | Each pair carries its expected relation before production runs. The expected result does not call `normalizeValue`, either identity path, or the predicate evaluator. | +| ORC-003 Responsibilities | The oracle file states the law, pair model, value grammar, two key drivers, predicate driver, and key-construction check together. | +| ORC-004 Grammar | The fixed cases reconstruct the audit's signed-zero and binary examples. Equal and unequal generated Date, Temporal, and byte pairs distinguish value equality from broad collapsing. The bounded Date range stays 1,000 ms inside valid endpoints. Temporal days vary within January and cross a month boundary. Byte lengths span 1–140, with empty bytes fixed. Removing an axis loses its type boundary. Ref proxies are excluded because the IR treats them as expressions. | +| ORC-005 Path and observation | The check calls the real D2 key path, the real IR demand-key path, and `compileExpression`. It observes key equality and predicate output immediately after construction. | +| ORC-006 Calibration | An IR signed-zero mutant failed the fixed signed-zero assertion. A constructor-sensitive binary mutant failed on empty Buffer versus Uint8Array. A binary-content collapsing mutant failed the generated IR-key assertion on Buffer `[0]` versus Uint8Array `[1]`. Each result was an assertion failure at the intended checkpoint. The mutants were reverted. | +| ORC-007 Campaign and replay | The package oracle command includes the file. Fixed seed `20260928` and a seedless campaign use the same property and 160-run budget. The named replay manifest owns `query-identity.equality-partition`. Guarded replay of seed `20260928`, shrink path `1:0:0`, selected the property and reproduced the binary-content collapsing mutant. | +| ORC-008 Model state | Not applicable. The pair relation has no state or next action. | +| ORC-009 Vocabulary | `EqualityPair` is a model-only description of two values and their declared relation. D2 key and IR operand key name the two production observations. | +| ORC-010 Failure fidelity | The property has no acquired resource to clean up. Fast-check shrank the binary-content mutant to one differing byte and kept the IR-key assertion as the primary failure. Guarded replay retained the same binary-content failure. | +| ORC-011 Second formulation | The declared relation checks each identity independently. The compiled predicate supplies a third observation of the same declared pair without serving as the model. This detects a shared identity error against the declared relation. | +| ORC-012 Review record | This record names every applicable requirement, the reviewed executable commit, controls, limits, and replay evidence. | + +## Verification + +- The final oracle file passed 32 tests. The neighboring identity tests and + named replay manifest checks passed in the focused run. +- TypeScript, ESLint, Prettier, and `git diff --check` passed on the reviewed + executable commit. +- The three temporary mutants failed by assertion. None timed out or failed + during setup. + +## External-review follow-up + +Reviewed executable commit: `f83aaedb9cf267f1997152641442bc6f29bce453`. +The earlier record above applies to `7f8617a4`. The later PR head +`ff90c360` added only that record. The external review then called the PR +test-only and clean with zero findings. The production diff remains zero, but +the zero-finding verdict missed four oracle evidence gaps: + +- **ORC-004:** The generated string branch originally compared a string only + with itself. A mutant that collapsed all primitive strings to one D2 key + passed all 20 equality checks. The first repair added unequal strings but + only with different lengths. A first-character-plus-length key mutant then + passed all 40 equality checks. The final grammar includes empty/equal strings, + different strings, and same-prefix, same-length unequal strings. The fixed + `ab`/`ac` pair and generated unequal pairs both reject that mutant. +- **ORC-005:** Fixed pairs now run a public live-query `groupBy`. After initial + publication, the check compares group count and row multiplicity with the + declared pair relation. Under the first-character-plus-length mutant, the + `ab`/`ac` public result had one group instead of two. The generated key check + still observes internal identity, and no end-to-end subscription test proves + subset-demand reuse. The coverage map assigns that witness to this owner. +- **ORC-007:** The original replay evidence omitted its command and test-name + filter. The direct command below selected one property execution. With the + first-character-plus-length mutant it reported `failed:true`, at the D2-key + assertion, for seed `20260928` and path `8:0:0`. After reverting the mutant, + the same command reported `failed:false`. +- **ORC-012:** This entry identifies the exact executable commit reviewed for + the follow-up. Any subsequent record-only commit does not change that code. + +From `packages/db`, the direct replay command is: + +```sh +TANSTACK_DB_ORACLE_PROPERTY=query-identity.equality-partition \ +TANSTACK_DB_ORACLE_SEED=20260928 \ +TANSTACK_DB_ORACLE_PATH=8:0:0 \ +node --import tsx tests/oracle-replay.ts \ + tests/query/identity-output-shape-oracle.test.ts \ + -t 'agrees across the supported value grammar, seed=undefined' \ + --maxWorkers=1 --coverage.enabled=false +``` + +The fixed-seed mutant campaign failed after nine cases and shrank to +`left: " "`, `right: " a"`. The guarded replay reached one execution and +preserved the same failure class. All temporary mutants were reverted. +The clean oracle passed 56 tests, the guarded replay and manifest suite passed +27 tests, and ESLint, Prettier, and `git diff --check` passed. The additional +public path covers the fixed value pairs at initial publication; it does not +close the mapped subset-demand reuse cell. + +## Main merge verification + +Reviewed merged executable commit: `d4f0507892584a2e59e96baea498989c41a2ae21`. +The merge kept the query-identity coverage note and main's SQLite coverage +notes. It changed no query-identity oracle or runtime source from the +follow-up reviewed above. At the merged commit, the oracle passed 56 tests, +the direct seed `20260928` and path `8:0:0` replay completed one execution, +and Prettier and `git diff --check` passed. The next record-only commit adds +this entry and changes no executable code. + +## Current-main merge verification + +Reviewed merged executable commit: `c795bf24f417ac6342e8c45c19e5175d8a625e13`. +This normal merge brought in `origin/main` at `63362dc8` and retained both +the query-identity coverage note and the newer coverage and oracle +registrations from main. It made no query-identity runtime or oracle changes. +The merged oracle passed 56 tests. Direct seed `20260928`, path `8:0:0` +replay selected one property execution and reported `failed:false`. ESLint, +Prettier, and `git diff --check` passed. + +For calibration, I temporarily changed the D2 equality key for primitive +strings to either one constant or the first character plus length. Each +mutant made the generated seed check fail at the D2-key assertion. Each also +made the public `ab`/`ac` grouping check show one group instead of two. A +direct replay of the first-character-plus-length mutant selected one property +execution and reported `failed:true` at the same-prefix string assertion. +Both mutants were removed after their RED runs. The later record-only commit +changes no executable code. diff --git a/docs/contributing/oracle-reviews/basic-index-nullish-build.md b/docs/contributing/oracle-reviews/basic-index-nullish-build.md new file mode 100644 index 0000000000..5ab6dc279e --- /dev/null +++ b/docs/contributing/oracle-reviews/basic-index-nullish-build.md @@ -0,0 +1,152 @@ +# BasicIndex build with a nullish value + +At PR head `175adce0cb32e29e5838853c821ee5c56fe0089a`, a focused runtime +witness built a direct BasicIndex from `undefined` and `1`. The default +comparator places `undefined` first. `BasicIndex.build` returned +`takeFromStart(2) = [one, undefined]` and an empty inclusive upper range +bounded by `undefined`. Incremental `BasicIndex.add` and both BTreeIndex paths +returned `[undefined, one]` and the expected range. A recording comparator +received no calls during the BasicIndex build. The build path is also reached +when `Collection.createIndex` builds an index over existing rows. + +The accepted law is the default ascending, nulls-first order documented by +`DEFAULT_COMPARE_OPTIONS`, `defaultComparator`, and the direct `IndexInterface` +contract. The primary `index-update-oracle.property.test.ts` already models +nullish positions with a source Map, but its nullish history grammar previously +used add/update/remove only; its generated build grammar used numeric values. +The smallest missing cell is `undefined` plus `1` through `BasicIndex.build`, +observed with `takeFromStart(1)` and an explicit upper range. A two-order +fixed build history and generated nullish builds now use the same Map checker +after each returned action. The Map distinguishes exact null and undefined +membership while its ordered projection treats them as one comparator +position. Raw duplicate source keys are resolved before build; mixed value +classes and custom comparators remain outside this claim. + +## RED evidence before the production change + +The new fixed oracle witness failed on the unchanged implementation: +`BasicIndex nullish direct index refinement > build preserves nulls-first +ordered reads and ranges` compared `takeFromStart(1)`, receiving `[one]` +against source-model `[undefined]` at the intended observation checkpoint. +The BTreeIndex version passed. The command's focused run had one failed test, +one passed test, and no type errors. This is a product mismatch, not a setup +failure or a changed expectation. + +Native `Array.sort` moved a raw `undefined` element to the end without invoking +the supplied comparator. `BasicIndex.build` now sorts wrapper records, so every +stored value reaches the configured comparator, then unwraps the sorted values. +The fixed two-order build history and generated put/delete/build histories +pass through the same independent Map checker after each returned action. + +## GREEN evidence and hostile review + +The complete direct index oracle passed 412 tests with no type errors after the +repair. The fixed history includes both source insertion orders and a later +build with `undefined`, `null`, and a defined value. The generated grammar can +replace an existing source Map through build. The fresh hostile reviewer +`/root/review_nullish_build_oracle` inspected the original RED witness, the +model, both index implementations, and the repair. It found no blocker for the +bounded direct-index law. It did not claim that a Collection query using a +newly built index has been observed publicly. + +The oracle's model derives exact membership and sorted source keys from the +source Map, not from either index. A build that leaves `undefined` last fails +`takeFromStart(1)` at the returned-action checkpoint. Range and cursor probes +also compare against the Map after each action. The direct-index law excludes +raw duplicate-entry builds, mixed string/number order, custom comparators, +Collection queries, and work complexity. Its fixed and generated build cells +do not establish every possible source history. The exact committed-head +review is recorded in a later entry. + +## Exact committed-head hostile recheck + +The reviewed commit is `9a145fba130f27c914a4a1fd4f44c5871a917483`. +`/root/exact_review_index_build` read the source-isolated index retirement +ledgers, the index oracle and coverage map, the direct index implementations, +and the default comparator and interface contracts. The commit preserves the +earlier RED witness: its parent still sorts raw values, so native `Array.sort` +moves `undefined` behind `1` without calling the comparator. The repaired +build sorts objects that hold the values; this invokes the checked comparator +for that pair and leaves equal comparator positions stable. This recheck +inspected the parent implementation and the recorded RED run; it did not +execute the parent commit again. + +The committed direct index oracle passed 412/412 tests with no type errors. +The guarded replay of `index-update.nullish-membership` at seed `20261012`, +path `0`, reached both index classes for 100 runs each and passed. `git show +--check` passed, and the worktree was clean after those runs. No new unit test +was retired by this build repair. The earlier retirement ledgers still delimit +the assertions removed from their source files. + +| Requirement | Exact-head result | +| --- | --- | +| ORC-001 | `DEFAULT_COMPARE_OPTIONS`, `defaultComparator`, and the direct `IndexInterface` methods support default ascending, nulls-first returned results. The interface lists methods but does not itself spell out this order. The claim is limited to direct default-option reads over `undefined`, `null`, and positive numbers. | +| ORC-002 | A source Map owns exact key/value pairs. Its `nullishPosition` projection freshly sorts and filters rows without production normalization, comparator, index storage, or traversal. | +| ORC-003 | Opening and adjacent oracle prose states the law and limits, Map rule, legal put/delete/build histories, direct BasicIndex/BTreeIndex path, returned arrays and sets, and the after-action comparison cut. | +| ORC-004 | The fixed trace reconstructs `undefined` plus `1` in both build insertion orders, a put after build, then a four-key replacement with `undefined`, `null`, and `1`. Generated histories use three keys, five values, 1–40 actions, and builds of at most three entries. Removing build loses the full-sort path; removing `undefined` loses the native-sort distinction; removing defined values loses the nullish boundary; removing replacement loses stale-owner detection. Duplicate generated keys are resolved by the Map before build. Raw duplicate-entry builds, other value domains, and direct updates of absent keys are excluded. | +| ORC-005 | The oracle invokes real direct `build`, `add`, `update`, and `remove` methods, then compares exact membership sets, complete forward range sets, and ordered key arrays after each returned action. `takeFromStart(1)` observes the original defect. No public Collection query is claimed. | +| ORC-006 | The recorded unchanged-production run failed at the `takeFromStart(1)` refinement comparison, with `[one]` instead of `[undefined]`; this is an assertion failure at the intended cut. The repaired version passes. No additional production mutant was run in this recheck. | +| ORC-007 | Fixed-seed and seedless lanes share the generated action grammar, recorder, comparison, and 100-run budget. The exact-head guarded seed/path replay reached two 100-run executions. The earlier round-4 ledger records a captured failure replay for this named property, before the build action changed its arbitrary. A captured failing seed/path for the revised arbitrary was not independently checked in this recheck; that replay-fidelity cell remains open. | +| ORC-008 | The existing Map retains key/value ownership because later put and delete actions distinguish owners. Build adds no independent model state; it replaces that Map. | +| ORC-009 | Model `put` maps to add or update according to Map ownership, `delete` to remove when owned, and `build` to a full replacement. The model-only comparator position combines null and undefined for order while exact membership keeps them separate. | +| ORC-010 | The action loop has no external resources. The guarded replay retains its named witness and removes its private temporary channel; the recorded original failure remains distinct from a setup or cleanup failure. | +| ORC-011 | No plausible fault shared by this bounded source-Map rule and production was identified. Nullish position follows the accepted default comparator contract; mixed domains and custom comparison need separate laws. | +| ORC-012 | This append-only entry ties the exact-head review, executed GREEN result, inspected RED path, guide audit, and limits to the reviewed commit. The revised-arbitrary failure replay named under ORC-007 is the remaining conformance evidence gap. | +| ORC-013 | The two-entry `undefined`/`1` build reaches the law's premise. The first fixed observation rejects the plausible wrong rule “native sort may place undefined last”; reversing source insertion order distinguishes an accidental insertion-order pass. | +| ORC-014 | No controlled provider or host premise supports a claim about real-provider behavior. Direct index results are the stated boundary. | + +There is no blocker for the bounded direct-index code or its source-Map +comparison. This result does not establish Collection query selection after +`createIndex`, custom comparator behavior, mixed string/number ordering, raw +duplicate-entry builds, all range shapes, or work complexity. The revised +arbitrary's captured-failure replay remains to be recorded before claiming +complete ORC-007 conformance for the expanded generated property. + +## Revised-grammar failure replay calibration + +This follow-up closes the ORC-007 evidence gap above for the oracle at reviewed +commit `9a145fba130f27c914a4a1fd4f44c5871a917483`. A temporary wrong +expected result was inserted after the nullish action checker: whenever a +generated `build` returned, it required `takeFromStart(1)` to equal the +impossible key `calibration-only-missing-key`. This deliberately faulty +expectation did not modify production or the action arbitrary. + +The seedless generated lane failed at the returned-build endpoint comparison +for BasicIndex after one test, seed `1770427226`, shrink path `0:0`. Its +counterexample was one empty build; the actual endpoint was `[]`, while the +temporary expectation was `[calibration-only-missing-key]`. BTreeIndex also +failed at the same temporary assertion in its seedless lane. Guarded direct +replay of `index-update.nullish-membership` with the BasicIndex test selector, +seed `1770427226`, and path `0:0` reached exactly one property execution. Its +witness recorded one run and `failed: true`; Vitest and the replay guard exited +nonzero with the same endpoint assertion. This is an assertion failure at the +intended comparison, not a setup error or timeout. It demonstrates replay +fidelity for the revised arbitrary; the empty-build case is a replay control, +not the nonempty nullish-order product witness. + +After removing the temporary expectation, the same guarded seed/path replay +reached one BasicIndex execution, recorded `failed: false`, and passed with no +type errors. `git diff --exit-code -- +packages/db/tests/index-update-oracle.property.test.ts` confirmed that the +oracle file again matched the reviewed commit. No calibration fault remains. +The preceding ORC-007 and ORC-012 gaps are closed by this executed evidence. + +## Transfer-only narrowing + +The later production refactor groups each normalized build value during the +existing entry pass. It removes the intermediate entries array and keeps the +wrapper sort that fixes the nullish position. A fresh hostile recheck read +the source Map model, original unit ledgers, and current build code. Its +focused index, work, and Collection-index run passed 535 tests with no type +errors. The recheck found no lost direct-index assertion after the grouping +change. It did not rerun the original wrong-sort mutant or extend the law to +raw duplicate source keys or custom callbacks within one normalized value. + +At exact code commit `24a61a8d5b6cef06d488c490e39f351b4d55fb68`, a +fresh hostile reviewer rechecked the one-pass build against the seven retired +index law families and the nullish fixed history. The source Map, wrapper +sort, and direct observations remained intact after the commit hook. A +focused index, work, and Collection-index run passed 535 tests with no type +errors. The reviewer found no in-scope loss. This recheck did not rerun the +original wrong-sort mutant; the earlier RED run remains the sensitivity +evidence. diff --git a/docs/contributing/oracle-reviews/bug-11-temporal-group-keys.md b/docs/contributing/oracle-reviews/bug-11-temporal-group-keys.md new file mode 100644 index 0000000000..914ec6c602 --- /dev/null +++ b/docs/contributing/oracle-reviews/bug-11-temporal-group-keys.md @@ -0,0 +1,221 @@ +# Temporal group-key oracle review + +## Reviewed state and contract + +- Base: `33a194941c8d51f8f98babb999fef2987dd6ff8b` (`origin/main`). +- Reviewed executable head: `6d3e58ec`. +- Owner: `packages/db-ivm/tests/temporal-group-key-oracle.test.ts`. + +The user confirmed that db-ivm `groupBy` must keep different Temporal values in +different groups. This repair uses db-ivm's established hash key domain: a +Temporal kind and its string representation identify a key. The oracle checks +the public grouped result after one input batch. It covers the eight Temporal +kinds recognized by hashing, with two different values and matching fresh +values for each kind. The model counts those descriptors in a plain Map. + +The original serializer treated each Temporal value as an empty plain object. +On the base commit, a public `groupBy` of January 15 and June 15 PlainDates +emitted one group with count 2. The new oracle failed at that public checkpoint +for all eight kinds: it observed one count-2 group where it expected two +count-1 groups. Eight matching-value controls passed. A direct serialization +control also failed because a Temporal value and `{}` both encoded as `{}`. + +The fix shares the existing Temporal discriminator with the serializer. The +serializer uses a tagged kind-and-value tuple for Temporal values at any +supported structural depth. The existing hash rule is unchanged. The repair +adds seven net production source lines; tests and contract records are separate. + +## Oracle requirements + +| Requirement | Outcome | +| --- | --- | +| ORC-001: authority and limits | Pass. The explicit user decision permits Temporal group keys. Existing db-ivm hashing defines the bounded kind-and-string domain. The oracle and coverage map name the one-batch checkpoint and exclusions. | +| ORC-002: independent judgment | Pass within that domain. A plain Map counts fixture values by Temporal kind and string. The model calls no db-ivm hash, serializer, or operator. The established hash domain is the separately justified semantic base; native `.equals()` is a distinct unresolved policy. | +| ORC-003: visible responsibilities | Pass. The opening states the law and limits. `temporalCases` is the bounded grammar, `expectedGroups` is the model, `observedGroups` drives public D2 `groupBy`, and the tests compare after `graph.run()`. | +| ORC-004: generated grammar controls | Not applicable. The oracle enumerates a fixed matrix; it does not claim generated-history coverage. Every listed kind runs distinct and matching cases. | +| ORC-005: path and observation | Pass. The driver imports the package entry point and observes every group output. It compares group identity and count. Its recorder keeps each emitted row; it cannot hide the one-group collision by overwriting a key in a Map. | +| ORC-006: checker calibration | Pass. The unchanged serializer was the plausible wrong design. It reached public `groupBy` and failed eight value assertions, not setup or timeout. The object control failed separately. | +| ORC-007: campaigns and replay | Not applicable. No important generated property is added. The fixed cases are individually named and directly runnable. | +| ORC-008: stateful-model minimality | Not applicable. Expected groups come from stateless recomputation of one batch. | +| ORC-009: vocabulary mapping | Pass. A fixture value is a db-ivm relation key inside a record. The model's descriptor combines the established Temporal kind and string identity; it is not a production hash or serialized key. | +| ORC-010: failure and cleanup | Not applicable. The in-memory graph has no acquired external resource or shrinker. The failing assertion preserves the observed group and checkpoint. | +| ORC-011: independent second formulation | The review identified native `Temporal.equals()` as a plausible different equality policy. No db-ivm contract selects it. The coverage map retains this exact decision; an `.equals()` oracle would impose new behavior outside this repair. | + +This record supplies ORC-012 evidence for executable head `6d3e58ec`. + +## Verification and remaining scope + +The focused oracle changed from 9 failed and 8 passed tests on the base commit +to 17 passed on the repaired head. The full db-ivm suite passed 576 tests in +40 files. Package TypeScript, changed-file ESLint, Prettier, Vite build, and +`git diff --check` passed. Vitest ran with at most two threads. + +The oracle does not cover incremental retractions. Symbols and cyclic keys +remain unsupported by the serializer. Mutable `RegExp.lastIndex` does not +belong to this Temporal law. An independent reviewer reproduced another +boundary: two ZonedDateTimes with `[UTC]` and `[Etc/UTC]` satisfy native +`.equals()` but have different kind-and-string keys. The coverage map records +the native-equality decision. This PR does not change that hash behavior. + +## External-review follow-up on `f25fff0d3eeb655075ecc9743d693c541f7bf92d` + +The review of PR #1919 found that the first oracle model classified inputs with +the same `Symbol.toStringTag` and `toString()` calls as production. This could +hide a collision between a real Temporal value and a plain object with a +spoofed tag. On starting head `c08a3201b3cf166ac25494302ea92ea1d8b2bcb6`, +a plain object with an own `Temporal.PlainDate` tag, a non-enumerable date-like +`toString()`, and an `extra` property entered the Temporal branch. Public +`groupBy` emitted one count-2 group for that object and a genuine PlainDate. +The new oracle case failed at the group-count assertion: expected two groups, +observed one. The original model would have expected that one group too. + +The follow-up model counts fixture-assigned identity classes. It no longer calls +`Symbol.toStringTag` or `toString()` to derive expected groups. The detector now +requires the accepted tag to exist on the input's prototype. The same oracle +case passes, and a second case proves that a genuine Temporal value with its +own matching tag still joins its matching group. This check prevents the +original plain-object collision without treating a matching own tag as proof of +Temporal identity. The entire db-ivm suite passed: 578 tests in 40 files. +TypeScript, changed-file ESLint, Prettier, Vite build, and diff checks passed. + +| Requirement | Follow-up outcome | +| --- | --- | +| ORC-001 | The law remains two distinct group-key identities and one matching identity after one batch. The established kind-and-string domain stays the authority. | +| ORC-002 | Fixture labels define expected classes. The model does not use the production Temporal classifier. | +| ORC-003 | The opening contract, `temporalCases`, `expectedGroupCounts`, `observedGroupCounts`, and assertions expose the five oracle responsibilities. | +| ORC-004 | Not applicable: the finite Temporal-kind matrix is not a generated property. | +| ORC-005 | The public `groupBy` output supplies group counts after `graph.run()`. The recorder also checks positive weights and unique emitted keys. | +| ORC-006 | The original detector survived the old classifier but failed the new spoofed-object case at the public group-count checkpoint. The repaired detector passed the same case. | +| ORC-007 | Not applicable: no generated property or replay campaign changed. | +| ORC-008 | Not applicable: the reference counts one stateless input batch. | +| ORC-009 | Fixture labels represent expected group-key identity classes. They do not represent a production hash or serialized key. | +| ORC-010 | Not applicable: the in-memory graph has no acquired resource or shrinking path. | +| ORC-011 | The fixture-label formulation is independent of the kind-and-string classifier. It rejects the named shared fault. | +| ORC-012 | This addendum records each applicable requirement for the exact follow-up head. | + +The current detector still accepts a custom prototype that advertises a +Temporal tag and supplies a matching `toString()` method. A direct probe found +that such an object still serializes to the same key as a genuine PlainDate. +The coverage map owns that remaining counterfeit-prototype witness and the +native/polyfill brand decision. No claim of complete spoofing resistance follows +from this change. Also, `Temporal.Duration.compare(PT1H, PT60M)` returns zero, +but `groupBy` emits two count-1 groups under the established string-key domain. +The coverage map retains that equality-policy decision with the ZonedDateTime +alias example. + +## Second review on `b9b59c089dd574b5eed7672a66b6768808837919` + +The second review challenged the remaining custom-prototype case named in the +coverage map. A plain object can inherit a `Temporal.PlainDate` tag and a +matching `toString()` from a custom prototype. With an own `extra: 1` field, +this object is a supported structural value distinct from a genuine PlainDate. +The current public `groupBy` merged those two rows. A new primary-oracle case +failed at `graph.run()`: expected group counts `[1, 1]`, observed `[2]`. +This is an in-scope counterexample to the plain-object versus Temporal group-key +identity law. It keeps the broader tag-spoofing class open. The RED case remains +local and uncommitted while the compatibility policy is undecided; the PR must +not gain an active failing test without its repair. + +A trusted brand method can distinguish this counterfeit. The local +`temporal-polyfill` `PlainDate.prototype.toString.call(value)` rejects it. +Node 24's native Temporal method also rejects it. Under `--harmony-temporal`, +the native method accepts genuine values from a separate VM realm for all eight +recognized Temporal kinds. The local polyfill method rejects a genuine +PlainDate from a separately loaded polyfill copy with `TypeError: Invalid +calling context`. Current `groupBy` accepts that cross-copy value and merges +it with a same-value local polyfill PlainDate into one count-2 group. Thus a +local-polyfill-only brand gate would break an observed public behavior. + +The smallest policy-dependent options are: + +1. Trust the runtime's native Temporal constructors and one imported + `temporal-polyfill` copy. Call each trusted prototype method on the input. + This preserves native cross-realm and local-polyfill values. It cannot + authenticate a separately loaded foreign polyfill copy. Treating that copy + as an ordinary object could merge distinct foreign dates with no enumerable + fields, so this option needs a declared unsupported boundary or an error. + It also needs a runtime polyfill dependency and roughly 15–25 additional + production lines before simplification. The final count needs the diff. +2. Add a trusted registration path for other polyfill copies. A registered + constructor supplies its brand-checking prototype method. This can preserve + cross-copy grouping after registration, but requires a new public capability + and an explicit decision about unregistered values. The extra registry, + lifecycle, and API need more production code than option 1. +3. Fail immediately for untrusted Temporal-like inputs. This prevents a silent + collision, but it rejects valid plain objects that advertise a Temporal tag + and does not preserve existing cross-copy grouping. + +Reading a constructor or method from the candidate object is not a sound brand +check: a counterfeit can supply both. JavaScript exposes no generic Temporal +internal-slot check that accepts arbitrary independent polyfill copies without +a trusted intrinsic or registration. The user is deciding whether separately +loaded polyfill copies must remain supported. Until that decision, this review +item is `confirmed-open`; no production change, changeset, PR-body update, or +push follows from this review. Production-code weight for this second review is +zero. The Duration alias-equivalence decision remains separate and unchanged. + +Reviewer assessment for this second finding: technically accurate and high +signal. It reached the public boundary and named a residual case that the +coverage map already owned. The request for a sound cross-realm brand check +exposes a real compatibility choice, so the proposed fix is incomplete until +that choice is made. This review sample supports a positive reviewer +recommendation, with the policy dependency stated explicitly. + +## Full rereview: own-only Temporal tag on `b9b59c089dd574b5eed7672a66b6768808837919` + +R19-4 proposed that a genuine Temporal value with only an own +`Symbol.toStringTag` would fail the current prototype-tag gate. This state does +not arise from ordinary native or `temporal-polyfill` construction. It is still +reachable through legal JavaScript operations. For both implementations, the +probe created two equal `PlainDate` values, added an own matching tag to one, +then moved that value to `Object.prototype`. It installed the implementation's +intrinsic `toString` method as a non-enumerable own property. The intrinsic +still returned `2024-01-15` for the changed value, proving that its Temporal +brand survived the prototype change. + +An own matching tag with the original prototype remained recognized by +`isTemporal`. With the changed prototype, `isTemporal` returned false and +`serializeValue` produced `{}` instead of the genuine date's Temporal key. +Public `groupBy` emitted two count-1 groups for the equal dates, while the +fixture-defined Temporal identity law expects one count-2 group. The same +result occurred with the local polyfill and Node 24 native Temporal under +`--harmony-temporal`. The claim is technically true for a valid, uncommon +operation. The current contract says Temporal values are valid group keys and +does not exclude prototype changes. The broader trusted-brand decision from +the second review also owns this case. No stricter gate or RED test is pushed +while the user decides the supported cross-copy polyfill domain. + +The rereview also called `PT1H` versus `PT60M` separation "by-design." That +phrase describes the existing kind-and-string key rule, but it overstates the +alias policy. The coverage map explicitly says whether equal-comparing +Durations should coalesce needs a separate contract decision. The first review +record makes the same distinction. Therefore the current behavior is documented, +but the alias-equivalence policy is open. This review does not change it. + +Full-rereview ledger: two source claims. R19-4 is `confirmed-open` with the +public groupBy probe above. The Duration wording is a `design-decision` with +its destination in the Temporal group-key coverage map. Neither item caused a +production edit, test commit, PR-body change, or push. Production-code weight +for this rereview is zero. Reviewer assessment: R19-4 identified a real but +uncommon legal state, while the "by-design" summary was too strong. The +technical signal is good; the review should keep documented behavior separate +from an approved product decision. + +## Product scope decision + +The maintainer chose ordinary native Temporal values or one polyfill copy as +the supported group-key inputs. Code that replaces a Temporal prototype or +creates a custom prototype that impersonates Temporal is outside this contract. +Separately loaded polyfill copies are also outside it. The RED probes above +remain evidence of those behaviors; they do not establish an in-scope product +bug under the chosen domain. The active oracle keeps its ordinary own-tag +plain-object control and does not include a failing altered-prototype case. + +The maintainer also chose to keep alternate representations separate. +`PT1H` and `PT60M` therefore remain separate groups even though +`Temporal.Duration.compare()` returns zero. This follows the established +kind-and-string key rule. The coverage map and oracle opening now state both +decisions. No production code or new runtime dependency is needed. + +The updated oracle checks a `PT1H`/`PT60M` pair at the public `groupBy` +checkpoint. The 20 Temporal oracle tests and db-ivm TypeScript check pass. diff --git a/docs/contributing/oracle-reviews/change-event-batch-order.md b/docs/contributing/oracle-reviews/change-event-batch-order.md new file mode 100644 index 0000000000..8f28ed309c --- /dev/null +++ b/docs/contributing/oracle-reviews/change-event-batch-order.md @@ -0,0 +1,75 @@ +# Change-event callback order decision and oracle review + +- Reviewed code head: `63cc7122560cefbcfed7187abab7924c71302eff`. +- Starting head: `33a194941c8d51f8f98babb999fef2987dd6ff8b`. +- Source: DEC-01 in the code-weight audit's `BUGS_AND_ORACLE_GAPS.md`. +- Primary owner: `packages/db/tests/change-event-history-oracle.test.ts`. + +## Decision and finding ledger + +The maintainer decided that messages for different keys may appear in either +order within one `subscribeChanges` callback. Messages for the same key retain +their causal order. The public method and `ChangeListener` documentation now +state this rule. + +| ID | Source claim and proposal | Technical verdict | PR action | Durable value | +| --- | --- | --- | --- | --- | +| DEC-01.1 | `commitNextPendingTransactionBatch` uses direct pending set order for `changedKeys`. STATE-10 may reorder callback messages. Compare batches as bags if order is free, or pin the order if promised. | Confirmed reordering risk. Cross-key order is free by maintainer decision. A whole-batch bag would erase same-key causal order. | Document the scoped contract and compare an ordered trace for each key. | The primary oracle and coverage map own the per-key law. STATE-10 may change cross-key order. | + +The audit note identified a real boundary and asked for a decision before +changing tests. Its proposed alternatives were useful. The bag proposal needed +the same-key qualification above. Reviewer assessment: technically accurate, +high signal, and appropriately cautious about contract authority. Hire +recommendation: yes for this class of review. + +## Reach and checker controls + +A controlled public probe held a local transaction, queued and canceled a sync +update for key 1, then completed direct edits of keys 2 and 1. The parent +upsert map held `[1, 2]`; the direct set held `[2, 1]`. The final callback +ended with `update:2, update:1`. This confirms that STATE-10 can change public +cross-key order. The old owner passed 376 tests when a temporary production +mutant reversed final sync events. It did not judge that order. + +The new fixed history commits three sync transactions while publication is +deferred. One callback contains insert, update, and delete messages for key 1, +and insert plus two updates for key 2. A plain Map derives prior and final row +values. The checker compares each key's ordered message trace, including type, +value, prior value, and multiplicity. It does not compare positions across keys. +It checks that publication produces exactly one callback and that final public +rows match the model. + +On the reviewed code head, the owner passed 377/377 tests. Two temporary +production mutants challenged the new comparison: + +| Mutant | Result at the intended callback | +| --- | --- | +| Reverse the complete deferred publication list | **Assertion failure.** Key 1 arrived as delete, update, insert instead of insert, update, delete. Key 2's update sequence also reversed. | +| Reverse each sync transaction's final event list | **Equivalent under this law.** The focused test passed 1/1 because only distinct-key positions changed. | + +Both mutants were removed. The original code and final oracle pass together. +The focused witness covers two keys, three immediate sync transactions, and +one deferred callback. It does not establish every adapter, cancellation, +reentrant callback, or arbitrary deferred history. + +## Oracle guide review + +| Requirement | Outcome | +| --- | --- | +| ORC-001 authority and limits | Pass. The maintainer decision and public API comments authorize the per-key law. The fixed history and limits appear above and in the executable owner. | +| ORC-002 independent judgment | Pass. A plain Map applies declared actions. It does not import Collection state, event composition, or key ordering. | +| ORC-003 visible responsibilities | Pass. The opening states the contract. `applyModelOp` and `expectedEventTrace` give the model. Fixed rounds give the grammar. Real sync commits and `_deferPublication` drive production. `perKeyEventTrace` compares the public callback. | +| ORC-004 generated grammar controls | Not triggered by the new fixed history. Existing generated local-mutation campaigns are unchanged. The fixed history uses legal absent-key inserts and present-key updates/deletes. | +| ORC-005 path and observation | Pass. The test reaches public `subscribeChanges`, observes zero callbacks before publication and one after, then compares per-key messages and final public rows. | +| ORC-006 checker calibration | Pass. Complete reversal fails at the per-key comparison. Distinct-key reversal passes at the same checkpoint. Neither is a setup failure or timeout. | +| ORC-007 fixed/random replay | Not triggered by this fixed witness. The owner's existing paired generated campaigns ran in the 377-test owner suite. Their replay interface is unchanged. | +| ORC-008 model-state minimality | Pass. The Map must retain each key's prior value to predict update and delete messages. Each per-key trace must retain order and duplicates. Different prior values or a reversed trace change the next public observation. | +| ORC-009 vocabulary mapping | Pass. One fixed round maps to one sync transaction. The model's `Op` is a declared sync action. Deferred publication combines three transactions into one callback. The Map is a reference model, not Collection state. | +| ORC-010 failure and cleanup | Pass. The shared cleanup helper preserves the primary mismatch and aggregates secondary failures. The driver releases its deferral, subscription, and Collection. | +| ORC-011 second formulation | Not triggered. No plausible fault shared by the simple Map and the production event derivation was named. | +| ORC-012 review evidence | This record gives the exact reviewed code head, applicable outcomes, non-applicable reasons, mutant classifications, and scope. | + +The loss audit finds one raw DEC-01 item and one disposition: resolved by a +design decision and oracle law. No item lacks evidence. This PR changes no +production event ordering. Future work on generated deferred histories belongs +to the change-event history owner before claiming wider batch-shape coverage. diff --git a/docs/contributing/oracle-reviews/code-weight-btree-trim.md b/docs/contributing/oracle-reviews/code-weight-btree-trim.md new file mode 100644 index 0000000000..ae38b2aa52 --- /dev/null +++ b/docs/contributing/oracle-reviews/code-weight-btree-trim.md @@ -0,0 +1,183 @@ +# Code weight: vendored BTree trim and range-scan law + +Reviewed executable revision: `067fbd94` (base `ae2eb3fb`, the fetched +`origin/main` at review time). This record follows in a documentation-only +commit. The reviewed revision changes `packages/db/src/utils/btree.ts` and +`packages/db/tests/btree-map-oracle.test.ts`. + +## Change + +`BTreeIndex` uses these B+ tree operations: `get`, `set`, `delete`, `clear`, +`minKey`, `maxKey`, `forRange`, `nextHigherPair`, and `nextLowerPair`. The +trim removes vendored `sorted-btree` features that nothing calls: `editRange`, +early-break callbacks, callback counters, in-place value edits, the `overwrite` +flag, and reused result arrays. It also removes the right-sibling shift in +`BNodeInternal.set`, which cannot run (see "Unreachable shift" below). `has()` +and the `get()` fallback stay because the Map oracle observes membership +through them. + +The change is intended to preserve behavior. It does not change the behavior +of a comparator that returns `NaN`. That existing defect is tracked in +[#1945](https://github.com/TanStack/db/issues/1945). + +Full `@tanstack/db` entry, esbuild minified, db-ivm inlined: + +| Revision | min | gzip | brotli | +| --- | ---: | ---: | ---: | +| `ae2eb3fb` | 376,495 | 106,289 | 90,043 | +| `067fbd94` | 375,072 | 105,834 | 89,692 | +| Δ | −1,423 | −455 | −351 | + +## Contract, path, and limits + +The owner is `packages/db/tests/btree-map-oracle.test.ts`. Its contract: for +numeric keys, the B+ tree is a Map whose traversal and neighbor operations use +numeric key order. The model is an independent `Map` plus a sort. It does not +use the tree's nodes, search, or traversal code. + +Before this change, law 2 checked only a full scan with `includeHigh: true`. +`BTreeIndex.rangeQuery` calls `forRange(from, to, toInclusive, …)` with +arbitrary bounds and both `toInclusive` modes. This change extends law 2. After +each checked cut, the refinement check scans five bound pairs, each with +`includeHigh` true and false: + +- a window around the probe that starts and ends between keys; +- the point range `[probe, probe]`; +- a reversed pair (`low > high`), which must be empty; +- the second and second-to-last model keys, which exercise the inclusive and + exclusive edges on existing keys; +- the first model key as a point range. + +Each scan must return exactly the model keys in `[low, high]`, or in +`[low, high)` when `includeHigh` is false, once each, in order, with the +modeled payloads. The observation records every visited key. Duplicates, +omissions, and order changes are therefore visible. + +Limits: + +- Keys are integers from 0 to 199, and node sizes are 4 to 8. The oracle does + not cover custom collation. The ordered-acquisition and index refinement + owners cover that. +- The oracle does not check balance, depth, or space. Node merging is not + observable through the Map contract. See N4 below. +- Comparators are consistent. Inconsistent comparators, including ones that + return `NaN`, are outside this law (#1945). + +## Unreachable shift + +`BNodeInternal.set` chooses `i = min(indexOf(key, 0), children.length - 1)`. +That is the first child whose max key is at least `key`. An internal node keeps +`keys[j] === children[j].maxKey()`. The removed right-sibling shift required +`cmp(child.maxKey(), key) < 0`. With a consistent comparator, that is true +only when `i` was clamped to the last child, and the last child has no right +sibling. Upstream `sorted-btree@1.8.1` has the same selection line and the same +unreachable branch. A non-transitive comparator can reach the branch. Without +the branch, the insert splits the full child instead of shifting an entry. The +tree stays valid, and Map observations are unchanged. + +## Grammar controls and calibration + +The change adds observations to the existing refinement check. It adds no new +generator. The existing histories reach the new checks at every cut in the +fixed long campaign (every 97 steps and at the end), in every cut of the +fast-check property, and in the dense growth, retirement, and reuse cases. + +- **Reconstruction:** each named wrong design below is reached by the existing + histories and rejected at a range cut. +- **Range:** empty trees use probe-derived bounds, because `sorted[1]` and + `sorted.at(-2)` are undefined there. One-key trees make the edge pairs + coincide. +- **Exclusion:** the reversed pair checks that `low > high` yields no keys. + +### Source mutants on unchanged production (`ae2eb3fb` BTree) + +Each mutant ran against the original oracle and the extended oracle. The source +was restored after each run. + +| Mutant | Original oracle | Extended oracle | +| --- | --- | --- | +| M1: public `forRange` passes `includeHigh: true` | survived (9/9 passed) | assertion failure (8 tests) | +| M2: leaf ignores `includeHigh` | survived | assertion failure (7 tests) | +| M3: leaf scan starts at the leaf's first key, ignoring `low` | survived | assertion failure (7 tests) | +| M4: a `low === high` leaf scan also includes the next key | assertion failure (8 tests) | assertion failure (8 tests) | + +### Source mutants on the trimmed tree + +These ran against the extended oracle plus `btree-index-work.test.ts` and +`btree-index-undefined-values.test.ts`. They ran on the trimmed tree before the +point-range shortcut was restored and before the right-sibling shift was +removed. Neither later change touches the mutated lines. + +| Mutant | Outcome | +| --- | --- | +| N1: leaf delete splices keys but not values | assertion failure (1 test) | +| N2: leaf delete does not update the size | assertion failure (7 tests) | +| N3: leaf scan ignores `includeHigh` | assertion failure (8 tests) | +| N4: internal delete skips `tryMerge` | survived: equivalent within the Map domain | +| N5: internal delete keeps empty children | assertion failure (6 tests) | +| N6: no root collapse after delete | assertion failure (5 tests) | + +N4 changes balance, not the Map relation. It belongs to performance evidence, +not to this law. + +### Executable controls + +`rejects a range scan that ignores includeHigh` and +`rejects a range scan that ignores the low bound` wrap the real `forRange`. Each +wrapper keeps the full scan correct, so only the new range checks can reject +it. The expected error must name a `forRange(low, high, includeHigh)` cut. +As a sanity check, replacing the `includeHigh` wrapper with an honest +pass-through made that control fail, because the refinement check no longer +threw. + +## Performance + +An A/B harness ran the base and trimmed trees on identical deterministic +workloads: bulk insert, get, overwrite, delete, range scans in both +`includeHigh` modes, full scans, neighbor walks, and churn. It used 1k, 20k, +and 200k keys, 15 to 41 interleaved rounds, and median times. An A/A run of the +base tree against a copy of itself measured a noise band of about ±2% to ±4.5%. + +- The first trim was 5% to 9% slower on delete and churn at 20k to 200k keys. + The cause was the removed single-search path for point ranges. The trim now + keeps that path in the leaf and internal `forRange`. +- After that, raw point operations remained 3% to 5% slower in both slot + orders, although their code is logically unchanged. Small range scans were + faster. +- At the `BTreeIndex` level (add, update churn, remove, `eq` lookup, + `rangeQuery`, `take`/`takeReversed`, at 20k and 100k rows), every workload was + within ±4%. The sign changed when the slots were swapped. No index-level + regression was measurable. + +The harness is not checked in. + +## ORC-001 through ORC-012 + +| Requirement | Result | +| --- | --- | +| ORC-001 | The oracle's opening comment states the Map contract, the four laws, and the extended law 2. This record states the limits above. | +| ORC-002 | Expected range results come from filtering the sorted model keys. The model does not import tree search or traversal. | +| ORC-003 | The contract, model (`stepModel`), histories, production driver (`stepTree` and the public `BTree` API), and refinement check (`expectRefinement`, `expectRange`) are visible in the oracle file. | +| ORC-004 | No generator changes. Reconstruction, range, and exclusion for the new observations appear above. Ablation: each bound pair targets a distinct edge, and M1 to M3 show that the partial and edge pairs are necessary. | +| ORC-005 | Every check calls the public `BTree` methods that `BTreeIndex` calls, and compares exact ordered keys and payloads at each cut. | +| ORC-006 | Source mutants and executable controls, with outcome classes, appear above. | +| ORC-007 | Existing gap, not changed here. The fixed LCG campaign and the fast-check property are different properties, so they do not form the fixed-and-random parity pair. The fast-check lane has no documented seed and path replay interface. This change only adds checks to the shared refinement check. | +| ORC-008 | The model state does not change. | +| ORC-009 | Model terms (put, delete, read, clear, range) map directly to `BTree` methods. | +| ORC-010 | The fixed campaign wraps failures with the law name, seed, round, node size, and history, and keeps the original cause. Range failures name the failing bounds. No external resources are acquired. | +| ORC-011 | No shared semantic fault calls for a second formulation. The Map model is already structurally independent. | +| ORC-012 | This record ties the reviewed revision, outcome classes, controls, limits, and performance evidence to the coverage map. | + +## Verification + +On `067fbd94`, the following passed: + +- `packages/db` Vitest, typecheck off: 191 files, 6,821 tests. +- `pnpm test:oracles`: 48 files, 2,831 tests. +- `pnpm test:minified-db`: 98 error names, index metadata, query rows, and live + updates. +- `packages/db-sqlite-persistence-core`: 340 passed, 1 todo. +- `tsc --noEmit -p packages/db`: no errors in `src` or in the changed test. The + existing `tests/conformance` errors also occur on `ae2eb3fb`. + +The environment was Node `v24.19.0` and Vitest `3.2.4` on Darwin arm64. diff --git a/docs/contributing/oracle-reviews/code-weight-draft-proxy.md b/docs/contributing/oracle-reviews/code-weight-draft-proxy.md new file mode 100644 index 0000000000..fa69043373 --- /dev/null +++ b/docs/contributing/oracle-reviews/code-weight-draft-proxy.md @@ -0,0 +1,595 @@ +# Code weight: simplify the draft proxy and share its equality walker + +Reviewed executable revision: `556cbd8ff` (base `18abceee4`, the fetched +`origin/main` at review time). This record follows in a documentation-only +commit. + +- `a6445a7c2` adds the draft revert oracle and the clone and `deepEquals` + laws on unchanged production code. +- `639ed5a1e` extends the revert oracle after a refactor mutant survived. The + extension also passes on unchanged code. +- `6b68b8dc3` is the refactor. +- `cccaf1b9e` returns functions stored in a draft as stored, with a new + native-differential law. This is a behavior change and a lost-write fix. +- `1a07cd3ce` applies review fixes: a stronger revert grammar and witness, a + duplicate comment, and a redundant `key in` check in `getChanges` (about 14 + minified bytes). +- `fcc353d5d` through `a49ae90e3` fix the bugs that a second review found. Each + fix extends an oracle first. See Fixes from the second review. +- `31b2a6bd4` removes draft code that only bought speed. See Code weight over + speed. +- `b825e6483` fixes a CodeRabbit finding in the `defineProperty` fix: a + non-configurable property with an object value threw. See Fixes from the + second review. +- `0ce101acf` fixes two more lost-write classes from CodeRabbit review: + accessor definitions and typed-array mutator methods. +- `cbd2aff06` and `556cbd8ff` act on a loss audit of these oracles against + `docs/contributing/oracle-rubric.md`. They fix one bug, make the array and + typed-array laws generated, and close the audit's grammar, checkpoint, and + replay gaps. +- `7e5dfee25` fixes three bugs that code review found in the earlier fixes: + class write-backs, writes after `Object.freeze`, and `subarray` reads. + +## Change + +`packages/db/src/proxy.ts` builds the drafts that `collection.update` and +`withChangeTracking` pass to callbacks. + +- The write-only `proxyCache`, the symbol-key branches over `assigned_`, and + the one-use `createObjectProxy` wrapper are deleted. Every writer of + `assigned_` stores a string key (`String(prop)`, `String(index)`, or `''`), + so the symbol branches could not run. +- The custom array iterator is deleted. Arrays bind the native + `Array.prototype[Symbol.iterator]` to the draft, so each element read takes + the `get` trap and its parent edge. `draft[Symbol.iterator]()` now returns a + native Array Iterator. +- The `set` trap's revert path sets `modified` and calls `checkParentStatus` on + its own tracker. That function already clears tracking and continues up the + chain when every value is reverted. +- `draftValuesEqual` becomes a `draft` mode of `deepEqualsInternal` in + `packages/db/src/utils.ts`. Draft mode keeps Map and Set order, RegExp + `lastIndex`, and array holes. The general mode does not change. + +- A function stored as data (a field, an array element, a nested object + member) is now returned as stored. See Stored functions. + +The audit prototype also rewrote `deepClone` as one `Reflect.ownKeys` loop. That +loop made every draft 8% to 17% slower, so the refactor first kept the +original two-loop form. `31b2a6bd4` adopts the shorter loop. See Code weight +over speed. + +| Consumer bundle (esbuild, `es2020`) | min | gzip | brotli | +| --- | ---: | ---: | ---: | +| full public API | −1,445 (−0.41%) | −343 (−0.33%) | −108 (−0.12%) | +| collection with a filtered live query | −1,445 (−0.54%) | −363 (−0.46%) | −279 (−0.41%) | +| local-only collection with an insert, an update, and a delete | −1,441 (−1.18%) | −350 (−1.00%) | −223 (−0.72%) | +| local-only collection with one insert | −1,441 (−1.18%) | −350 (−1.00%) | −227 (−0.73%) | + +Each row bundles the built `dist`, with private members renamed, for base and +`31b2a6bd4` under the same `package.json`. The refactor alone (`1a07cd3ce`) +saved 1,739 minified and 453 gzip bytes on the full public API. The fixes from +the second review added 917 minified and 303 gzip bytes. `31b2a6bd4` then +removed 623 minified and 193 gzip bytes of speed-only code. + +## Why the oracle work came first + +Sixteen source mutants on unchanged code model plausible mistakes in this +refactor. Before this work, eight of them passed the whole db suite (7,596 +tests). Seven are real gaps: + +- A partial revert treated as a full revert, which drops the remaining change + (P2). +- A nested write under a symbol key treated as no change (E5). +- `deepClone` copying non-enumerable string keys, or dropping enumerable or + non-enumerable symbol keys (C1, C2, C3). +- `deepEquals` comparing RegExp `lastIndex`, or treating array holes as + differences (D2, D4). + +The eighth, a wrong parent edge for array iterator elements (P4), is +equivalent under every public observation. See Mutant calibration. + +A ninth (`deepEquals` comparing Maps in order) failed one unrelated test only. + +### The draft revert oracle + +`packages/db/tests/proxy-revert-oracle.property.test.ts` generates write +histories on a draft of a three-field row. An operation sets a field to a new +value, reverts it to its original, deletes it, writes a nested property (a +string or symbol key), writes an array index, or writes a row property through +`for...of`. Values are primitives (including `-0` and `NaN`), Dates, RegExps, +Maps (with primitive or Set values), Sets, arrays with holes, objects with a +symbol key, and arrays of rows. + +The model applies the same operations to plain-data specs and compares values +with its own encoding of draft equality. It never reads a draft. After each +history, the check asserts: + +- `getChanges()` holds exactly the fields whose final value differs from the + original, each with that value, and deleted fields as `undefined`. +- The draft reads back the model's final value. +- The original row is unchanged. + +The generator also emits a revert of a field that is currently changed, so +most reverts run the `set` trap's revert branch. A second property builds +partial reverts directly: it changes two or three fields in any order, then +reverts all of them but one. + +Each property runs a fixed campaign (seed `2026101`) and a random campaign: +400 general histories and 200 partial-revert histories. +`TANSTACK_DB_PROXY_REVERT_SEED` and `TANSTACK_DB_PROXY_REVERT_PATH` select a +replay. A positive witness requires the fixed general campaign to reach every +operation, a change made only under a nested symbol key, and effective +reverts: at least 10 histories where a change survives a revert, and at least +30 histories that end fully reverted. A revert counts only when the field +differs from its original before the revert. Eleven pinned histories hold one +rule each. + +Review of the first version found that its witness also counted reverts of +unchanged fields, which write an equal value and do nothing. With those +excluded, the first grammar reached 10 partial and 11 full reverts in 400 +histories. The changed-field revert and the partial-revert property raise +that coverage. Mutant N2 now fails 6 of the oracle's 16 cases, including both +partial-revert campaigns. + +Limits: + +- Map, Set, and array mutator methods (`set`, `add`, `push`) mark a value + changed without a revert check. The native-operation tests in + `proxy.test.ts` own them. +- Top-level symbol writes are outside the grammar. `getChanges()` does not + report them, and the coverage map lists symbol writes as unsupported. + +### Clone key classes and `deepEquals` order rules + +- `proxy-detachment-contract.test.ts` pins that a draft copies a row's own + enumerable string keys and every own symbol key, enumerable or not, and + does not copy non-enumerable string keys. This is current behavior, not a + promise. +- `utils.property.test.ts` adds four generated laws: `deepEquals` ignores Map + and Set insertion order, RegExp `lastIndex`, and array holes. These are the + rules draft equality keeps, so a shared walker must not leak one mode into + the other. + +## Stored functions + +The `get` trap bound every function it returned to the private copy, so it +could call built-in methods such as `Map.prototype.get`, which reject a Proxy +receiver. That binding also applied to functions stored as data: + +| Read on a draft of `{ handler: f, fns: [f], obj: { g: f } }` | `main` | refactor before the fix | now | +| --- | --- | --- | --- | +| `draft.handler === f` | false | false | true | +| `draft.fns[0] === f` | false | false | true | +| `[...draft.fns][0] === f`, `for...of` | true | false | true | +| `draft.obj.g === f` | false | false | true | + +On `main`, array iteration was the only read path that kept identity, because +the custom array iterator did not take the `get` trap. The native iterator +does, so the refactor first lost identity there too. + +The binding had a worse effect: a stored method saw the private copy as `this`. +A method such as `bump() { this.count++ }` then changed the copy without +tracking, and `getChanges()` reported no change. Inside `collection.update`, +that write was lost. + +`cccaf1b9e` returns an own data function as stored. A call then sees the draft +as `this`, so its writes take the `set` trap. Inherited Array, Map, and Set +methods keep their draft handling. + +`proxy.test.ts` adds a native-differential law. Each probe runs on a native row +and on a draft of an equal row, and the results must be equal. The probes are +field access, array index, `for...of`, spread, `includes` and `indexOf`, an +array callback, a nested field, `Object.values`, a Map value, a Set member, a +call, a function assigned during the callback, and `this` inside a stored +method. One more case checks that a write through `this` appears in +`getChanges()`. The law fails 8 of 14 cases on `main` and 10 of 14 on the +refactor before the fix. + +| Mutant on `cccaf1b9e` | Owners | +| --- | --- | +| F1: every function is bound again | assertion failure (10/455) | +| F2: inherited methods are returned unbound too | assertion failure (103/455) | +| F3: the own-property check reads the original row | assertion failure (1/455) | + +## Fixes from the second review + +A second review found one regression in the refactor and several bugs that +`main` also has. Each fix extends an owner first, so the new law fails before +the fix. Bytes are for the full public API, minified and gzip, against the +commit before. + +| Commit | Bug | Owner law | Fails on `main` | Bytes | +| --- | --- | --- | --- | ---: | +| `fcc353d5d` | A nested add-then-delete, a fully reverted nested object beside a changed sibling, and a key added as `undefined` left wrong changes. | Revert oracle: nested delete op and nested round-trip property. | 6 of 24 cases | +138 / +49 | +| `36030ef8f` | The refactor cloned a typed array with `new Ctor(source)`, so a subclass that does not forward its argument cloned empty. A typed array of `NaN` never equaled itself. | Revert oracle: `Float64Array`, `Uint8Array`, and a subclass, with index writes. `utils.property`: typed elements compare like numbers. | NaN cases only | +133 / +53 | +| `967a28c1d` | The native iterator bound to the draft made `for...of` over 200 numbers 4 times slower than `main`. | Iteration contract: arrays visit and publish what a native array does, with an edit while the iterator is open. | none (performance) | +293 / +93 | +| `5b4049cdf` | General mode allocated a draft entry list for every Map. | Existing Map mutants N6, N11, N14. | none (performance) | −5 / +5 | +| `b57d60022` | `at`, `slice`, `concat`, `flat`, `toReversed`, `toSpliced`, and `with` returned raw elements, so a write through them was lost. A search for a draft element failed, and `constructor` was a bound copy. | `proxy.test.ts`: every non-mutating method of `Array.prototype`, against a native array. | 15 of 39 cases | +38 / +16 | +| `38d8ba78f` | `Object.defineProperty(draft, key, { value })` threw unless the descriptor set `writable`. | `proxy.test.ts`: six definitions against a native row. | 3 of 6 cases | +8 / +8 | +| `815432ebd` | Two URLs, or two instances whose state is in private fields, were equal, so a draft dropped a write that replaced one. | Revert oracle: URL values and a private-field class, with a write-twice property. `utils.property`: class and identity law. | both campaigns | +312 / +79 | +| `b825e6483` | `38d8ba78f` defined a clone, so a non-configurable object value broke the Proxy invariants and threw. `get` would also have wrapped it. | `defineProperty` law: a new key with only an object value, with identity where the invariants require it. | throws on `main` too | +9 / +5 (module only) | +| `0ce101acf` | Defining a getter over a field reported no change. `fill`, `set`, `sort`, `reverse`, `copyWithin`, and writes through `subarray` on a typed-array draft changed the private copy without a change. | `proxy.test.ts`: every method of `TypedArray.prototype` against a native typed array, and two getter definitions. | 6 of 31 methods, 2 of 2 getters | +70 / +25 (module only) | +| `7e5dfee25` | The class rule made writing back the row's own class instance a change, because draft snapshots hold class instances as plain objects. After `Object.freeze(draft)`, a nested write was lost. A `subarray` read counted as a change. | Revert oracle: a keyed class instance in original rows. `proxy.test.ts`: rows after freeze, seal, or a fixed key against native rows, and the typed-array law with and without a write. | 7 oracle cases and 3 pinned cases on `ca0a2170c` | about +160 / — (proxy and utils modules) | +| `cbd2aff06` | `sort`, `reverse`, `fill`, and `copyWithin` on a draft returned the private copy, not the draft. `main` has the same bug. | N: generated rows record whether a method returned the array itself. | the four self-returning mutators, on arrays and typed arrays | small (one comparison) | +| `556cbd8ff` | No product bug. R's grammar could not write back the row's own object, the frozen-key boundary had no nearby witness, and the two-step callback property had no fixed campaign or replay. | R: same-object reverts. T: sealed and read-only configurable keys. `proxy.test.ts`: campaigns and replay. | F4 survived before the read-only configurable case | 0 | +| `a49ae90e3` | None. A detached stored method must throw, as on a native row. | `proxy.test.ts` stored-function law. | passes | 0 | + +Design decisions for these fixes: + +- **Typed-array class.** Draft equality treats a typed array of another class + as a change. `deepEquals` still ignores the class, as `utils.test.ts` has + pinned since #434. +- **Array read methods.** Every non-mutating method reads through the draft. + `b57d60022` first ran searches, `join`, `keys`, and `toString` on the copy + for speed. `31b2a6bd4` removed that list. Running the element methods on the + copy and then replacing elements with drafts was faster but cost 167 more + gzip bytes. +- **Classes and keyless objects.** In both modes, instances of two different + classes differ. A plain or null-prototype object from any realm compares by + keys with any class, because draft snapshots and plain JSON hold class + instances as plain objects. (`815432ebd` first made a plain object differ + from a class instance; `7e5dfee25` reverted that after review found that + writing back the row's own instance became a change.) + A class instance without enumerable keys equals only itself. URLs compare + by `href`, because a draft copies a URL by its `href`; by identity, an + untouched URL would differ from its own snapshot. + +The law that takes its methods from `Array.prototype` closes a whole class: a +new built-in method fails the law until it has arguments. Its last case also +caught the bound `constructor`. + +Limits that remain: + +- A draft reads a class instance of the original row as a plain object, so a + private-field getter reads `undefined`. The detachment contract owns that + boundary. The revert oracle writes class instances but never starts with + one. +- A built-in method called through `this` on a nested Map or Set draft, such + as `Map.prototype.get.call(this.m, key)`, throws, because the receiver is a + Proxy. This is a Proxy limit. +- Reading an object under a frozen or fixed key of a draft counts that key as + changed, because the Proxy invariants require the raw copy. The row may + publish an equal value. +- Writing back a keyless class instance of the original row counts as a + change, because the snapshot holds it as an empty plain object. +- `DataView` setters on a draft are not tracked. No owner generates a + `DataView`. +- A mutator method that changes nothing, such as `sort` on a sorted array or + `add` of a present Set member, counts as a change, so the row publishes an + equal value. Arrays, Maps, and Sets did this on `main`. Typed arrays do too + since `0ce101acf`. This is a deliberate limit: a revert check after mutators + would cost bytes to avoid a publish of an equal value. + +| Mutant | Owners | +| --- | --- | +| T1: typed clone by constructor argument | assertion failure (7/470) | +| T2: typed elements compare with `!==` | assertion failure (9/470) | +| T3: draft ignores typed-array class | assertion failure (1/470) | +| I1: iterator yields raw elements | assertion failure (9/488) | +| I2: `entries()` yields no index | assertion failure (7/488) | +| I3: iterator records a wrong parent edge | assertion failure (19/488) | +| I4: iterator caches the length | assertion failure (12/488) | +| I5: `values()` reads the raw copy | assertion failure (2/488) | +| I6: `entries()` reads the raw copy | assertion failure (2/488) | +| A1: element methods read the copy | assertion failure (42/527) | +| A2: searches do not unwrap a draft | assertion failure (5/527) | +| A3: `slice` runs on the copy | assertion failure (2/527) | +| A4: `constructor` is bound | assertion failure (1/527) | +| A5: searches read through the draft | survives (equivalent) | +| K1: no class check | assertion failure (4/538) | +| K2: keyless instances compare by keys | assertion failure (5/538) | +| K3: URLs compare by identity | assertion failure (10/538) | +| K4: a null prototype is another class | assertion failure (2/538) | +| K5: any two URLs are equal | assertion failure (4/538) | +| K6: empty arrays compare by identity | assertion failure (8/538) | +| K7: a URL equals a non-URL with the same `href` | assertion failure (2/538) | + +I4 first survived every owner, because no test edited an array while an +iterator was open. The iteration contract's array law closes that gap. K7 +first survived too, and `utils.property` now compares a URL with an object +that inherits the same `href`. A5 gives the same results and is only slower: +searches through the draft compare memoized drafts. + +## Code weight over speed + +Drafts run inside `collection.update` callbacks. These calls are rare and +almost never touch large values, so draft speed does not matter, and bytes +do. `31b2a6bd4` removes code that only bought speed: + +| Removed | Speed it bought | Law that still owns the behavior | +| --- | --- | --- | +| The light array iterator from `967a28c1d` | `for...of` over 200 numbers: 0.98 of `main` instead of 4.0 | Iteration contract, array law | +| The list of array methods that ran on the copy | `join()` on 50 numbers: 1.03 instead of 2.8 | `Array.prototype` law in `proxy.test.ts` | +| The primitive fast path in the `get` trap | `slice` on small arrays | Every proxy owner | +| The two-loop `deepClone` key walk | 8% to 17% for each draft | Detachment contract key-class law | +| The typed-array element loop (`TypedArray#set` copies instead) | none | Revert oracle typed values | +| The deferred class check in `deepEquals` | unequal rows exit before the class check | Class law in `utils.property` | + +Mutants of each new form fail the owners: + +| Mutant on `31b2a6bd4` | Owners | +| --- | --- | +| C1: clone copies non-enumerable strings | assertion failure (2/539) | +| C2: clone drops every symbol | assertion failure (10/539) | +| C3: clone drops non-enumerable symbols | assertion failure (2/539) | +| T1: typed clone stays empty | assertion failure (13/539) | +| N3: the iterator reads the raw copy | assertion failure (16/539) | +| A1: read methods run on the copy | assertion failure (56/539) | +| A2: searches run on the copy | assertion failure (4/539) | +| K1: no class check | assertion failure (4/539) | +| K2: keyless instances compare by keys | assertion failure (5/539) | +| K3: URLs compare by identity | assertion failure (10/539) | +| K4: a null prototype is another class | assertion failure (2/539) | +| K5: any two URLs are equal | assertion failure (5/539) | +| K6: empty arrays compare by identity | assertion failure (8/539) | +| D1 (on `b825e6483`): `get` wraps a read-only non-configurable value | crash, the reported TypeError (1/540) | +| D2 (on `b825e6483`): `defineProperty` stores a clone | crash, the reported TypeError (1/540) | +| G1 (on `0ce101acf`): accessor definitions untracked | assertion failure (2/574) | +| G2 (on `0ce101acf`): every definition tracked, sealing too | assertion failure (1/574, the existing `Object.seal` test) | +| Y1 (on `0ce101acf`): typed-array mutators untracked | assertion failure (6/574) | +| Y2 (on `0ce101acf`): `subarray` untracked | assertion failure (1/574) | +| Y3 (on `0ce101acf`): typed-array `set` untracked | assertion failure (1/574) | +| R1 (on `7e5dfee25`): a plain object differs from a class | assertion failure (10/611) | +| R2 (on `7e5dfee25`): no class check | assertion failure (2/611) | +| R3 (on `7e5dfee25`): the keyless rule needs `a` on the class side | assertion failure (4/611) | +| F1 (on `7e5dfee25`): a frozen-key object read is not a change | assertion failure (2/611) | +| F2 (on `7e5dfee25`): a frozen-key primitive read is a change | assertion failure (1/611) | +| S1 (on `7e5dfee25`): `subarray` returns a plain view | assertion failure (2/611) | +| S2 (on `7e5dfee25`): `subarray` marks a change on call | assertion failure (1/611) | +| M1 (on `cbd2aff06`): mutators return the raw copy | assertion failure (12/587) | +| M2 (on `cbd2aff06`): mutators always return the draft | assertion failure (10/587) | +| A1, A2, N3, Y1, Y3, S1, S2 (rerun on `cbd2aff06` against N) | assertion failure (55, 21, 23, 7, 3, 3, 3 of 587) | +| F3 (on `556cbd8ff`): the frozen-key boundary checks configurability alone | assertion failure (1/589) | +| F4 (on `556cbd8ff`): the frozen-key boundary checks writability alone | assertion failure (1/589); survived before the read-only configurable case | + +The I and A3 to A5 mutants in the previous section apply to code that +`31b2a6bd4` removed. + +## Mutant calibration + +"Owners" are the revert oracle and the four existing owners +(`proxy.test.ts`, `proxy-detachment-contract.test.ts`, +`proxy-iteration-contract.test.ts`, `utils.property.test.ts`). "Before" is the +owners without this change's tests. The full-suite column shows whether any +other db test caught the mutant. + +### On unchanged production (`18abceee4`) + +| Mutant | Before | Full suite before | Owners now | +| --- | --- | --- | --- | +| P1: a nested revert does not reach the parent | 20/417 | — | assertion failure (21) | +| P2: a partial revert is treated as full | 0/417 | 0/7,596 | assertion failure (3) | +| P3: the array iterator yields raw elements | 1/417 | — | assertion failure (4) | +| P4: the array iterator records a wrong parent edge | 0/417 | 0/7,596 | survives (equivalent) | +| C1: `deepClone` copies non-enumerable strings | 0/417 | 0/7,596 | assertion failure (2) | +| C2: `deepClone` drops symbol keys | 0/417 | 0/7,596 | assertion failure (4) | +| C3: `deepClone` drops non-enumerable symbols | 0/417 | 0/7,596 | assertion failure (2) | +| E1: draft Set comparison ignores order | 2/417 | — | assertion failure (3) | +| E2: draft Map comparison ignores order | 2/417 | — | assertion failure (3) | +| E3: draft RegExp ignores `lastIndex` | 2/417 | — | assertion failure (3) | +| E4: a draft array hole equals `undefined` | 1/417 (crash) | — | assertion failure (3) | +| E5: draft comparison ignores symbol keys | 0/417 | 0/7,596 | assertion failure (2) | +| D1: `deepEquals` compares Maps in order | 0/417 | 1/7,596 | assertion failure (2) | +| D2: `deepEquals` compares RegExp `lastIndex` | 0/417 | 0/7,596 | assertion failure (2) | +| D3: `deepEquals` compares Sets in order | 3/417 | — | assertion failure (5) | +| D4: `deepEquals` treats array holes as differences | 0/417 | 0/7,596 | assertion failure (2) | + +P4 is equivalent under every public observation. An array element's parent +edge feeds only the array's own revert check, and `getChanges()` works at the +row level, where it compares each field with draft equality. A wrong edge can +make the array mark itself reverted early, but the row then compares the field +by value and still reports the change. The refactor's native iterator records +the right edge through the `get` trap. + +### On the refactor (`6b68b8dc3`) + +| Mutant | Owners | +| --- | --- | +| N1: a nested revert does not reach the parent | assertion failure (25/441) | +| N2: a partial revert in the `set` trap clears all tracking | assertion failure (26/441) | +| N3: the array iterator binds the raw copy | assertion failure (4/441) | +| N4: `deepClone` drops non-enumerable symbols | assertion failure (2/441) | +| N4b: `deepClone` drops every symbol | assertion failure (4/441) | +| N5: `deepClone` copies non-enumerable strings | assertion failure (2/441) | +| N6: draft Map comparison ignores order | assertion failure (3/441) | +| N7: draft Set comparison ignores order | assertion failure (3/441) | +| N8: draft RegExp ignores `lastIndex` | assertion failure (3/441) | +| N9: draft arrays use the element walk | assertion failure (2/441) | +| N10: general mode compares `lastIndex` | assertion failure (2/441) | +| N11: general mode compares Maps in order | assertion failure (2/441) | +| N12: general arrays use the key walk | assertion failure (4/441) | +| N13: draft mode is not passed into object values | assertion failure (1/441) | +| N14: draft mode is not passed into Map values | assertion failure (1/441) | + +N14 first survived, because generated Map values were only primitives. +`639ed5a1e` lets Map values be nested Sets and adds a pinned case for a +reordered Set inside a Map value. + +## Performance + +Each timing process loads one implementation and runs each workload five +times after two warm-up passes. Processes alternate, and each ratio is the +median of 11 processes of each. An A/A control of two copies of the base +stayed within ±1%. + +| Workload | A/A | Prototype loop | Kept loop | +| --- | ---: | ---: | ---: | +| draft: two writes, then `getChanges` (20,000 rows) | 1.010 | 1.012 | 0.827 | +| draft: a write, then a revert (20,000 rows) | 1.006 | 1.163 | 0.900 | +| draft: Map, Set, and array writes (10,000 rows) | 1.005 | 0.910 | 0.772 | +| `deepEquals`: equal rows (20,000) | 0.993 | 0.953 | 0.965 | +| `deepEquals`: equal rows with Map, Set, RegExp (10,000) | 1.010 | 1.019 | 1.023 | +| `deepEquals`: unequal rows (20,000) | 0.999 | 1.024 | 1.009 | +| draft: 20 Array method calls (10,000 rows) | 1.021 | — | 0.961 | + +Each ratio is new time over base time. "Prototype loop" is the audit prototype. +"Kept loop" is the final code. The array-method row was measured with the +stored-function fix, which adds an own-property check before each inherited +method is handled. A second run with the fix gave 0.830, 0.876, and 0.766 for +the three draft rows above. +Bisection showed that its `deepClone` loop caused the regression: the +equality fold alone was faster (0.92, 1.00, 0.81 on the draft rows), and the +other proxy changes with the original loop were faster too (0.92, 0.93, 1.03). +`deepClone` runs twice for each draft, and the `Reflect.ownKeys` loop calls +`propertyIsEnumerable` for each key. Keeping the two-loop form costs about 18 +minified bytes. The harness is not checked in. + +### Timings for the fixes + +These timings describe `a49ae90e3`. `31b2a6bd4` removed the iterator and +the copy-method list for code weight, so the iteration and `join` rows no +longer hold. These runs used 11 processes of each variant. The machine was loaded, and an +A/A control stayed within ±5%. + +| Workload | Ratio | Compared with | +| --- | ---: | --- | +| `for...of` and spread over 200 numbers | 0.98 (4.02 before `967a28c1d`) | `main` | +| `for...of` over 200 objects | 0.99 | `main` | +| `slice(0, 1)` on a 2-element draft array | 1.45 | the commit before `b57d60022` | +| `slice()` on a 50-number draft array | 5.6 | the commit before `b57d60022` | +| `join()` on a 50-number draft array | 1.03 | the commit before `b57d60022` | +| `deepEquals`: equal rows | 1.06 to 1.08 | the commit before `815432ebd` | +| `deepEquals`: unequal rows | 1.00 | the commit before `815432ebd` | + +The iterator first used a generator, which made object elements 1.4 times +slower. It also named the `get` trap as a function expression, which made +every draft write about 7% slower. The kept iterator is a module function. It +calls the trap through the handler object and skips primitives. The class +check in `deepEquals` runs after the keys match, so an unequal row exits +before it. + +## ORC-001 through ORC-014 + +The owners are: + +- **R**, the revert oracle `proxy-revert-oracle.property.test.ts`: four + generated properties over one grammar (general histories, partial reverts, + nested round trips, and URL and keyless writes). +- **N**, the native-methods oracle `proxy-native-methods.property.test.ts`: + generated array and typed-array calls compared with native values. +- **T**, the native-differential tables in `proxy.test.ts`: stored functions, + `defineProperty`, and frozen and sealed drafts. These are bounded + enumerations, not important generated properties. +- **U**, the laws this change adds to `utils.property.test.ts`. + +| Requirement | Result | +| --- | --- | +| ORC-001 | R states the `getChanges()` contract, nine draft-equality rules, the three laws, their authority (rules 1 to 6 from `src/proxy.ts` and earlier tests, rules 7 to 9 from this record), and the limits. N and each T table open with the contract they check, against a native value. | +| ORC-002 | R's model is an encoding of plain-data specs. It does not import `draftValuesEqual`, `deepEquals`, or `deepClone`, and it never reads a draft. N and T take the expected result from the same call on a native value. U builds each pair with a known expected relation. | +| ORC-003 | R keeps contract, model (`canon`, `step`, `expectedChanges`), grammar (the arbitraries), driver (`drive`, `realize`), and check (`expectHistory`) apart in one file. N's opening comment names the same five parts. | +| ORC-004 | See ORC-004 controls below. | +| ORC-005 | R and N drive the draft that `createChangeProxy` or `withChangeTracking` returns. The checkpoint is the end of the callback, before publication: R compares `getChanges()` and the draft, and N the call result and the published row. Positive witnesses: R's fixed general campaign reaches every operation, both revert outcomes, 20 or more same-object reverts, and a class-instance write-back. R's URL and keyless witness requires 10 or more of each write. N's witnesses reach every method, empty rows, holes, nested objects, `NaN`, and `-0`. The partial-revert and nested round-trip properties reach their histories by construction. | +| ORC-006 | Every mutant in this record has an outcome class. P4 and A5 are equivalent within the tested domain, with reasons. | +| ORC-007 | R, N, and the two-step callback property each run a fixed campaign (seeds `2026101`, `2026102`, `2026103`) and a random one with the same property, grammar, check, and budget. Replay: `TANSTACK_DB_PROXY_REVERT_*`, `TANSTACK_DB_PROXY_NATIVE_*`, and `TANSTACK_DB_PROXY_CALLBACK_*` accept a seed and path and run only the replay. Under mutant M1, a captured random failure (`[[], "sort", 0]`) replayed directly to the same counterexample. U uses the replay interface `utils.property.test.ts` already has. | +| ORC-008 | R's model is a stateless recomputation over specs, and N and T use native values, so this requirement does not apply. | +| ORC-009 | Terms match `proxy.ts` and `utils.ts`: draft, revert, changes, draft equality. Model-only spec kinds such as `rows` and `point` are named in R. | +| ORC-010 | fast-check reports seed, path, and shrunk input. R's messages give the field and the history. N's give the row, method, and argument. | +| ORC-011 | N and T are native-differential, a second formulation independent of R's model. No shared semantic fault was found that would call for another one. | +| ORC-012 | This record ties the revisions, outcome classes, limits, and performance evidence to the coverage map. See Bug-class closure below. | +| ORC-013 | See Reusable boundary laws below. | +| ORC-014 | No controlled provider or host supplies a premise. Drafts run in-process on the JavaScript engine, so this requirement does not apply. | + +### ORC-004 controls + +**R (all four properties share one grammar).** + +- **Reconstruction:** the pinned histories rebuild each rule and each reported + bug in R's own grammar and check. These include a nested add-then-delete, + a stale parent edge, a key added as `undefined`, a typed-array subclass, + typed `NaN`, a URL write, a keyless write, and a class-instance write-back. + The general grammar reaches each of their shapes. `556cbd8ff` added the + same-object revert so that a write-back of the row's own object is + generated, not only pinned. +- **Ablation:** each mutant in Mutant calibration and Fixes from the second + review removes one rule, edge, or clause. Every one fails an owner, except + P4, which is equivalent. +- **Range:** three fields. Values are primitives `0`, `-0`, `1`, `NaN`, + `"a"`, `"b"`, `null`, and `undefined`; Dates (including invalid); + RegExps with `lastIndex` 0 or 1; Maps of up to two entries, with values + that may be Sets; Sets of up to two members; arrays of up to three slots + with holes; objects with up to two string keys and a symbol key; arrays of + rows; typed arrays of up to three elements (three classes); URLs; a keyed + class instance; and, as written values only, a keyless class instance. The + marginal cases are empty containers, `-0` and `NaN`, holes, and an absent + key against a key holding `undefined`. Typed-array subclasses, URLs, + keyless instances, and the class write-back each exposed a missing rule when + first added. +- **Exclusion:** `applicable` rejects a nested write on a value that is not an + object, an index write beyond the length or into a `Uint8Array`, and a + revert of a field that is absent both originally and now. Model and driver + skip the same operations. Mutator methods and top-level symbol writes are + outside the grammar. + +**N (arrays and typed arrays).** + +- **Reconstruction:** the rows of the earlier pinned tables run for every + method as fixed cases. +- **Ablation:** mutants A1, A2, N3, M1, M2, Y1, Y3, S1, and S2 each fail an + owner. +- **Range:** array rows of up to four slots from `0` to `3`, `undefined`, + holes, objects, and nested arrays of up to two objects. Typed rows of up to + four values from `0`, `-0`, `1`, `2`, `NaN`, and `3.5`. An index-shaped + argument from 0 to 4, so negative and out-of-range indexes occur. Every + method of the built-in prototypes. The marginal cases are empty rows, holes, + and duplicate elements. +- **Exclusion:** every generated call is legal JavaScript, and a native throw + is an observation that the draft must reproduce. A callback that writes is + outside the grammar. + +**U.** The laws use small fixed value sets with the pinned pairs inside the +same property. Mutants K1 to K7 and R1 to R4 are the ablations. Every pair in +the domain is legal, so no exclusion applies. + +The two-step callback property was already on `main`. This change only adds +its campaigns and replay, and it makes no new grammar claim for it. + +### Reusable boundary laws (ORC-013) + +| Law | Premise witness | Nearby witness | Wrong boundary it rejects | +| --- | --- | --- | --- | +| A key that is both read-only and non-configurable returns the raw value and counts as changed | freeze, then a nested write | a sealed key, and a read-only configurable key, read without a change | F3 (configurability alone), F4 (writability alone) | +| Two different classes differ, and a plain object compares with any class by keys | `Point` against `Other` | `Point` against a plain object, and a draft write-back | R1 (a plain object differs from a class), R2 (no class check) | +| A keyless class instance equals only itself; a URL compares by `href` | `Secret` against `Secret` | `Secret` against `{}`, and a URL against an object that inherits the same `href` | K2, R3, K7 | +| Typed-array class counts only in draft mode | the draft `Float64Array` to `Uint8Array` write | `deepEquals` of `Uint8Array` and `Int8Array` | T3, and the #434 test against a class check in general mode | +| Every non-mutating array method reads through the draft | generated rows for every method | searches with draft elements and duplicates | A1 (all methods on the copy), A2 (searches on the copy) | + +### Bug-class closure (ORC-012) + +Each class below is bounded by contract, histories, production path, and +observation. Open cells name their owner. + +| Class | Boundary | Open in-scope cells | +| --- | --- | --- | +| Stored functions read back bound | Any draft read path (field, index, iterator, spread, Map value, Set member, `Object.values`) on the `get` trap; observed by identity and `getChanges()` | A built-in method called through `this` on a nested Map or Set draft rejects the Proxy (T, a Proxy limit) | +| Array and typed-array methods lose writes or identity | Every built-in method on generated rows (N); observed by result, identity, returned self, and the published row | A callback that writes (N, outside the grammar); a no-op mutator publishes an equal value (N and R, declared limit) | +| `defineProperty` throws or loses changes | Value, accessor, read-only, and fixed definitions (T); observed by result, descriptor, read, and changes | None known | +| Nested reverts leave stale changes | R's nested, delete, and round-trip histories; observed by `getChanges()` | Mutator methods have no revert check (R, declared) | +| Equality hides writes of URLs, keyless objects, and classes | R's URL, keyed, and keyless class values and U's pairs; observed by `getChanges()` and `deepEquals` | Writing back a keyless instance of the original row is a change; a draft reads a class instance of the original row as a plain object (detachment owner) | +| Frozen drafts lose nested writes | Freeze, seal, and fixed keys (T); observed by the published row | An object read under a frozen key publishes an equal value (T, declared) | + +None of these classes has a known reachable counterexample to its claimed law. +The open cells are listed in the coverage map's Drafts row. + +### Guide prompts that do not apply + +The guide's reusable boundary-law checklist targets adapter and lifecycle +oracles. Drafts have no provider, classifier, transport, `await` boundary, or +acquired resource, so real-provider conformance, minimal ambiguity, name +invariance, await-boundary transitions, local/transport refinement, and +partial-construction cleanup do not apply. Representation symmetry appears as +the class rule above. Value-and-work refinement does not apply, because no +work bound is promised. + +## Verification + +On `556cbd8ff`, which includes `origin/main` at `3463cf8ad`, with the built +`dist`: + +- `packages/db` Vitest, typecheck off: 199 files, 7,801 tests. +- `packages/db` `tsc --noEmit`: no errors. +- `pnpm check:mangle`: 368 names. +- `pnpm test:minified-db`: error names, index metadata, query rows, and live + updates. +- `pnpm --filter @tanstack/db test:dist`: 5 files, 290 tests. + +The environment was Node `v24.19.0` and Vitest `3.2.4` on Darwin arm64. diff --git a/docs/contributing/oracle-reviews/code-weight-hash-dispatch.md b/docs/contributing/oracle-reviews/code-weight-hash-dispatch.md new file mode 100644 index 0000000000..2309491733 --- /dev/null +++ b/docs/contributing/oracle-reviews/code-weight-hash-dispatch.md @@ -0,0 +1,417 @@ +# Code weight: one type dispatch for hashing and equality + +Reviewed executable revision: `3b8412f4` (base `0122da80`, the fetched +`origin/main` at review time). This record follows in a documentation-only +commit. Work started on `8283f2e8`. `f4c390a6` merges `0122da80`, which does +not change `packages/db-ivm`. + +- `c0182397` adds the hash identity oracle and the work-cap tests on unchanged + production code. +- `d1fa5a9a` is the refactor. +- `b7455952` extends the oracle after mutants on the refactor found three + grammar gaps. +- `6073cb13` applies review fixes. It fixes one performance regression and + one behavior change, and it closes three more grammar gaps. +- `33603a2f` compares type markers by object identity, after an external + review found that equality used random numbers as type identity. +- `3b8412f4` runs every oracle law in a module copy whose type markers all + collide, generates any value as a Map key, and aims half of the mutations + at container nodes. + +Each version of the new tests also passes on the base code. The documentation +commit that follows also adds comments to the oracle file, with no executable +change. + +## Change + +`hashObject` and `equalHashValues` in `packages/db-ivm/src/hashing/hash.ts` +each had a six-way type dispatch: Date, binary, Temporal, RegExp, and the +Map/Set/array/object shapes. Each branch had its own hasher or its own +comparison. One `objectParts(input)` function now returns a +`[marker, header, body?]` view that both use. Each marker is a private object +that holds a random hash number. Hashing writes the number, and equality +compares the objects by identity. + +| Type | Header | Body | +| --- | --- | --- | +| Date | timestamp, or `invalid` | none | +| binary (at most 128 bytes) | the bytes as one string | none | +| Temporal | type tag, string form | none | +| RegExp | source, flags, `lastIndex` | the RegExp | +| Map | none | its entries | +| Set | none | its values | +| array | length | the array | +| object | none | the object | + +Also removed: + +- the four per-type hashers (`hashDate`, `hashUint8Array`, `hashTemporal`, + `hashPlainObject`) and `structuralShape`. +- the duplicate root and nested paths in `getCachedHash`. +- the unreachable `typeof` `default:` warning in `updateHasher`. The switch + already covers all eight `typeof` results. + +`referenceValues` moved into `isReferenceHashedObject`, which both functions +already call. + +The change is intended to preserve behavior. Hash values change because the +binary header is now a string. Hash values were never stable: the markers are +random for each process, and nothing persists them. + +| Entry | Revision | min | gzip | brotli | +| --- | --- | ---: | ---: | ---: | +| full `@tanstack/db` (db-ivm inlined) | `0122da80` | 378,307 | 106,762 | 90,361 | +| | `3b8412f4` | 377,379 | 106,527 | 90,141 | +| | Δ | −928 | −235 | −220 | +| standalone `@tanstack/db-ivm` | `0122da80` | 34,374 | 10,570 | 9,513 | +| | `3b8412f4` | 33,452 | 10,347 | 9,353 | +| | Δ | −922 | −223 | −160 | + +All numbers come from an esbuild bundle of the public entry, minified. + +## What earlier versions changed, and what this change keeps + +The audit prototype (ledger IVM-01) and the first port of it changed the +behaviors below. This change keeps each one as the base code had it: + +1. **Work cap.** The prototype wrote every header value directly to the + hasher. Array lengths and RegExp fields then stopped counting toward + `MAX_STRUCTURAL_HASH_WORK`, so a 1,000,000-element array was accepted + instead of rejected. Now header values of a type that has a body go through + `updateHasher`, as before. Date, binary, and Temporal headers still cost + nothing. +2. **Pair memo.** The prototype entered the equality pair memo before it + compared headers. That allocated memo entries for every Date, binary, and + Temporal pair, and binary equality took 2.3 times as long. The first port + moved the whole memo after `objectParts`, which copies Map and Set entries. + Each revisit of a shared or cyclic Map then cost O(entries): 100 revisits + of one pair made 200 entry copies instead of 2. Now the lookup comes before + `objectParts`, and the insert comes after the markers and headers match, + only for types with a body. That is the base order. +3. **Arrays from another realm.** `instanceof Array` is false for an array + from another realm, so both versions give it the object marker. The base + equality also compared lengths with `Array.isArray`. The prototype dropped + that check. This change keeps it. +4. **Header comparison.** The first port compared headers with the `NaN` + rule, so a RegExp whose `lastIndex` is `NaN` equaled its copy. The base + code compared RegExp fields with `!==`. Headers now compare with `===`, and + the header of an invalid Date is `invalid`, so all invalid dates stay equal. +5. **Type identity.** The base code told types apart with `instanceof`. The + first port compared the random marker numbers, so two types whose numbers + collided compared equal, for example an empty Map and an empty Set. Map and + array then also gave different results in each argument order. Markers are + now objects, compared by identity. + +## Why a new owner + +Before this change, `equalHashValues` had no direct test. `topKBatch` uses it +to cancel a retraction against its replacement, and only +`tests/operators/topk-batch-contract.test.ts` reached it. The hash-values +owner (`hash.property.test.ts`) checks pairwise hash laws on flat values. It +has no model of equality, and it does not generate Temporal values, RegExp +fields, holes, symbol keys, registered handles, `File`, or Map/Set order. + +The new owner, +`packages/db-ivm/tests/hash-identity-oracle.property.test.ts`, checks both +functions against one identity model. + +## Contract, path, and limits + +Authority: the method comments in `src/hashing/hash.ts`, and the behavior at +`8283f2e8`. + +1. Primitives compare by value. `-0` equals `0`, `NaN` equals `NaN`, and a + bigint differs from the equal number. Symbols compare by identity. +2. Functions, registered handles, `File` values, and binary values over 128 + bytes compare by identity. +3. Dates compare by timestamp. All invalid dates are equal. +4. Binary values of at most 128 bytes compare by bytes. `Buffer` and + `Uint8Array` do not differ. +5. Temporal values compare by type tag and string form. +6. Regular expressions compare by source, flags, `lastIndex`, and enumerable + own properties. The fields compare with `===`, so a `NaN` `lastIndex` never + equals itself. +7. Arrays compare by length and enumerable own properties. A hole differs from + `undefined`. +8. Maps and Sets compare by entries or values in insertion order. Other + properties on a Map or Set do not count. +9. Other objects compare by enumerable own string and symbol properties, in + any order. The prototype and non-enumerable properties do not count. + +The model is a canonical encoding of a value **spec**, which is plain data. +It never reads a production object. The driver builds both sides of a pair as +fresh objects from their specs. A seed varies property order, prototypes +(`{}`, `Object.create(null)`, a class instance), hidden non-enumerable +properties, `Buffer` or `Uint8Array`, extra Map/Set properties, `-0` for `0`, +and sharing or copying of a repeated subtree. None of these changes the +identity. + +Every law also runs in a second copy of the module, loaded with a +`captureHashSession` tape whose initialization draws are all equal. Every +random constant, including every type marker, then has the same number, in any +declaration order. A pinned case checks that precondition. In that copy, only +the sampled distinct-hash control is skipped. + +For each acyclic pair, the check asserts: + +- `equalHashValues` in both argument orders equals the model verdict. +- Equal identities have equal hashes. +- Distinct identities have distinct hashes. This is a sampled control, because + a 32-bit hash can collide. + +Cyclic values are chains of arrays, Map values, Sets, or plain objects with a +back edge to an ancestor. For each one, the check asserts that +`equalHashValues` accepts a second build of the same spec and rejects a build +where one leaf holds a text that no pool value has. It also asserts that `hash` throws +`Cannot hash cyclic structural values`. These verdicts come from +construction, so the model needs no bisimulation rule. + +`hash-work.test.ts` pins seven work-cap boundaries. Each case names the +largest input that fits, and the next size must throw. One more law counts Map +entry copies: equality on 100 revisits of one shared Map pair copies entries +twice, once for each side. + +Limits: + +- Arrays from another realm are outside the grammar. One pinned case holds + equality's length check. Hashing gives such an array the object marker, so + `[1, ]` and `[1]` from another realm hash equal but compare unequal. + This mismatch is present on the base revision too. +- Map and Set order sensitivity, and ignored Map/Set properties, are pinned + current behavior. No contract promises them. +- Getters and proxies are outside the grammar. +- A RegExp with a `NaN` `lastIndex` is outside the grammar. One pinned case + holds strict comparison. Hashing normalizes `NaN`, so that pair hashes equal + but compares unequal. This mismatch is present on the base revision too. +- Cycles through a RegExp property, a Map key, or a symbol-keyed property are + outside the grammar. So are pairs that differ only in where a back edge + points, such as `a = [a]` against `b = [[b]]`. Those need a bisimulation + model. +- `hash` and `equalHashValues` still list enumerable own keys in two ways + (`Object.keys` plus symbols, and `Reflect.ownKeys`). The oracle's hidden + non-enumerable properties catch drift between them: mutant N16 fails. +- Real Temporal types print distinct formats. Only a polyfill-shaped value can + share text across types, so one pinned case holds the type tag. + +## Grammar controls and calibration + +Specs cover every kind in the contract, nested to a small depth. Map keys are +any generated value. The model merges primitive keys and shared references +that are equal under SameValueZero, as Map and Set do: the first position +stays and the last value wins. A pair is either the same spec (30%) or the +spec with one near-miss mutation. Half of the mutations target a container +node, because leaves outnumber containers: + +| Mutation | Examples | +| --- | --- | +| header | a Date timestamp, one binary byte, an appended byte, a Temporal value, RegExp flags or `lastIndex` | +| hole | an appended hole, a hole for a value, `undefined` for a hole | +| order | reversed Map entries, Set values, or object keys (object order keeps the identity) | +| property | a renamed object or Map key, a repeated primitive Map key, a changed symbol key, an added RegExp or array property | +| retype | A Date becomes its timestamp. Binary becomes an array of its bytes. A Map becomes a Set or an array of entry pairs. A Set becomes an array. An array becomes an object. | +| replace | a different primitive or reference id | +| split | a shared child becomes two copies, one of them changed | + +- **Reconstruction:** 22 pinned pairs hold one rule each, in both module + copies. Six more pinned cases hold the Temporal tag, strict RegExp field + comparison, a shared child against two different copies, the cross-realm + length check, the colliding-marker precondition, and a `topKBatch` + Map-to-Set replacement with colliding markers. One case builds the same + shared/copied spec with 39 seed pairs. +- **Ablation and exclusion:** the mutants below. +- **Range:** the positive execution witness requires the fixed campaign + (seed `2026930`, 300 pairs) to reach all 20 value kinds and all eight + mutation classes, to give a `false` verdict for header, hole, retype, + replace, and split mutations, and to give only `true` for unmutated pairs. + It also requires Map keys that are numbers, symbols, references, and + containers, and at least one merged Map key. + +Each run executes a fixed campaign and a random campaign of 300 pairs for +acyclic values, and the same for cyclic values. +`TANSTACK_DB_IVM_HASH_IDENTITY_SEED` and `TANSTACK_DB_IVM_HASH_IDENTITY_PATH` +select a direct replay. + +### Source mutants on unchanged production (`8283f2e8` sources) + +All rows use the tests at `3b8412f4`. The columns count failures: + +- **Identity oracle:** `hash-identity-oracle.property.test.ts` (34 cases). +- **Work-cap pins:** the seven boundary cases in `hash-work.test.ts`. +- **Pre-existing:** every other db-ivm test, with `hash-work.test.ts` at its + `origin/main` version (589 tests). + +| Mutant | Identity oracle | Work-cap pins | Pre-existing | +| --- | --- | --- | --- | +| W1: equality ignores RegExp `lastIndex` | assertion failure (3) | 0 | 3 | +| W2: equality ignores array length | assertion failure (4) | 0 | 3 | +| W3: equality ignores the container kind | assertion failure (9) | 0 | 3 | +| W4: binary equality compares only lengths | assertion failure (3) | 0 | 3 | +| W5: Temporal equality compares only tags | assertion failure (2) | 0 | 0 | +| W6: registered handles compare structurally | assertion failure (3) | 0 | 3 | +| W7: equality ignores symbol keys | assertion failure (3) | 0 | 3 | +| W8: a Set hashes with the array marker | assertion failure (4) | 0 | 0 | +| W9: hashing skips symbol keys | assertion failure (3) | 0 | 7 | +| W10: hashing omits the array length | assertion failure (3) | 6 | 2 | +| W11: invalid dates compare with `===` | assertion failure (3) | 0 | 0 | +| W12: equality compares large binary values by content | assertion failure (1) | 0 | 3 | + +A first mutant for W11, "Date equality accepts a non-Date", survived every +test. It is equivalent in this domain: the earlier `typeof` guard rejects a +Date compared with a primitive, and no generated object has `getTime`. + +### Source mutants on the refactor (`3b8412f4`) + +| Mutant | Identity oracle | Work-cap pins | Pre-existing | +| --- | --- | --- | --- | +| N1: a Set uses the Map marker | assertion failure (6) | 0 | 0 | +| N2: equality skips header values | assertion failure (7) | 0 | 15 | +| N3: the binary header holds only the length | assertion failure (3) | 0 | 3 | +| N4: header-only types compare equal before their headers | assertion failure (5) | 0 | 6 | +| N5: header values skip the work counter (the prototype) | 0 | assertion failure (6) | 0 | +| N6: hashing drops symbol keys | assertion failure (3) | 0 | 7 | +| N7: a RegExp has no body | assertion failure (2) | 1 | 0 | +| N8: a Map body holds only values | assertion failure (7) | 0 | 7 | +| N9: the Temporal header drops the tag | assertion failure (1) | 0 | 0 | +| N10: registered handles compare structurally | assertion failure (3) | 0 | 3 | +| N11: the cross-realm length check is removed | assertion failure (1) | 0 | 0 | +| N12: the revisit check keys on the left object only | assertion failure (3) | 0 | 0 | +| N13: equality skips values under symbol keys | assertion failure (2) | 0 | 0 | +| N14: headers compare with the `NaN` rule (the first port) | assertion failure (1) | 0 | 0 | +| N15: the revisit lookup follows `objectParts` (the first port) | 0 | 0 | 0 | +| N16: hashing counts non-enumerable string keys | assertion failure (4) | 6 | 0 | +| N17: equality compares marker numbers (the first port) | assertion failure (9) | 0 | 0 | + +N15 changes only work, not identity. The entry-copy law in +`hash-work.test.ts` rejects it with an assertion failure (1 case). The +production code before `33603a2f`, which compared marker numbers, fails the +same 9 oracle cases as N17, including both generated pair campaigns. + +History of the grammar gaps: + +- N1, N8, N9, and N11 survived the first oracle (`c0182397`). `b7455952` adds + Sets of generated values, a Map key rename, a Map-to-Set retype, and two + pinned cases. +- N12, N13, N14, and N15 came from review of `b7455952`. `6073cb13` adds the + split mutation, mutations of values under symbol keys, the strict RegExp + pin, and the entry-copy law. +- N17 came from an external review of `f4c390a6`. `33603a2f` added a pinned + collision at fixed draw positions. `3b8412f4` replaces it with the + colliding module copy, which does not depend on declaration order. + +Fourteen mutants pass every pre-existing db-ivm test: W5, W8, W11, N1, N5, N7, +N9, N11, N12, N13, N14, N15, N16, and N17. + +## Performance + +`hash` runs for structural keys and unkeyed consolidation. `equalHashValues` +runs only in `topKBatch`, for a key with more than one entry in one batch. + +Each timing process loads one implementation and runs every workload five +times after two warm-up passes. Most workloads use 20,000 values. Processes +alternate, and each ratio is the median of 15 processes of each +implementation. The machine had a load average of 4.5 to 10 during the run. + +| Workload | A/A | A/B | +| --- | ---: | ---: | +| hash rows (6 fields with a Date and an array) | 1.011 | 1.017 | +| hash join keys (`[number, string]`) | 1.068 | 1.129 | +| hash binary (16 bytes) | 0.973 | 1.104 | +| hash nested (objects, arrays, Map, Set) | 1.024 | 0.897 | +| hash dates (one valid, one invalid) | 1.043 | 0.710 | +| equality rows | 1.020 | 0.964 | +| equality join keys | 1.045 | 1.388 | +| equality binary | 1.021 | 1.300 | +| equality nested | 0.984 | 0.989 | +| equality with 2,000 revisits of one shared Map | 1.003 | 0.739 | + +Each ratio is new time over base time. The A/A control compares two copies of +the base code. Its noise reached ±7% in this run and ±10% in earlier runs. + +- Hashing is neutral or faster, except binary values, which are about 10% + slower. The murmur stream writes two bytes for each string character, and + the base code wrote each byte once. +- Equality on small distinct pairs is 1.3 to 1.4 times slower for join keys + and binary values. Each side now builds a parts tuple and a header array. + `equalHashValues` runs only in `topKBatch` for a key with more than one + entry in one batch, so this cost stays on a rare path. +- Equality over a shared Map is faster than the base, and it copies entries + once for each distinct pair. + +The harness is not checked in. These timings predate `33603a2f`, which adds one +property read for each hashed object and compares marker objects instead of +numbers. They were not rerun for that change. + +## ORC-001 through ORC-012 + +| Requirement | Result | +| --- | --- | +| ORC-001 | The oracle's opening comment states the nine identity rules, the three laws, the authority, and the limits above. | +| ORC-002 | The model is a canonical encoding of plain-data specs. It does not import production helpers or read production objects. Cyclic verdicts come from construction. | +| ORC-003 | The contract, model (`identity`, `canon`), grammar (`specArb`, `nearMiss`, `pairArb`, `cyclicArb`), driver (`realize`), and checks (`expectPair`, `expectCyclicPair`) are separate and visible in one file. | +| ORC-004 | Reconstruction, ablation, range, and exclusion appear above. | +| ORC-005 | The driver calls the production functions. The positive witness shows that the fixed campaign reaches every kind, every mutation class, and both verdicts. | +| ORC-006 | The source mutants above have their outcome classes. One equivalent mutant is recorded with its reason. | +| ORC-007 | Fixed and random campaigns share each property, grammar, check, and budget (300 runs). The replay variables select a direct replay. The properties do not use `fc.commands`. | +| ORC-008 | The model is a stateless encoding, so this requirement does not apply. | +| ORC-009 | The model does not use production's marker/header/body split. It encodes each contract rule directly on spec kinds. `ref` is production's reference leaf. The oracle declares two model-only kinds. `twice` is one child that the driver shares or copies. `back` is a back edge to an ancestor. | +| ORC-010 | fast-check reports the seed, path, and shrunk pair. Assertion messages give the mutation class and both identities. | +| ORC-011 | The Map/Set order rule, the cross-realm mismatch, and the `NaN` `lastIndex` mismatch are shared current behavior. The limits name them instead of hiding them. | +| ORC-012 | This record ties the reviewed revisions, outcome classes, limits, and performance evidence to the coverage map. | + +## Verification + +On `3b8412f4`, the following passed: + +- `packages/db-ivm` Vitest: 42 files, 631 tests, no type errors. +- `packages/db-ivm` `tsc --noEmit`: no errors. +- `packages/db` Vitest, typecheck off: 193 files, 7,162 tests. +- `pnpm test:oracles`: 49 `@tanstack/db` files with 2,845 tests, and 16 + `@tanstack/query-db-collection` files with 454 tests and 1 todo. +- `pnpm test:minified-db`: error names, index metadata, query rows, and live + updates. + +The environment was Node `v24.19.0` and Vitest `3.2.4` on Darwin arm64. + +## External review follow-up at `33603a2f` and `3b8412f4` + +The executable fix at `33603a2f` follows reviewed PR head `f4c390a6` and +base `0122da80`. An external review found that the refactor used independent +31-bit random hash numbers as equality type discriminators. Those numbers can +collide even though the value types differ. With a fresh module and controlled +`Math.random` draws, making only the Map and Set marker numbers equal made +`equalHashValues(new Map(), new Set())` and its reverse return `true` on +`f4c390a6`. Both returned `false` on the base. Making the Map and array numbers +equal made Map-to-array return `true` and array-to-Map return `false` on the PR. +Both directions returned `false` on the base. Each probe observed 19 draws. + +The new pinned oracle first failed on the unmodified PR at the Map/Set +assertion (`expected true to be false`). Each type marker now carries its +original random hash number in a distinct private object. Hashing writes that +number. Equality compares the marker objects by identity. This keeps the +shared type dispatch and its random hash values while separating equality +from numeric marker collisions. The same controlled probes then returned +`false` for both pairs in both argument orders. The oracle also checks that +`topKBatch` retains one Map retraction and one Set addition for the same key. +The full db-ivm suite passed 625 tests in 42 files. `tsc --noEmit -p +packages/db-ivm/tsconfig.json`, Prettier, and `git diff --check` passed. + +The test checks equality type separation for empty Map/Set and Map/array pairs +under forced collisions, plus the corresponding `topKBatch` output. Distinct +marker objects cover the other supported carrier types by the same invariant. +The test does not prove hash collision freedom or all live-query histories. +`3b8412f4` closes both gaps. The pinned draw-position test is replaced by the +colliding module copy, which runs every generated and pinned law with all type +markers equal. Generated Map keys now include numbers, symbols, references, +and containers, with SameValueZero merging in the model. + +Three other external suggestions remain optional. The `if (body)` test in the +header loop is invariant, but runs at most three times for a RegExp and once +for an array. Distinct object comparisons allocate two parts tuples. A +Map/Set mismatch copied one entries iterator on both the PR and base, while a +binary/Date mismatch newly calls `String.fromCharCode` once. A direct type +short-circuit would duplicate the shared dispatch without an established hot +path need. For a fresh 16-byte binary value, a deterministic counter observed +56 `writeByte` calls on the PR and 40 on the base. The earlier review's 10% +binary timing and 1.3–1.4× small-pair equality timing were not rerun for this +fix. Reintroducing specialized binary hashing remains a conditional +performance follow-up, not part of this correctness fix. diff --git a/docs/contributing/oracle-reviews/code-weight-multiset-consolidation.md b/docs/contributing/oracle-reviews/code-weight-multiset-consolidation.md new file mode 100644 index 0000000000..caa8b9fc26 --- /dev/null +++ b/docs/contributing/oracle-reviews/code-weight-multiset-consolidation.md @@ -0,0 +1,228 @@ +# Code weight: one MultiSet consolidation loop + +Reviewed executable revision: `a27600fd` (base `bef12e24`, the fetched +`origin/main` at review time). This record follows in a documentation-only +commit. The oracle lands in `35720388` on unchanged production code. The +refactor lands in `b1599995`. Review fixes land in `a27600fd`. + +The work started on `b5d92ceb` and was rebased onto `bef12e24`. The +`packages/db-ivm` sources are identical at those two revisions. The base +mutants and the timings below ran against the `b5d92ceb` sources, and they +apply to `bef12e24` without change. + +## Change + +`MultiSet.consolidate()` had two hand-written loops. The keyed loop handled +`[key, value]` multisets. The unkeyed loop handled other data. Both summed +multiplicities per identity, kept the first record, and dropped zero sums. One +`consolidateBy(inner, identityOf)` helper now serves both. Only the identity +function differs. + +- The internal `ObjectIdGenerator` class becomes a `getStringId` function with + the same encoding. +- The internal `DefaultMap` class and its four unit tests are removed. No other + code used it. Neither class was exported from `@tanstack/db-ivm`. +- A contract comment on `consolidate()` replaces the removed method comments. + +The change is intended to preserve behavior, including the keyed identity +collisions in [#1948](https://github.com/TanStack/db/issues/1948). A fix for +that bug is a separate behavior change. + +`b1599995` changed one behavior. On the single-primitive path, the base code +returned the `Map` key, so a `-0` record came back as `0`. `b1599995` returned +the input record, so `-0` stayed `-0`. The oracle did not generate `-0`, and +no other db-ivm test failed. Review found the change. `a27600fd` returns the +`Map` key again, and the oracle grammar now contains `-0` and `NaN`. + +| Entry | Revision | min | gzip | brotli | +| --- | --- | ---: | ---: | ---: | +| full `@tanstack/db` (db-ivm inlined) | `bef12e24` | 375,166 | 105,868 | 89,634 | +| | `a27600fd` | 374,186 | 105,541 | 89,418 | +| | Δ | −980 | −327 | −216 | +| standalone `@tanstack/db-ivm` | `bef12e24` | 35,345 | 10,894 | 9,855 | +| | `a27600fd` | 34,374 | 10,570 | 9,513 | +| | Δ | −971 | −324 | −342 | + +All numbers come from an esbuild bundle of the public entry, minified. + +## Why a new owner + +Before this change, `MultiSet.consolidate` had no owner in the coverage map. +`tests/multiset.test.ts` checks primitive and string values only. The existing +db-ivm suite rejects each wrong design below, but no test states the identity +rules that the refactor must keep. The new oracle, +`packages/db-ivm/tests/multiset-consolidate-oracle.property.test.ts`, states +them and checks them against an independent model. + +## Contract, path, and limits + +Authority: the contract comment on `MultiSet.consolidate()`. It restates the +behavior and the keyed and unkeyed method comments of `b5d92ceb`. + +1. **Keyed.** Every record is a `[key, value]` pair with a string or number + key. The key compares by value. A primitive value compares by value. An + object value compares by reference. A value that is an array of length 2 is + a join tuple, and its two elements compare by the same rule. +2. **Unkeyed, one primitive type.** Every record is a string, or every record is + a number. Records compare as `Map` keys do: `-0` equals `0`, and `NaN` + equals `NaN`. The result holds `0` for a `-0` record. +3. **Unkeyed, structural.** Any other multiset. Records compare by structure. + +For each identity, the result holds one record: the first in input order +(normalized as in rule 2), with the summed multiplicity. An identity whose sum is zero is absent. The input is +not changed. + +The model computes identity with a type-tagged key and a local reference map. +It does not import `getStringId`, `ObjectIdGenerator`, or `hash`. The driver +calls `new MultiSet(records).consolidate().getInner()`. The refinement check +records every output entry, so duplicates, extra identities, and missing +identities stay visible. It compares each multiplicity with the modeled sum and +each retained record with the modeled first record by `Object.is`. It then +checks that every input pair still holds its original record and multiplicity, +and that a tagged encoding of all record contents did not change. + +Limits: + +- The oracle does not assert output order. No contract states it. +- Keyed identity is a text encoding that is not injective (#1948). The grammar + excludes each known collision: keys or values of different types with the + same text (`1` and `'1'`, `true` and `'true'`, `1n` and `1`), keys or values + that contain `|`, and symbol or function values, which compare by text. The + exclusions must go when that bug is fixed. +- Structural identity uses a 32-bit hash in production. The small domain makes + a collision unlikely, but the oracle does not claim injectivity. +- Generated objects have one key, so key-order sensitivity is outside this law. + +## Grammar controls and calibration + +The grammar has five modes: + +- `keyed`: keys from `0, 1, 2, 'a', 'b'`. Values are primitives (including + `-0` and `NaN`), three shared + objects (two with equal contents), fresh join tuples of those leaves, or fresh + arrays of length 3. +- `numbers`: `0, -0, 1, 2, 3, NaN`. `strings`: `'p', 'q', 'r'`. +- `structural`: `1`, `'1'`, `true`, `null`, fresh one-key objects, and fresh + arrays of length 2. +- `fallback`: keyed records plus one appended non-keyed object, which makes the + whole multiset unkeyed. + +Multiplicities are integers from −2 to 2, so zero multiplicities and cancelling +sums occur. + +- **Reconstruction:** six pinned witnesses reconstruct each identity rule: + reference identity, tuple unpacking, other array lengths, the fallback, + `-0` and `NaN` on the single-number path, and `1` versus `'1'` on the + structural path. +- **Ablation:** each mode has a distinct rule. The mutants below show that + removing a rule's distinction fails the oracle. +- **Range:** empty multisets occur in the keyed, `numbers`, and `strings` + modes. The fixed campaign reaches every mode. In that campaign, more than 30 + of 300 histories merge or cancel records. +- **Exclusion:** the `fallback` mode checks that one non-keyed record makes the + keyed rule inapplicable. + +### Source mutants on unchanged production (`b5d92ceb`) + +"Suite" means the rest of the db-ivm suite. The column shows failed tests over +total tests. + +| Mutant | New oracle | Suite | +| --- | --- | --- | +| W1: keyed values compare by `hash` (contents) | assertion failure (4/7) | assertion failure (4/591) | +| W2: join tuples compare by reference | assertion failure (2/7) | assertion failure (47/591) | +| W3: keyed loop skips a non-keyed record instead of falling back | assertion failure (3/7) | assertion failure (3/591) | +| W4a: keyed result keeps zero sums | assertion failure (3/7) | assertion failure (18/591) | +| W4b: unkeyed result keeps zero sums | assertion failure (2/7) | assertion failure (5/591) | +| W5: the last record replaces the first | assertion failure (3/7) | assertion failure (3/591) | +| W6: mixed unkeyed data uses raw values as keys | assertion failure (3/7) | assertion failure (4/591) | + +### Source mutants on the refactor (`b1599995`) + +| Mutant | New oracle | Suite | +| --- | --- | --- | +| N1: keyed values compare by `hash` | assertion failure (4/8) | assertion failure (4/588) | +| N2: join tuples are not unpacked | assertion failure (2/8) | assertion failure (47/588) | +| N3: keyed detection checks only the first record | failure by crash (3/8) | failure by crash (3/588) | +| N4: zero sums are kept | assertion failure (3/8) | assertion failure (21/588) | +| N5: the last record replaces the first | assertion failure (4/8) | assertion failure (4/588) | +| N6: mixed unkeyed data uses raw values as keys | assertion failure (3/8) | assertion failure (4/588) | +| W7: an input pair becomes the output entry | assertion failure (6/8) | assertion failure (14/588) | + +### Source mutants on the reviewed head (`a27600fd`) + +| Mutant | New oracle | Suite | +| --- | --- | --- | +| N7: the single-primitive path returns the input record (`b1599995`) | assertion failure (3/9) | assertion failure (3/589, oracle only) | +| N8: the keyed path writes a `-0` value as `0` in the input pair | assertion failure (2/9) | assertion failure (2/589, oracle only) | + +The N1–N6 and W7 counts come from `b1599995`, which had 8 oracle cases. +`a27600fd` changes only the single-primitive result and the loop that builds +the result, so those mutants apply unchanged. N7 and N8 survive the rest of +the db-ivm suite. N8 changes no output record, so only the input contents check +rejects it. + +N3 fails because the keyed path destructures a non-pair record and throws a +`TypeError`. This rejects the design, but by crash, not by a value assertion. +W7 is a risk that only the refactor introduces. Summing into a reused input pair +changes the input, and the input-immutability check rejects it. + +Each oracle run executes two campaigns of 300 generated histories each (fixed +seed and random), the positive execution witness, and six pinned witnesses. + +## Performance + +Consolidation runs on every graph step, so the change was timed. Each process +loads one implementation and times one workload set. Processes alternate +between implementations, and the comparison uses the median over five +processes of each. + +A first same-process harness showed a position bias. An identical copy of the +base code measured 6% to 22% slower when it loaded second. The +process-per-implementation harness removed that bias. Its A/A control measured +±2% on most workloads and up to ±7% on hash-heavy unkeyed objects. + +| Workload | n=10 | n=1,000 | n=100,000 | +| --- | ---: | ---: | ---: | +| keyed rows (update pairs) | 0.683 | 0.830 | 0.865 | +| keyed join tuples | 0.773 | 0.810 | 0.894 | +| keyed primitive values | 0.634 | 0.743 | 0.836 | +| unkeyed numbers | 0.991 | 1.008 | 0.763 | +| unkeyed objects | 0.821 | 0.802 | 0.824 | + +Each ratio is `a27600fd` time over base time. Lower is faster. Unkeyed numbers +at sizes 10 and 1,000 are inside the A/A noise band. Batches of size 10 +ran 20,000 times, size 1,000 ran 200 times, and size 100,000 ran twice. Output +sizes matched in every workload. The refactor keeps one `Map` of +`[record, sum]` entries per identity. The base code kept two parallel maps. The +harness is not checked in. + +## ORC-001 through ORC-012 + +| Requirement | Result | +| --- | --- | +| ORC-001 | The oracle's opening comment states the three identity rules, the retained record, zero-sum removal, input immutability, the authority, and the limits above. | +| ORC-002 | The model uses its own type-tagged identity and reference map. It does not import production identity helpers or `hash`. It restates the keyed/unkeyed classification because the contract comment on `consolidate()` states that classification. The `fallback` mode and mutants W3 and N3 attack that boundary. | +| ORC-003 | The contract, model (`expectedConsolidation`), grammar (`historyArb`, `buildRecords`), driver, and refinement check (`expectConsolidation`) are separate and visible in the oracle file. | +| ORC-004 | Reconstruction, ablation, range, and exclusion appear above. | +| ORC-005 | The driver calls the public `MultiSet` API. The positive witness shows that the fixed campaign reaches all five modes and many merges. | +| ORC-006 | The source mutants above have their outcome classes. | +| ORC-007 | One property runs a fixed campaign (seed `2026929`) and a random campaign with the same grammar, check, and budget (300 runs). `TANSTACK_DB_IVM_CONSOLIDATE_SEED` and `TANSTACK_DB_IVM_CONSOLIDATE_PATH` select a direct replay. The property does not use `fc.commands`. | +| ORC-008 | The model is a stateless recomputation, so this requirement does not apply. | +| ORC-009 | Model terms (keyed, unkeyed, identity, retained record) name production concepts directly. | +| ORC-010 | fast-check reports the seed, path, and shrunk history. Assertion messages name the identity that diverged. No external resources are acquired. | +| ORC-011 | No shared semantic fault calls for a second formulation. The #1948 collisions are known faults, and the grammar excludes them explicitly instead of sharing them silently. | +| ORC-012 | This record ties the reviewed revisions, outcome classes, limits, and performance evidence to the coverage map. | + +## Verification + +On `a27600fd`, the following passed: + +- `packages/db-ivm` Vitest: 41 files, 589 tests, no type errors. +- `packages/db-ivm` `tsc --noEmit`: no errors. +- `packages/db` Vitest, typecheck off: 193 files, 6,830 tests. +- `pnpm test:oracles`: 49 files, 2,838 tests. +- `pnpm test:minified-db`: error names, index metadata, query rows, and live + updates. + +The environment was Node `v24.19.0` and Vitest `3.2.4` on Darwin arm64. diff --git a/docs/contributing/oracle-reviews/code-weight-private-member-mangling.md b/docs/contributing/oracle-reviews/code-weight-private-member-mangling.md new file mode 100644 index 0000000000..63df3ee2d2 --- /dev/null +++ b/docs/contributing/oracle-reviews/code-weight-private-member-mangling.md @@ -0,0 +1,233 @@ +# Code weight: rename private members in the `@tanstack/db` build + +Reviewed executable revision: `9c422842` (base `c0d123b8`, the fetched +`origin/main` at review time). This record follows in a documentation-only +commit. The rename landed in `6dbf3982` on base `9125dab6`. Later commits fix +two review findings, merge `origin/main`, and add the dist smoke lane. + +## Change + +Consumer minifiers rename local variables but never rename properties. Long +TypeScript-private member names, such as `authoritativeRequestState` or +`truncateReplayState`, therefore reach every production bundle in full. The +`@tanstack/db` build now renames 368 of those members to short names. The +renaming happens in a `renderChunk` step of `packages/db/vite.config.ts`, which +runs esbuild `mangleProps` on each emitted ESM and CJS module. + +- `packages/db/mangle-cache.json` is the committed map from each original + name to its short name. It makes the renaming reversible and keeps the short + names stable across releases. +- The build step fails if esbuild renames any name that is not in the cache. +- `scripts/mangle-private-members.mjs` checks the cache (`pnpm check:mangle`) + and updates it (`--write`). `--write` keeps existing assignments, drops names + that are no longer safe, and gives new safe names the next free short names. +- `scripts/test-minified-db.mjs` now bundles the built `dist`, not `src`, and + rejects a `dist` that is older than `src`, the cache, or the package's build + configuration (`vite.config.ts`, `package.json`, `tsconfig.json`). CI builds db-ivm and + db before this lane and runs `check:mangle` first. + +The published declarations, the public API, and the source maps do not +change. Each source map still points at the original TypeScript, which the +package also publishes in `src`. `@tanstack/db-ivm` is not renamed: it already +uses native `#private` members, and a probe showed a gain under 0.5% gzip. + +## Bytes + +Each row bundles the built `dist` as a consumer would, with esbuild at target +`es2020`, and both builds sit under the same `package.json`, so both apply +`"sideEffects": false`. + +| Consumer entry | min | gzip | brotli | +| --- | ---: | ---: | ---: | +| full public API | −30,083 (−8.0%) | −2,917 (−2.7%) | −2,077 (−2.3%) | +| local-only collection with one insert | −10,951 (−8.4%) | −1,059 (−3.0%) | −718 (−2.3%) | +| collection with a filtered live query | −20,606 (−7.3%) | −1,901 (−2.4%) | −1,431 (−2.1%) | +| full public API, minified with terser | −30,074 (−7.9%) | −2,886 (−2.8%) | −1,869 (−2.2%) | + +The minified saving is large because each long name repeats in every bundle. +Gzip and brotli already encode most repeats as short back-references, so the +compressed saving is smaller. + +An earlier measurement of the minimal entry showed a gzip saving of 13%. That +was a harness fault: the unrenamed copy sat outside the package, so esbuild did +not apply `"sideEffects": false` to it and kept nine modules that have +top-level registration statements. With both builds under the same +`package.json`, the minimal entry saves 2.9% gzip. + +## Why the rename is safe + +esbuild renames every property with a cached name, anywhere in the output, +without regard to types. So `map.size` on a native `Map` would also change if +`size` were a cached name. The safety rule therefore covers every use of each +name, not only the uses on the private member. + +A name is safe when all of these hold: + +1. Every property-name use of it in `packages/db/src` resolves, through the + TypeScript checker, to a `private` class member declared in + `packages/db/src`. The uses include property access, object literal keys, + class members, constructor parameter properties, interface members, + destructuring, and shorthand properties. + An unresolved use (an `any` receiver) or a use that resolves to another + declaration (a lib type, db-ivm, an internal interface) makes the name + unsafe. +2. It never appears as a string literal in `packages/db/src`. esbuild does not + rename quoted keys. +3. No other package's `src` or tests read `.name` or a quoted `'name'`. Those + packages consume the renamed `dist`, so an untyped read would break. + +Each short name must also be unique and must not be an identifier anywhere in +db or db-ivm `src`, so a renamed member cannot collide with a real property. + +Of 470 private member names, 368 pass. The first text-based probe for this +change used public `.d.ts` output as a fourth filter. Every name that filter +alone removed appeared in `.d.ts` only in a comment, in a `private` +declaration, or as a function or constructor parameter name. None of those is +a property. The type-checked rule covers public +types directly, because a name in a public type has a non-private declaration. + +## Guard calibration + +Each row plants one hostile use of the cached name `authoritativeRequestState` +(short name `l`) and runs `pnpm check:mangle`. Every row exits with status 1 +and names the file and line. + +| Planted use | Guard result | +| --- | --- | +| C1: public object literal key in db src | `PropertyAssignment ... used at src/SortedMap.ts` | +| C2: untyped read `(globalThis as any).name` in db src | `unresolved use` | +| C3: string literal of the name in db src | `string literal` | +| C4: untyped read in `packages/react-db/src` | `used in packages/react-db/src/index.ts` | +| C5: an identifier equal to the short name `l` in db src | `l is an identifier in src` | +| C6: an interface member with the same name | `PropertySignature ... used at src/SortedMap.ts` | +| C7: a `public`, `readonly`, or `protected` constructor parameter property | `Parameter in packages/db/src/SortedMap.ts` | +| Control: a `private` constructor parameter property | passes (status 0) | + +CodeRabbit review of `3799ffa4` found that the first guard never visited +constructor parameters. A non-private parameter property with a cached name +passed the check in all three modifier forms. The guard now checks every +parameter property as a property declaration. No current source had such a +property, so the cache did not change. + +The minified lane keeps its three hostile controls. With the lane bundling +`dist`, the `new.target.name` control first passed silently, because its mutant +plugin matched `src/errors.ts`, which the bundle no longer loads. The plugin +now matches `dist/esm/errors.js`, and all three controls fail at their intended +assertions: + +| Control | Result | +| --- | --- | +| `--calibrate-error-name` | assertion failure: `CollectionConfigurationError.name` | +| `--calibrate-public-member` | assertion failure: `public index metadata method` | +| `--calibrate-output-shape` | assertion failure: query rows differ | +| a source file newer than `dist` | error: `packages/db/dist is older than packages/db/src/SortedMap.ts` | +| `vite.config.ts`, `package.json`, `tsconfig.json`, or the cache newer than `dist` | error naming that file | + +An external review of `a1d2b85b` found that the freshness check did not +include the build configuration. With only `vite.config.ts` touched after a +build, the lane passed against output from the older pipeline. The check now +includes the configuration files. Each input was touched alone after a fresh +build, and each one fails the check with its own path. + +## Behavior evidence + +The rename changes no source, so the db suite, which runs against `src`, +cannot observe it. Every package that depends on `@tanstack/db` resolves it +through the workspace to the built `dist`, so their suites run against the +renamed output. On `6dbf3982`, with the 368-name cache: + +| Package | Files | Tests | +| --- | ---: | ---: | +| `@tanstack/angular-db` | 2 | 57 passed, 1 todo | +| `@tanstack/db-sqlite-persistence-core` | 9 | 369 passed, 1 todo | +| `@tanstack/electric-db-collection` | 13 | 585 | +| `@tanstack/offline-transactions` | 17 | 202 | +| `@tanstack/powersync-db-collection` | 9 | 173 | +| `@tanstack/query-db-collection` | 15 | 532 | +| `@tanstack/react-db` | 13 | 286 | +| `@tanstack/rxdb-db-collection` | 1 | 12 | +| `@tanstack/solid-db` | 2 | 81 | +| `@tanstack/svelte-db` | 6 | 107 | +| `@tanstack/trailbase-db-collection` | 3 | 67 | +| `@tanstack/vue-db` | 5 | 109 | + +These 2,580 tests pass. `test:minified-db` also passes against the renamed +`dist`. The run used the working tree just before `6dbf3982`. That commit +changes only formatting and one lint fix in `vite.config.ts`, and its built +`dist` is byte-identical to the verified build. After the merge of +`origin/main` at `7b8142fc`, CI's Test job ran every consumer suite against +the rebuilt, renamed `dist` and passed. + +### db tests against the renamed dist + +The consumer suites reach db only through other packages. `pnpm --filter +@tanstack/db test:dist` also runs five of db's own test files against the +built `dist`. `packages/db/vitest.dist.config.ts` maps each `src` import to the +matching built module, which exists because the build emits one module per +source module. Modules without runtime code (re-export barrels and type-only +files) are not emitted, so those load from `src`, and their imports then +resolve to `dist`. Any other unbuilt module throws. A planted `throw` in the +built `collection/index.js` showed that the lane loads `dist`. + +The five files were chosen for coverage of the 14 modules that hold about 90% +of the renamed members. Coverage is measured on the built modules and mapped +back to source. Files that read renamed members directly were excluded, +because they would test the rename map instead of behavior. + +| File | New key-module statements | Cumulative | +| --- | ---: | ---: | +| `query/ordered-source-loader-state.test.ts` | 1,951 | 37.4% | +| `live-query-observer.test.ts` | 641 | 49.8% | +| `query/includes-publication-oracle.test.ts` | 479 | 58.9% | +| `query/bucket-facade-adapter.test.ts` | 178 | 62.4% | +| `collection-subscription.test.ts` | 179 | 65.8% | + +The five files run 254 tests in about 3 seconds. All 14 measured candidates +together reached only 72.6%. `live-query-window-controller` has no coverage +from db tests that avoid its internals. The framework adapters' infinite-query +suites exercise it, and they run against `dist` in CI's Test job. + +Calibration: in the built `ordered-source-loader.js`, the first of 26 `.l` +accesses was restored to `.authoritativeRequestState`, which is an inconsistent +rename. The lane then failed 48 of 254 tests in 2 of 5 files. With the file +restored, all 254 tests pass. A source file newer than `dist` makes the lane +throw the freshness error before any test runs. + +## Costs + +- `dist` JavaScript is harder to read directly (`this.ay`). Source maps and the + cache decode it. Stack traces that are not source-mapped show short method + names for renamed private methods. +- esbuild reprints each emitted module. The reprint keeps `@__PURE__` + annotations (299 before and after) and legal comments, and drops other + JavaScript comments. The `.d.ts` documentation does not change. +- The db build takes about one second longer. + +## Limits + +- The rule is static. Reflection that enumerates instance properties (for + example `Object.keys(collection)` or `JSON.stringify` of a class instance) + would see short names. No such use exists in the checked packages, and the + consumer suites pass, but a user who inspects internal instances this way + would see different keys. +- A private member added later is not renamed until someone runs `--write`. + That costs bytes but is never unsafe. +- `check:mangle` reads other packages in this repository only. Code outside + the repository that reads `@tanstack/db` internals through `any` is + unsupported and can break. + +## Verification + +On `9c422842`: + +- `pnpm check:mangle`: 368 names. +- `pnpm --filter @tanstack/db test:dist`: 5 files, 254 tests. +- `pnpm test:minified-db` against the built `dist`: error names, index + metadata, query rows, and live updates. +- `packages/db` Vitest, typecheck off: 193 files, 7,164 tests. +- `pnpm test:oracles`: 49 `@tanstack/db` files with 2,847 tests, and 16 + `@tanstack/query-db-collection` files with 454 tests and 1 todo. +- The consumer suites in the table above ran on `6dbf3982`. On `7b8142fc`, + CI's Test job ran them again against the merged, renamed `dist`. + +The environment was Node `v24.19.0` and Vitest `3.2.4` on Darwin arm64. diff --git a/docs/contributing/oracle-reviews/comparison-unit-retirement-round-5.md b/docs/contributing/oracle-reviews/comparison-unit-retirement-round-5.md new file mode 100644 index 0000000000..337c0c7738 --- /dev/null +++ b/docs/contributing/oracle-reviews/comparison-unit-retirement-round-5.md @@ -0,0 +1,72 @@ +# Comparison unit retirement: non-finite default order + +A source-isolated Field Lab loss audit compared `comparison.test.ts` with +`comparison-oracle.property.test.ts` at frozen head +`f6857fee9de59dc82cbe596ca7df4a18152004ed`. The receiver initially +excluded NaN and invalid Dates and did not observe `defaultComparator` sorting. +The audit froze the five blocks below before retirement. The remaining three +unit tests cover symbol order and `compareValues` behavior at another entry +point. + +| Retired assertion | Receiving check | +| --- | --- | +| `ascComparator(NaN, 5)` is positive and the reverse is negative. | Tagged finite and non-finite number pairs check both signs. | +| `ascComparator(NaN, NaN)` returns exact zero. | Duplicate non-finite pairs check `toBe(0)`, including the sign of zero. | +| Sorting `[3, NaN, 1, 5, NaN]` puts the finite values in order and both NaNs last. | A fixed tagged history predicts every stable sorted row ID. | +| Sorting `[5, NaN, null, 1]` puts null first and NaN last. | Fixed and generated histories combine optional null with non-finite values. | +| An invalid Date sorts after a valid 2023-01-01 Date in both directions. | A fixed history preserves that day; generated valid epoch days and invalid Dates check both signs and sorted IDs. | + +Changelog #1582 promises PostgreSQL-style NaN and invalid-Date ordering. +`DEFAULT_COMPARE_OPTIONS` and `defaultComparator` supply the default ascending, +nulls-first path. The independent model receives tags for null, finite, and +non-finite values; it compares tag rank and supplied finite values. It never +uses production's unorderable classifier to predict a result. The driver calls +`ascComparator` and sorts with `defaultComparator`. It checks direct signs and +exact zeros at return, then compares every ordered row ID after sorting. + +The generated grammar uses 1–5 distinct finite numbers or valid Dates in +epoch-day range -20,000..20,000, one or two NaNs or invalid Dates, and optional +null. Number and Date histories remain separate. Mixed number/Date order, +Temporal values, symbols, other options, `compareValues`, and public query or +index paths are outside this transfer. + +## Review and replay + +The direct wrong-result control ties a non-finite value to a finite value and +fails the sign check. A separate wrong-sort control ties NaN during sorting +and fails the ordered-ID check. Fixed-seed and seedless campaigns share the +same 100-case property. Direct property 23 replay at seed `20260911`, path +`0`, passed. + +The latest hostile reviewer read the retired unit blocks, ledger, model, +production comparator, and coverage map. It found no lost assertion at the +declared direct and sort checkpoints. The focused comparison suite passed +100 tests with no type errors. Earlier PR revisions explored cross-realm and +long-byte behavior; those experiments are outside this transfer and are not +claimed by this record. + +## Oracle guide boundary + +The opening law and ledger state authority and limits (ORC-001). Tagged +finite values and source ranks give an independent computation (ORC-002); +the oracle places law, model, grammar, driver, observation, and checkpoint +beside their code (ORC-003). Fixed operands reconstruct the five old blocks, +and generated finite, duplicate non-finite, and null cases exercise nearby +histories (ORC-004). Direct comparator returns and sorted row IDs are the +declared observations (ORC-005). The two wrong-result controls +fail at those comparisons (ORC-006 and ORC-013). The replay above checks the +shared fixed-seed and seedless harness (ORC-007 and ORC-010). Model tags are +input labels, with no state transition or host provider (ORC-008, ORC-009, +and ORC-014). This ledger records the loss audit, hostile challenge, and +bounded result (ORC-011 and ORC-012); it makes no claim about other value +domains. + +## Exact code-commit review + +At `24a61a8d5b6cef06d488c490e39f351b4d55fb68`, a fresh hostile reviewer +compared all five retired blocks with the committed tagged model, direct +comparator checks, and sorted row IDs. It found no lost assertion. The +committed production and oracle blobs match the reviewed working tree; the +commit hook did not change them. A focused comparison and evaluator run +passed 236 tests with no type errors. This verdict does not extend to +`compareValues` or public query and index paths. diff --git a/docs/contributing/oracle-reviews/comparison-unit-retirement-round-6.md b/docs/contributing/oracle-reviews/comparison-unit-retirement-round-6.md new file mode 100644 index 0000000000..c503924e11 --- /dev/null +++ b/docs/contributing/oracle-reviews/comparison-unit-retirement-round-6.md @@ -0,0 +1,72 @@ +# Comparison unit retirement: Temporal order + +A source-isolated Field Lab loss audit compared two Temporal blocks in +`packages/db/tests/comparison.test.ts` with the comparison oracle at frozen +head `c038b2b673a601d3bfbc70261f2cbb61f6f934ea`. The receiver had no +Temporal input. These four observations were frozen before the blocks were +removed. The symbol block and two `compareValues` NaN blocks stay in the unit +file because they have different obligations. + +| Retired block | Original observation | Receiving oracle | +| --- | --- | --- | +| PlainDate calendar order | January 2024 precedes June 2024; reversing the operands is positive; a fresh January value compares at exact zero. | Supplied ISO year, month, and day fields predict both signs and exact equal-date zero. Fixed operands reconstruct January and June; generated fields vary each part. | +| ZonedDateTime instant order | 06:30 UTC and 12:00 +05:30 representations of one instant compare at exact zero. | Supplied epoch-nanosecond offsets predict both signs and zeros across four offset zones, including one-nanosecond neighbors. | + +Changelog #1519 promises Temporal static comparison for `orderBy` and range +operators. It specifies instant order for ZonedDateTime, while `eq` retains +zone as part of identity. The receiver models PlainDate calendar fields and +ZonedDateTime instants independently of production dispatch and Temporal's +static comparator. Its driver builds ordinary values from one polyfill copy, +calls `ascComparator` with default options, and compares each direct return. +It does not claim `compareValues`, index, public query, or provider behavior. + +The grammar uses ISO PlainDates in years 2000–2030 with days 1–28. It uses +ZonedDateTimes within two seconds of a fixed 2024 instant in UTC, +05:30, +-05:00, and +14:00. Equal-value branches create separate objects; adjacent +instants differ by one nanosecond. Mixed Temporal kinds, unsupported +PlainMonthDay order, subclasses, altered constructors or prototypes, and +mixed native/polyfill implementations are outside this transfer. + +## Review and replay + +The fixed cases reconstruct all four old assertions. A wrong comparator +that uses zoned display text fails the equal-instant sign check; a +millisecond-truncating comparator fails the adjacent-instant check. ISO +PlainDate display text has the same order as the tested calendar fields, so +the text-order control only distinguishes the zoned law. The generated +properties run fixed-seed and seedless 100-case campaigns. Direct replays +38 and 39 at seed `20260911`, path `0`, passed. + +The latest hostile reviewer read the retired assertions, model, production +comparator, and coverage map. It found no lost assertion within the direct +`ascComparator` scope. The focused comparison suite passed 100 tests with +no type errors. The production change removes a tag-wide dispatch cache. +For ordinary Temporal objects this preserves the required static comparison +while reducing code. Earlier PR revisions tested caller-modified constructors, +spoofed tags, and subclasses; the maintainer declined guarantees for modified +standard values, and those witnesses are not part of this transfer. + +## Oracle guide boundary + +The opening contract identifies #1519 and the direct-return limit (ORC-001). +Supplied fields and offsets determine the expected result without importing +production classification or comparison (ORC-002). The oracle places model, +grammar, driver, observation, and comparison beside explanatory prose +(ORC-003). Fixed operands and generated year, month, day, zone, equal-instant, +and adjacent-nanosecond dimensions cover the bounded grammar (ORC-004). +`ascComparator` returns are checked immediately (ORC-005). Wrong text and +truncated-instant results fail those checks (ORC-006 and ORC-013). Direct +seed/path replay verifies the shared campaign harness (ORC-007 and ORC-010). +The tuple and instant model has no mutable state or host provider (ORC-008, +ORC-009, and ORC-014). This record preserves the loss audit, hostile review, +and explicit limits (ORC-011 and ORC-012); it makes no cross-boundary claim. + +## Exact code-commit review + +At `24a61a8d5b6cef06d488c490e39f351b4d55fb68`, a fresh hostile reviewer +reconstructed the three PlainDate assertions and equal-instant zoned +assertion from the committed oracle. The comparator and oracle blobs match +the versions reviewed before commit. Its focused comparison and evaluator +run passed 236 tests with no type errors. It found no lost ordinary +same-realm assertion. Modified Temporal inputs, `compareValues`, and public +query or index paths remain outside this verdict. diff --git a/docs/contributing/oracle-reviews/dec-03-window-overlap.md b/docs/contributing/oracle-reviews/dec-03-window-overlap.md new file mode 100644 index 0000000000..6ce67d1157 --- /dev/null +++ b/docs/contributing/oracle-reviews/dec-03-window-overlap.md @@ -0,0 +1,197 @@ +# DEC-03 window-overlap oracle review + +Reviewed semantic head: `f42fbcf4a642bc936e91309241137325c6f034e3` (branch `codex/audit-dec-03`). +Base: `33a194941c8d51f8f98babb999fef2987dd6ff8b`. +Owner: `packages/db/tests/conformance/infinite-suite.ts`, instantiated by the +React, Vue, and Svelte receiving drivers. + +## Decision and boundary + +The maintainer chose this rule: after a failed preload overlaps a successful +page request, `hasNextPage` follows the latest committed page count. The failed +preload's error remains visible until explicit recovery. The earlier behavior +forced continuation to `false` on the later page success. The fix recomputes +continuation in the asynchronous page-success branch. + +The bounded contract is finite ordered source rows × a page size of two × a +failed initial-window preload × a successful second-page window × a public +snapshot after each settlement. Four rows distinguish exact exhaustion; five +rows distinguish a remaining row. The two settlement orders distinguish a +failure followed by success from success followed by failure. Each history also +checks explicit recovery. The model derives visible rows, page boundaries, +params, continuation, status, and error from the source length, committed page +count, and the chosen error lifetime. It does not copy the controller's +`failedHasNextPage` cache. + +The production driver calls the exported DB window controller in each package +realm. It uses a real ordered live-query Collection and controls the two +`setWindow` results with Promises. It checks the exact limits 3 and 5, then +compares public snapshots at 12 checkpoints per driver. Current framework +hooks expose `fetchNextPage` but no `preload`, so this test does not establish +the same overlap through a hook. Adjacent shared scenarios retain hook paging +coverage. + +## RED, GREEN, and hostile controls + +On unchanged production, the new shared cell failed in React, Vue, and Svelte +at the failure-first, five-row final checkpoint: expected `hasNextPage: true`, +received `false`. This was an assertion failure on the public snapshot. After +the one-branch production change, the DB controller suite passed 75/75 and the +package-local conformance suites passed React 36/36, Vue 38/38, and Svelte +38/38. The DB build, changed-file ESLint, Prettier, and `git diff --check` +passed. Package-local Vitest configuration is required for the React and Svelte +receiving suites. + +Temporary wrong-design controls were built and tested, then removed: + +- Restoring the asynchronous success assignment to `false` failed at the + five-row continuation assertion. +- Leaving the failed continuation frozen through asynchronous success failed + at the four-row exact-exhaustion assertion (`true` versus `false`). +- Clearing the error on later success failed the earlier Error identity + assertion (`undefined` versus the sentinel Error). + +A review suggested testing an additional synchronous page-success branch. A +legal synchronous-success cell was reached by preparing the physical five-row +window first. It passed with the synchronous assignment changed back to +`false`: that mutant survived. The assignment was therefore removed from the +fix. At synchronous success, `requestPageCount` has cleared an earlier error; +a still-pending preload failure can run only after that synchronous call and +then recomputes continuation itself. The surviving mutant and execution order +make the synchronous assignment irrelevant to the reported public failure +state. This does not claim all synchronous window behavior is covered. + +## ORC-001 through ORC-011 + +| Requirement | Outcome | +| --- | --- | +| ORC-001 authority and limits | Pass. The maintainer selected the overlap rule. The executable contract and coverage map name the direct-controller boundary and hook limit. | +| ORC-002 independent judgment | Pass. Expected continuation comes from source length and committed page count. The model imports no production window classifier or failure cache. | +| ORC-003 visible responsibilities | Pass. The shared contract, pure expected snapshot, bounded matrix, production driver, and public comparisons are adjacent in the shared suite and its contract module. | +| ORC-004 generated grammar controls | Not applicable. The four histories are an explicit finite matrix, not a generated-history or input-grammar claim. | +| ORC-005 path and observation | Pass. The driver asserts both physical window limits and observes rows, pages, params, continuation, fetching state, status, and error after each controlled settlement and recovery. | +| ORC-006 checker calibration | Pass for the asynchronous overlap. The three controls above failed by assertion at the intended public checkpoints. The synchronous assignment mutant survived and is reported as such. | +| ORC-007 fixed/random campaigns | Not applicable. This is a bounded scenario, not an important generated property. | +| ORC-008 model minimality | Not applicable. Expected snapshots are stateless recomputation from scenario inputs; no mutable reference-model state changed. | +| ORC-009 vocabulary mapping | Pass. `pageSucceeded` represents two committed pages; `preloadFailed` represents the observable retained pagination error. The model does not represent physical lease state. | +| ORC-010 failure fidelity and cleanup | Pass. The scenario resolves both held gates in `finally`, waits for both operations, disposes the controller, and restores the spy. `ScenarioLifetime` cleans the dependent live query before its source and preserves a primary assertion if cleanup also fails. No shrinking is involved. | +| ORC-011 second formulation | Not applicable. No additional shared-fault hypothesis was identified that a second formulation would distinguish within this direct-controller boundary. Four and five rows already reject opposite wrong continuation rules. | + +This record supplies ORC-012 evidence for semantic head `f42fbcf4a642bc936e91309241137325c6f034e3`. +It claims closure only for a failed initial preload overlapping an asynchronous +second-page success on finite ordered local rows at the stated checkpoints. +The coverage map retains framework hook scheduling and other acquisition +paths outside this owner; a reachable counterexample there would require a +separate witness. + +## Follow-up: subscribed source updates under a retained error + +Reviewed semantic head: `29d46e184981a2841d60e814904571642c81a021`. +This section preserves the earlier exact-head decision above. It extends the +same direct-controller boundary after both overlap settlements, while the +earlier pagination error remains visible and a controller subscriber is active. + +The source-length model now checks a later insertion of the fifth row after a +four-row overlap and a later removal of the fifth row after a five-row overlap. +The observer must first publish the changed rows in the live-query Collection. +At that checkpoint, the public controller snapshot must retain the original +error and update `hasNextPage` to match the committed two-page prefix and the +current source extent. Both settlement orders are included, followed by +explicit recovery: four histories and 16 public checkpoints per receiving +driver. An unsubscribed controller has no observer-update claim. + +On the prior head, insertion produced five published rows and retained the +error but reported `hasNextPage: false` instead of `true`; removal produced +four rows and reported `true` instead of `false`. Both were public assertion +failures. The permanent shared conformance cell failed on the original +implementation at the first insertion checkpoint. The fix refreshes the +cached continuation on a ready observer notification while a pagination error +is visible, without changing the error lifetime. After the fix, targeted +overlap cells passed in React, Vue, and Svelte; the DB controller suite passed +75/75, and full receiving suites passed React 36/36, Vue 38/38, and Svelte +38/38. Changed-file ESLint, Prettier, and `git diff --check` passed. + +Two temporary synchronous-success probes also reached literal-`true` page +success. One held an earlier preload failure until after synchronous page +commit; that later failure recomputed continuation correctly. The other began +with a visible error, which cleared before synchronous page commit. Both +passed and were removed. They support the earlier record's narrow statement +that the surviving synchronous-assignment mutant does not cause the reported +retained-error failure; they do not claim complete synchronous-path coverage. + +This follow-up supplies ORC-012 evidence for semantic head +`29d46e184981a2841d60e814904571642c81a021`. It closes the subscribed, +ready-observer source-update cell for these finite ordered rows and public +checkpoints. Framework hook overlap scheduling and other acquisition paths +remain outside the claim, as recorded in the coverage map. + +## Follow-up: detached snapshot reads under a retained error + +Reviewed semantic head: `fa5a3185` (branch `codex/audit-dec-03`). The earlier +sections retain their exact-head claims. This follow-up extends the same shared +conformance owner to a controller with no active subscriber. The preloaded live +query retains a five-row physical window. After a subscribed source update and +unsubscribe, a second source write reverses the fifth row's presence. A direct +`getSnapshot()` read must derive continuation from those current rows while +preserving the earlier error until explicit recovery. Four or five source rows +cross both settlement orders. The shared model still computes its answer from +source length and committed page count, not the controller's cached value. The +cell now checks 20 public snapshots in each receiving driver. + +At parent head `0b0ca99c`, React's public snapshot had the correct four rows +and retained error after the detached removal, but `hasNextPage` remained +`true` instead of `false`. The same oracle was green after moving continuation +recomputation from the subscriber callback into `getSnapshot()`. It also covers +the reverse four-to-five-row transition. The DB controller suite passed 75/75, +and the full receiving suites passed React 36/36, Vue 38/38, and Svelte 38/38 +before the final one-line simplification. The DB package was built before the +Svelte run, which imports its built entry point. After that simplification, +the DB suite passed 75/75 and the focused shared cell passed in all three +receiving drivers. Changed-file ESLint, Prettier, and `git diff --check` passed. + +A separate temporary control reached the synchronous page-success assignment +with a held earlier preload failure. Replacing that assignment with a throw +failed at the intended line; changing its value from `false` to `true` left the +public error snapshot correct because the later failure recomputed +continuation. Both controls were restored, then the dead synchronous write was +removed. This supports only the stated execution order; it does not prove +every synchronous history equivalent. + +The rereview's broader explanation that every error-entry path recomputes +continuation is too strong. The `requestPageCount` synchronous throw and +asynchronous rejection paths preserve a captured `previousHasNextPage` value. +Those paths do not read the dead synchronous assignment. Their distinct +overlap histories are not established by the diagnostic probe above. + +ORC-001 through ORC-003 remain grounded in the maintainer's overlap decision, +the source-length model, and the adjacent shared contract/model/driver/check. +ORC-004 and ORC-007 remain inapplicable because the four histories form an +explicit bounded matrix. ORC-005 reaches the exported controller in all three +package realms and compares rows, pages, params, continuation, fetching, +status, and error at the detached read. ORC-006 is supported by the original +RED result and the same-path synchronous controls above. ORC-008 is +inapplicable because the reference has no mutable state. ORC-009 maps +`pageSucceeded` and `preloadFailed` as in the earlier section. ORC-010 retains +the scenario's controlled gate release and cleanup. ORC-011 remains +inapplicable within this direct-controller boundary because no new plausible +shared semantic fault requiring a second formulation was identified. This +section supplies ORC-012 evidence for the named semantic head. + +The added claim covers a detached `getSnapshot()` after the preloaded source +publishes a change within a retained physical window. It makes no notification +claim without a subscriber. Hook scheduling, arbitrary provider acquisition, +and other source-state transitions remain outside this cell. + +## Post-merge validation + +Semantic head `5dfd32c7` merges current `origin/main` through `e16d46ce` +without changing the controller or its oracle. On that merged head, the DB +build passed; the controller suite passed 75/75; and the full React, Vue, and +Svelte receiving suites passed 36/36, 38/38, and 38/38. Changed-file ESLint, +Prettier, and diff checks passed. The full PR's controller source diff against +the merge base is 9 additions and 10 deletions: net minus one production line. + +Main later advanced to `68f0b65c`. Merge head `3e930add` preserved the +window-controller coverage row and main's new hash-oracle row. The controller +and shared conformance owner did not change in this merge. The DB build and +full DB, React, Vue, and Svelte suites passed again at the same counts. diff --git a/docs/contributing/oracle-reviews/failed-transaction-retention.md b/docs/contributing/oracle-reviews/failed-transaction-retention.md new file mode 100644 index 0000000000..38131b1572 --- /dev/null +++ b/docs/contributing/oracle-reviews/failed-transaction-retention.md @@ -0,0 +1,364 @@ +# Failed transaction retention review + +Evidence by revision, on `fix-failed-transaction-retention`, based on +`origin/main` `2ab7f3e55`: + +- First campaign: oracle commit `fb9a682e9`, fix `89de5ea0a`. It removed a + transaction in both `isPersisted` handlers. +- Review follow-up: `f777321fd` (tests) and `64f1c1045` (fix), plus + `8274b81f3`, which keeps an owned transaction's unobserved rejection + handled. They move removal into the recompute pass, remove + `scheduleTransactionCleanup`, and make `touchCollection()` recompute every + Collection before it rethrows. The mutant results and CI counts below ran on + `8274b81f3`. The + RED results for findings 1, 2 and 5 below were run on `89de5ea0a`, and + finding 5 also on `2ab7f3e55`. +- Medium-review follow-up (PR head `609e97162`, findings M1–M10 below): the + owning-Collection set, removal in the overlay pass, settlement that survives + a throwing conflicting rollback, and the transaction-ownership oracle. RED + runs used `609e97162` and `origin/main` `07ffcfe47`. + +## Contract and evidence + +A Collection tracks its optimistic transactions in `state.transactions`. Every +per-mutation pass walks that map: `recomputeOptimisticState`, +`overlayActiveTransactions`, `hasPersistingTransaction`, +`commitLocalOnlyDirect`, and the Query Collection's +`isMutationPublicationBlocked`. A settled transaction must leave the map, +whether it succeeded or failed. Otherwise per-mutation cost grows with history, +not with live work. + +The law has three parts: + +1. After its Collections recompute, a Collection tracks exactly its unsettled + transactions. A completed row that a queued sync transaction still holds + stays in the held-row layer (`heldOptimisticRows`), which records it during + the recompute. The transaction itself does not stay tracked for it. +2. Removing an entry never removes a different transaction. `createTransaction` + accepts a caller id, and offline restoration reuses ids, so two + transactions can share an id over time. +3. One mutation's work does not depend on how many transactions settled + before it. + +Authority: no reader needs a settled transaction. Every loop over the map skips +`failed` entries. A completed entry is read once, by the recompute that +records its held rows. Readers outside `packages/db` are the offline-transactions +restore path and an Electric test that reads a completed transaction. + +## Design + +Removal now happens in `recomputeOptimisticState`. The pass that records a +completed transaction's held rows collects every settled entry and deletes those +entries after the loop. Deleting during the loop would skip the next entry, +because `SortedMap.delete` splices the key array that the loop iterates. + +Every way into the map settles through a recompute: a commit or rollback calls +`touchCollection()`, and offline restoration calls `rollback()` or recomputes +itself. So the sweep covers offline restoration without a separate cleanup +path. `scheduleTransactionCleanup` and its per-`mutate()` handler registrations +are gone. + +The sweep deletes by map entry, so a same-id successor that replaced its +predecessor's entry stays tracked. + +The removed `isPersisted` handlers also kept a rejected `isPersisted` promise +from reaching `unhandledrejection`, as `main` does. Every transaction now +marks the promise handled when it is created, so an empty transaction that no +Collection tracks is covered too. Without that, the db +and offline-transactions suites reported 48 and 154 unhandled rejections while +every test passed. + +`touchCollection()` now recomputes every Collection of a settled transaction +before it rethrows the first error. Before, a throwing subscriber in one +Collection left the next Collection showing the settled transaction's +optimistic row. This was a pre-existing visibility bug. + +## Oracle changes + +- The optimistic-history driver (`optimistic-history-oracle.ts`) now checks, + after every step, that `collection._state.transactions.size` equals the + model's persisting transactions. The first campaign used "at most persisting + plus held", which could hide a retained failed transaction while a held one + existed, and could not detect an early removal. The opening prose states the + law. +- `collection.test.ts` adds these focused witnesses: + - 200 rejected updates and manual rollbacks leave nothing tracked. + - One mutation counts the same Map and Set steps, including `Map#get`, after + 0 and after 200 rollbacks. This is the public cost law; it does not read + the internal map. + - A same-id successor stays tracked and visible after its predecessor rolls + back. + - Three transactions that settle before one recompute all leave the map. + - A rollback across two Collections recomputes the second Collection when + the first one's subscriber throws. +- `offline-e2e.test.ts`: a restored transaction that fails permanently is no + longer tracked after its rollback. + +## Results + +| Check | Before | After | +| --- | --- | --- | +| Optimistic oracles, 6 files | `origin/main`: 37 of 323 fail at a failed settle | 556 pass with the exact law | +| Same-id successor | `89de5ea0a`: `expected undefined to be Transaction`; `2ab7f3e55`: pass | Pass | +| Offline restoration rollback | `89de5ea0a`: `expected 1 to be +0` | Pass | +| Two-Collection rollback with a throwing subscriber | `89de5ea0a` and `2ab7f3e55`: the second Collection keeps the rolled-back row | Pass | +| Tracked after 200 rollbacks | `origin/main`: 200; `89de5ea0a`: up to 49 between flushes | 0 | + +The first campaign measured 4,000 mutate and rollback cycles on 10k rows: the +median per cycle for the last 200 cycles was 0.67–0.94 ms on `origin/main` and +0.008 ms on the branch. `scripts/bench/incremental-update.ts` gave a write +geomean of 0.88× and 0.84× against `origin/main`, with same-code noise at 1.03×. +The bench rolls back after each optimistic iteration, so its numbers on `main` +include this leak. + +## Mutants + +All mutants ran on the follow-up revision. + +| Mutant | Outcome (ORC-006) | +| --- | --- | +| Remove by id when `isPersisted` settles (the first campaign's design) | Assertion failure: the same-id witness | +| Failed transactions never removed | Assertion failure: 51 tests, the offline witness, and the work witness (443 steps against 34) | +| Keep a failed transaction while a held sync transaction exists | Assertion failure: 24 tests | +| Remove a persisting transaction early | Assertion failure: 91 tests | +| Delete settled entries during the recompute loop | Assertion failure: the adjacent-settled witness only. The generated grammar never settles two transactions before one recompute. | + +## ORC-012 requirement audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Applicable. The law and authority are above. The claim covers a Collection's tracked transactions; it does not bound transaction-scope state in `transactions.ts`. | +| ORC-002 | Applicable. The expected count comes from the model's transaction states, not from production. | +| ORC-003 | Applicable. The opening prose of the history oracle states the law beside the model, and the driver states the observation. | +| ORC-004 | Applicable, with gaps. The history grammar generates failed settles, cascading rollbacks, held sync transactions and truncates over one Collection. The ownership grammar adds two Collections, merged-away pairs, truncate-listener rollbacks and throwing subscribers. Same-id instances, offline restoration and two settlements before one recompute have focused witnesses. | +| ORC-005 | Applicable. The driver reads `_state.transactions`, the semi-public observation used by `collection.test.ts` and the local-only direct-write oracle. The work witness checks the public cost law without that field. | +| ORC-006 | Applicable. See the mutant table. | +| ORC-007 | Applicable. The history campaigns keep their fixed and random seeds. The ownership oracle runs a fixed seed (2080) and a random or replayed seed through `oraclePropertyOptions`, with the same property and budget. | +| ORC-008 | Applicable. The ownership model keeps each transaction's owning Collections separately from its writes, because a merged-away pair leaves an owner with no write; the pinned history distinguishes them. | +| ORC-009 | Applicable. "Unsettled" maps to the glossary's persisting state; the model has no separate pending state. | +| ORC-010 | Applicable, with a gap. The ownership driver cleans up in `finally`, so a cleanup error after an assertion failure replaces it. No run showed a cleanup failure. | +| ORC-011 | Applicable. The reviewer named a shared fault: an id-keyed removal. The same-id witness is the independent check. | +| ORC-013 | Inapplicable. No threshold law. | +| ORC-014 | Inapplicable. No controlled provider. | + +## Medium-review follow-up + +The review of `609e97162` found that removal reached only the Collections that +the transaction's current mutations touch, while the removed `isPersisted` +cleanup had reached every Collection that ever tracked it. + +| ID | Finding | RED | Fix | +| --- | --- | --- | --- | +| M1 | A conflicting transaction's rollback throws, so the primary never settles | `tx1Settled:false, bValue:1, bTracksTx1:true` on `609e97162` and `main` | `rollback()` runs every step, then rethrows a subscriber error | +| M2 | Mutations that merge away leave the Collection tracking the transaction | Tracked after commit on `609e97162`; released on `main` | The transaction records each Collection that tracked it; settlement, including an empty commit, recomputes all of them | +| M3 | Two Collection instances with one id | The first instance keeps the transaction on `609e97162`; both keep it on `main` | `touchCollection()` reaches instances, not ids | +| M4 | A rollback in a `truncate` listener during a sync commit | Tracked on `609e97162` and `main` | Removal moved into `overlayActiveTransactions`, which the sync commit calls after it skips recomputes | +| M5 | Only the first settlement error survives | — | Accepted after the simplification below: these failures are rare, and every step still runs | +| M6 | The history oracle compared a count | A wrong-identity removal could keep the count | It compares transaction ids | +| M7 | The offline witness yielded a microtask before asserting | — | The yield is gone; the row and the entry leave in one recompute | +| M8 | The work witness patched `Map` and `Set` globally and asserted equality | — | It counts walks of the Collection's own `transactions`, warms up first, and asserts a bound | +| M9 | `as any` in the work witness | — | `Reflect.get` and `Reflect.set` | +| M10 | Each removal splices `sortedKeys` | — | Kept. A recompute normally removes one entry. A local measurement deleted k entries from a `SortedMap` of n: at n = 1,000 and k = 1, splicing took 1.3 ms against 64 ms for a deferred rebuild over the same 2,000 runs; splicing stays cheaper until k nears n / 10. | + +The transaction-ownership oracle +(`packages/db/tests/transaction-ownership-oracle.property.test.ts`, registered +as `transaction-ownership.settlement-release`) owns the law across two +Collections. It runs a fixed campaign (seed 2080) and a random or replayed +campaign, 200 runs each, plus a pinned history. Its grammar covers writes, a +pair that merges away, commits, successful and failed settlements, rollbacks, +truncate-listener rollbacks, and a throwing subscriber during a settling step. +After each synchronous settling step it compares the tracked ids in the same +call stack, then compares tracked ids, rows and `isPersisted` settlement after +promises flush. The model's first draft treated an update that keeps the +visible value as a write; production creates no mutation for it, which the +contract permits, so the model now skips it. + +| Mutant | Outcome | +| --- | --- | +| A persisting transaction counts as settled | Assertion failure, 15 tests | +| Only the current mutations' Collections recompute | Assertion failure: all three ownership campaigns and the same-id witness | +| `touchCollection()` deduplicates by Collection id | Assertion failure: the same-id witness only | +| No removal during a sync commit | Assertion failure: all three ownership campaigns | +| A conflicting rollback's throw skips the primary's settlement | Assertion failure: the pinned ownership history only | +| Removal one microtask late | Assertion failure: the same-call checks and two focused witnesses | +| Never remove (`main`) | Assertion failure, 28 tests | + +## Low-review follow-up + +A low review of `26d34291d` found that the ownership driver removed its +throwing subscriber in a synchronous `finally`, before a commit's settlement +resumed in a later microtask. A subscriber therefore never threw during an +asynchronous success or failure settlement. Measuring the reach showed a wider +gap: steps chose a transaction index from 0 to 3 at random, so most pointed at +a missing transaction or one in the wrong state, and in one run the `settle` +step ran 0 times in 400 histories. + +The driver now keeps the subscriber installed until promises flush, and it +asserts that the subscriber ran whenever the model predicts that the step +changes that Collection's rows. A step's `tx` number now chooses among the +transactions whose state allows the step, and settle and commit steps are +weighted higher. A pinned history throws during an asynchronous success and an +asynchronous failure settlement. + +| Check, on `cb9f8a47b` production code | Result | +| --- | --- | +| New driver | All 4 cases pass; no production defect | +| Old subscriber ordering with the new invocation assertion | The random campaign fails: `the throwing subscriber ran: expected 0 to be greater than 0`, at an asynchronous failed settlement | +| Mutant: success settlement skips `isPersisted` when a subscriber throws | New driver: assertion failure in the random campaign and the asynchronous pinned history. Old driver: survives | +| Mutant: `touchCollection()` stops at the first throwing Collection | New driver: assertion failure in the asynchronous pinned history. Old driver: its random campaign can also reach it through the synchronous rollback path, so this mutant does not distinguish the drivers | + +The fixed seed alone does not catch the first mutant; the pinned history is the +reliable witness for asynchronous settlement with a throwing subscriber. + +## Medium review round 2 (reviewed head `7fd5de08b`) + +Probes on `7fd5de08b` and on `main` `fe284ccbd`, from a scratch probe file: + +| Finding | `7fd5de08b` | `main` | Disposition | +| --- | --- | --- | --- | +| A second live transaction with the same id evicts the first | First evicted; its row shows `0`, not `5` | Same | Fixed: `trackTransaction` throws `DuplicateTransactionIdError` (code 233) before applying. Ids are documented as unique (`TransactionConfig.id`) | +| `TransactionScope` removes by id | Settling `t2` removed live `t1`; a later conflicting rollback left `t1` pending | Same | Fixed: removal by identity | +| `commit()` loses the mutation error when a rollback subscriber throws | Rejects with the subscriber error | Same | Fixed: `commit()` rejects with the mutation error | +| Nested `AggregateError` from conflicting rollbacks | Pinned history: `flat aggregate: expected true to be false` | Only the first error escapes | Removed with the aggregate; see the simplification below | +| A settled transaction keeps its Collections | `collections.size` is 1 after settlement | No such field | Fixed: settlement clears the set | +| Offline restoration bypasses ownership and removes by id | By source | Same | Fixed: restoration uses `trackTransaction`, and its cleanup settles through `touchCollection()` | +| Release depends on a recompute | Could not make the capture step throw during settlement; by source, the next recompute or sync-commit end sweeps a skipped release | Main never releases | Refuted as a permanent skip; a transient residue until the next recompute is accepted | + +The ownership oracle gained: + +- an error-shape law, checked for each settling call and each commit rejection + against the subscriber errors the step raised, with throwing subscribers on + one or both Collections; +- an `open` that reuses an earlier id, with the duplicate-id rule in the model; +- a `mutate()` callback that writes and then throws; +- a retention observation: a settled transaction's set of tracking + Collections is empty (its mutations still name their Collection); +- identity comparison of tracked transactions; +- four pinned histories (scope identity, duplicate id, flat aggregate, + commit error). + +A focused witness covers a Collection cleaned up before its transaction +settles. + +All before the fix, on the oracle above: 8 of 8 cases failed on `7fd5de08b`. +After the fix: 8 of 8 pass. + +| Mutant | Result | +| --- | --- | +| Swallow settlement errors | Assertion failure, 5 tests | +| Always wrap errors | Assertion failure, 4 tests | +| Throw only the first error | Assertion failure, 2 tests | +| Scope removes by id | Assertion failure, 1 test (the pinned scope-identity history) | +| Overwrite a live same-id transaction | Assertion failure, 3 tests | +| Keep `collections` after settlement | Assertion failure, 7 tests | +| `commit()` rethrows settlement errors instead of keeping the mutation error | Assertion failure, 2 tests | + +The optimistic-history oracle's tracked-transaction check counted only +persisting transactions as unsettled. `main`'s pending manual edits (#2078) +showed that the model was wrong, not production: a pending transaction is +tracked. The check now counts pending and persisting transactions. + +The collections CI group then failed to build PowerSync. A private +settlement helper made a handler's `TransactionWithMutations` unassignable +to `Transaction`, because that type omits a key from `Transaction`, and +`Omit` drops private members. The db typecheck missed it because nothing in db +made that assignment. The helper was then `@internal`; the simplification below removed it. +`mutation-handler-compatibility.test-d.ts` asserts the assignability. The test +fails with the private method and passes without it. + +## Simplification: one error, every step + +Review decision: precise reports for these rare settlement failures do not +earn their code. Correct handling does. The `AggregateError`, its code 234 +and the internal error lists are removed: + +- `touchCollection()` recomputes every tracking Collection, then rethrows the + first subscriber error. +- `rollbackConflictingTransactions()` rolls back every conflicting + transaction, then rethrows the first error. +- `rollback()` rejects `isPersisted` and recomputes its Collections in a + `finally`, so a throwing conflicting rollback cannot skip them. +- `commit()` rejects with the mutation error when the rollback also throws. + +The ownership oracle's error-shape law now requires one of the step's +subscriber errors, as is, and the mutation error for a failed commit. It does +not choose which subscriber error, because the contract does not. + +| Mutant | Result | +| --- | --- | +| `touchCollection()` stops at the first throw | 4 tests fail | +| Conflicting rollbacks stop at the first throw | 1 test fails | +| A throwing conflicting rollback skips the primary's settlement | 2 tests fail | +| `commit()` rejects with the settlement error | 3 tests fail | + +## Targeted review (head `9e7ebb0a8`) + +Probes on `9e7ebb0a8` and on `main` `2c98b4992`: + +| Finding | `9e7ebb0a8` | `main` | Disposition | +| --- | --- | --- | --- | +| A direct write (`commitDirect`) whose subscriber throws leaves a pending transaction tracked | Handler never runs; the row stays pending | Same | Open: the settlement law for a direct write that throws is undecided (rollback or commit) | +| `OfflineTransactionAPI.mutate()` creates a new transaction with the same id on each call | Second call throws `DuplicateTransactionIdError` | The second transaction replaced the first and hid its rows | Fixed: repeated calls add to one transaction | +| A completed restoration never settles `isPersisted` | Pending | Same | Fixed | +| A restoration that one Collection cannot track stays tracked by the others | The first Collection keeps the restored row | Not reachable: no duplicate-id error | Fixed: a secondary rollback, which leaves live transactions on the same keys alone | +| `rollback({ error })` on a settled transaction replaces its error | Replaced | Kept | Fixed: the error is recorded after the state checks | +| "A settled transaction holds no reference to its Collections" | Its mutations still name their Collection | — | Claim corrected: its set of tracking Collections is empty | +| The optimistic-history oracle compares tracked transactions by id | Equivalent within its grammar, where ids are unique | — | Now compares identity | +| `commit()` and `isPersisted` report settlement errors in different shapes | By source | — | Open: needs a decision | +| A recompute that throws delays release until the next recompute | Same as round 2 | — | Accepted residue, as in round 2 | + +The focused witnesses in +`packages/offline-transactions/tests/restoration-ownership.test.ts` and +`packages/db/tests/transactions.test.ts` failed on `9e7ebb0a8` and pass now. + +| Mutant | Result | +| --- | --- | +| `mutate()` replaces the offline transaction | Assertion failure, 1 test | +| A completed restoration skips `isPersisted` | Assertion failure, 1 test | +| No rollback after a restoration fails to track | Assertion failure, 1 test | +| A plain rollback, which cascades to the live same-key transaction | Assertion failure, 1 test | +| `rollback()` records the error before checking the state | Assertion failure, 1 test | + +## Unresolved + +- The generated ownership grammar reaches a conflicting rollback that throws, + and an asynchronous settlement with a throwing subscriber, only sometimes; + the pinned histories are their reliable witnesses. +- Two Collection instances with one id, and a Collection cleaned up before + its transaction settles, are covered only by focused witnesses. +- Offline restoration's tracking path is covered by offline-transactions' + witnesses, not by the ownership oracle or any generated history. +- A direct write whose subscriber throws leaves its transaction pending, on + `main` and here. No oracle grammar authors a direct write. +- The ownership oracle does not combine queued sync transactions that hold a + completed row with a second Collection. The optimistic-history oracle owns + held rows for one Collection. +- One random seed of the partial-update history in + `collection-state-retention-oracle.property.test.ts` + (`TANSTACK_DB_ORACLE_SEED=-526998278`) fails identically on `main` + (`fe284ccbd`). A partial sync update publishes a value that the model does + not allow. It is unrelated to transaction retention and is not addressed here. +- The per-mutation pass is still O(unsettled transactions). A key-scoped + recompute would remove that term, but it touches every settlement law. + +## Follow-up review (head `16009c138`) + +The review had nine findings from reading the code. Each behavioral claim was +probed on this head, and on `main`'s core where the claim compared them. + +| Finding | Verdict | Change | +| --- | --- | --- | +| 1. With `autoCommit`, a second offline `mutate()` throws | Confirmed behavior change | Accepted: the first call commits, and `mutate()` on a committed transaction throws, as for any Transaction. On `main` the second call replaced the first transaction and hid its rows. The changeset says so. Witness: the second call throws `TransactionNotPendingMutateError` and the first row stays. | +| 2. Corrupted outbox mutations without a Collection throw | Refuted | `TransactionSerializer.deserializeMutation` throws when a Collection is missing, so no restored mutation lacks one. The remaining dead guard in restoration is removed. | +| 3. `commit()` drops rollback errors | Accepted | Precise errors for rare settlement failures are not required (maintainer decision, 2026-10-09). The mutation error is the reported cause. | +| 4. `rollback()`'s `finally` can mask the first error | Accepted | Same decision. The oracle law requires one subscriber error, not a particular one. | +| 5. Release inside `overlayActiveTransactions` drops held rows when a subscriber commits sync into B | Refuted on the stated path | Probe: a transaction spans A and B, B's confirmation is queued, and A's subscriber commits a sync row into B during settlement. B keeps row 1 as `local` with no delete event, on this branch and on `main`'s core. | +| 6. A Collection tracks before `applyMutations`, which might throw | Refuted | `applyMutations` uses only Map and array operations; nothing in it can throw. Tracking first lets the duplicate-id check change nothing. | +| 7. Restoration warns on a live transaction with the same id | Confirmed, low reach | Restoration loads only transactions the scheduler does not hold, so it needs a cleared scheduler with the transaction still live. A live transaction with this id already shows its rows, so restoration now skips it without a warning. Witness: the existing untrackable-restoration test asserts no warning; a mutant that warns again fails it. | +| 8. A test name includes the PR number | Confirmed | Renamed `generated histories with a fixed seed`. | +| 9. A test name promises one flat aggregate | Confirmed | Renamed to state the current law: every transaction settles, and one subscriber error is rethrown. | + +**Noted, outside this change.** `OfflineTransaction.mutate()` with +`autoCommit` rethrows a failed auto-commit inside a `.catch`, so a failed +auto-commit is an unhandled rejection. This is on `main` as well. + diff --git a/docs/contributing/oracle-reviews/gap-02-ivm-type-markers-and-empty-window.md b/docs/contributing/oracle-reviews/gap-02-ivm-type-markers-and-empty-window.md new file mode 100644 index 0000000000..5ff49876d1 --- /dev/null +++ b/docs/contributing/oracle-reviews/gap-02-ivm-type-markers-and-empty-window.md @@ -0,0 +1,97 @@ +# GAP-02: db-ivm type markers and empty-window moves + +Reviewed executable revision: `a921e760` (parent `33a19494`, the fetched +`origin/main` at review time). This record and its changeset follow in a +documentation-only commit. The reviewed revision changes tests and the coverage +map. It does not change production code. + +## Contract, path, and limits + +The structural hash owns sampled distinctions between container kinds. For +each generated integer sequence, the test compares a Set or Map with a plain +object that has the same indexed entries. The independent expectation is that +the two container kinds produce different fingerprints in this sampled domain. +The test invokes `hash` through the native hash session and compares each pair +after hashing. A 32-bit hash can collide, so this is not an injectivity claim. + +The fractional-index top-K operator reports a changed window through an empty +D2 output message when no relation rows move. The fixed history inserts one +row, retracts it, moves the requested window, and runs the graph. The test +checks the exact output-message suffix at that checkpoint. It does not run a +lazy source or establish that downstream acquisition starts. + +## Grammar controls and calibration + +The hash grammar generates one to five unique integers, including negative, +zero, and positive values. The same values construct both compared carriers. +The one-value `[[0]]` counterexample reconstructs the reported type-marker +fault for Set and Map. Unique values are necessary: duplicate Set values or +Map keys would make the indexed-object comparison cease to represent matching +entries. The carrier axis distinguishes Set from Map. Removing either carrier +would leave that type marker unchecked. Length one is the marginal case; the +generator also covers longer sequences. Empty carriers, duplicate entries, and +arbitrary object keys are outside this law. The fixed top-K history has no +generated grammar. + +On unchanged production, the focused tests passed. A temporary hash mutant +replaced `hasher.update(marker)` with `hasher.update(0)` in `hashPlainObject`. +Both Set/object and Map/object properties failed in fixed and random campaigns. +The outcome class was **assertion failure**, not setup failure or timeout. The +fixed campaign reported seed `1659001`, path `0:0:0`, and minimized values +`[0]` for both carriers. Direct replay with +`TANSTACK_DB_IVM_HASH_TYPE_SEED=1659001` and +`TANSTACK_DB_IVM_HASH_TYPE_PATH=0:0:0` failed at the same comparison. Hash +initialization changes numeric fingerprints between processes, but the +counterexample and equality failure remain the same. + +A temporary top-K mutant suppressed output when `TopKState.isEmpty` was true. +The fixed test reached its final assertion and received `[]` messages instead +of the required `[[]]`. Its outcome class was **assertion failure**. Both +production files were restored before the full green run. + +## ORC-001 through ORC-012 + +| Requirement | Result | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| ORC-001 | The hash owner's opening contract states fingerprint scope and collision limits. The top-K test names the empty-window report and its lazy-source motivation. The coverage map records both limits. | +| ORC-002 | The hash expectation comes from equivalent indexed entries with different carrier kinds. The top-K expectation comes from the window-change report, without importing its change classifier. | +| ORC-003 | The hash file shows the contract, generated values, relation, native driver, and comparison. The top-K addition is a focused regression beside the existing top-K relation owner. | +| ORC-004 | The hash grammar reconstruction, carrier ablation, value range, and duplicate-entry exclusion appear above. The top-K regression is fixed, so the generated-grammar trigger does not apply. | +| ORC-005 | `hash` and `topKWithFractionalIndex` execute on production paths. The tests compare the hashes and exact D2 message suffix at their stated checkpoints. | +| ORC-006 | Both named wrong designs failed by assertion at the intended checkpoints, as recorded above. | +| ORC-007 | Each hash carrier runs the same property for 100 cases with fixed seed `1659001` and a random seed. The seed and shrink path select a direct replay. The property does not use `fc.commands`. The top-K regression is not generated. | +| ORC-008 | No stateful reference model changes. | +| ORC-009 | No model-only terms combine or split glossary concepts. | +| ORC-010 | The existing `HashReplayError` retains the law, input, observations, initialization tape, and original cause. The native fast-check report retains seed and path. Neither test acquires an external resource. | +| ORC-011 | No named shared semantic fault calls for a second formulation. The two carrier comparisons and hostile mutants calibrate the stated laws. | +| ORC-012 | This record ties the reviewed executable revision, the outcome classes, grammar controls, and limits to the coverage map. | + +## Verification + +From `packages/db-ivm`, the package Vitest binary ran all 39 test files: +564 tests passed with no type errors. The package TypeScript check passed. +`git diff --check` passed. The package uses the normal full test campaign; +the four new hash campaigns run as part of that command. The test environment +used Node `v24.19.0`, Vitest `3.2.4`, and fast-check `3.23.2` on Darwin arm64. + +## Replay-harness follow-up + +Reviewed executable revision: `8653d2a9`. This follow-up changes only the hash +oracle's replay configuration and avoids building Map entries during Set runs. +The contract, generator, native hash driver, and comparison above stay the same. + +Before the follow-up, `TANSTACK_DB_IVM_HASH_TYPE_SEED=garbage` ran a selected +carrier test successfully. `Number('garbage')` produced `NaN`, which fast-check +coerced to seed `0`. A large integer such as `4294967297` also changed seed +through fast-check's 32-bit conversion. A path without a seed failed during +module collection, so Vitest ran no tests from the file. After the follow-up, +these invalid settings fail only the Set and Map replay tests with a direct +configuration error. A replay seed must be a 32-bit integer. The other 40 hash +tests still run and pass. A valid seed and path pass both replay tests, and the +ordinary file run passes all 44 tests. This strengthens the direct-replay +interface required by ORC-007 without changing the oracle law. + +The full db-ivm run at `8653d2a9` passed all 564 tests in 39 files with no type +errors. The package TypeScript check and `git diff --check` passed. The earlier +wrong-result controls remain the ORC-006 calibration for the unchanged hash +comparison and empty-window assertion. diff --git a/docs/contributing/oracle-reviews/gap-03-draft-order.md b/docs/contributing/oracle-reviews/gap-03-draft-order.md new file mode 100644 index 0000000000..3310f1efed --- /dev/null +++ b/docs/contributing/oracle-reviews/gap-03-draft-order.md @@ -0,0 +1,65 @@ +# Draft native-order oracle review + +Reviewed code head: `cac3b2b95cf8683177040eef2c3b0198c9a8deff`. +Source: `GAP-03` in the code-weight audit's `BUGS_AND_ORACLE_GAPS.md`. +This record reviews the test and coverage-map change at that head. + +## Result + +A draft reorder must retain the native iteration order of a Set or Map. +The test crosses two container kinds with replacement and clear-and-readd. +It checks the change patch after the callback and the public Collection row +once `update()` persists. + +The original detachment owner passed 70 tests. A broad unordered Set mutant +failed one existing cyclic-back-reference test. Thus, the audit's claim that +this mutant survived every owner test is stale at the reviewed base. +A narrower unordered primitive Set mutant passed all 70 old tests. The new +Set replacement case rejected it because the `value` patch was missing. +An unordered Map mutant failed at the same checkpoint in the new Map case. +The changed owner passed 74 tests. Two existing RegExp cases rejected a +`lastIndex`-blind mutant: they observed position 1 instead of 2. + +The review found no production bug on the current head. This PR adds a +bounded order witness. It does not claim all native values or histories. + +## Guide check + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | The `proxy.ts` comment states the Set-order and RegExp-position rules. The detachment and iteration owners promise native Map snapshots. The Map check concerns public iteration order after a reorder. It does not define arbitrary Map key equality. | +| ORC-002 | A fresh native Set or Map computes the expected order. The test does not import the draft comparator. | +| ORC-003 | The oracle file states the law, native model, four-case grammar, production driver, observations, and checkpoints beside the test. | +| ORC-004 | Not triggered: the four cases are bounded enumeration, not a generated-history coverage claim. | +| ORC-005 | The test calls `withChangeTracking` and `Collection.update()`. It compares the patch and stored row at the stated checkpoints. | +| ORC-006 | Both unordered mutants failed at an intended patch assertion. The broad Set and RegExp mutants failed existing assertions. No mutant timed out or failed in setup. | +| ORC-007 | Not triggered: this is bounded enumeration, not an important generated property. | +| ORC-008 | Not triggered: the change adds no stateful reference model. | +| ORC-009 | Not triggered: the native reference adds no model-only subsystem term. | +| ORC-010 | No shrinking or capture occurs. The driver cleans its in-memory Collection in `finally`. This fixture does not inject cleanup failure. | +| ORC-011 | Not triggered: no plausible shared comparator fault was named for native iteration and the draft comparator. Replacement and clear-and-readd exercise adjacent paths. | +| ORC-012 | This record gives the outcomes and limits for the reviewed code head. No universal bug-class closure is claimed. | + +## Audit disposition + +- Set order: **fixed-now** as a test gap. The narrow unordered mutant was an + old-suite survivor and is now rejected. +- Map order: **fixed-now** as a test gap. The new test checks native iteration + after replacement and clear-and-readd. +- RegExp `lastIndex`: **already-fixed** in the existing owner. No duplicate + test was added. +- Contract source: **already-fixed** for Set and RegExp in the `proxy.ts` + comment. The Map claim stays limited to native iteration in the owner. + +All four source items have a disposition. None has an evidence gap. +The coverage map still excludes general native-mutator and symbol-write +support. This test does not cover nested nonprimitive values or every Map key +class. No in-scope counterexample is known for the four tested histories. + +## Reviewer assessment + +The audit found a useful primitive Set-order gap and a useful Map case. +Its broad Set survivor count and RegExp gap were stale on this base. +The proposed test path was appropriate, but the report needed mutant runs +against the current head before it classified each claim. For this sample, +**hire: yes for issue discovery with a required reproduction gate**. diff --git a/docs/contributing/oracle-reviews/gap-04-paced-mutations.md b/docs/contributing/oracle-reviews/gap-04-paced-mutations.md new file mode 100644 index 0000000000..3d4bd1a04a --- /dev/null +++ b/docs/contributing/oracle-reviews/gap-04-paced-mutations.md @@ -0,0 +1,146 @@ +# GAP-04 paced-mutation oracle review + +## Reviewed state and authority + +- Starting head: `33a194941c8d51f8f98babb999fef2987dd6ff8b`. +- Initial implementation commit: `1b844588bc18e96a4c18c0de6bb1edadcad85332`. +- Reviewed implementation commit: `ccdeaa5ba61f94e7cf81930987d728769266a1cd`. +- Maintainer-decision implementation commit: `baf6cf27`. +- Owner: `packages/db/tests/paced-mutations-oracle.test.ts`. +- Public authority: strategy option types, `createPacedMutations` reference, and `docs/guides/mutations.md`. + +The owner uses finite virtual-clock histories over public `createPacedMutations` and the three real strategy factories. Its independent models use an appointment time, pending mutation IDs, and a plain ordered list. The tests compare immediate public Collection rows, mutation callback times and payloads, returned transaction identity, final transaction state, and `isPersisted.promise` fulfillment with the returned transaction object. A held-write history crosses the queue timer and two pending persistence promises to check serialization. + +The maintainer chose the documented pacing behavior: factories preserve caller-owned options, `maxSize` overflow fails the returned transaction and rolls back its optimistic state, and explicit non-leading throttle waits for its first trailing edge. The baseline `maxSize:1` witness returned two completed and two permanently pending transactions after four calls. The baseline explicit non-leading throttle witness started at t=0 rather than the trailing edge. The follow-up oracle encoded both laws and failed on the unchanged production implementation. Omitted leading/trailing defaults remain outside the finite domain. + +## Verification on the reviewed commit + +- The expanded oracle passed 18 tests. The existing paced-mutation suite passed 13 tests, and the React hook suite passed 6 tests under jsdom. +- The DB package typecheck, changed-file ESLint, Prettier, and `git diff --check` passed. +- Hostile controls for queue extraction, debounce trailing, throttle leading and non-leading timing, ignored capacity, false-green overflow admission, and caller option mutation each failed their intended checker. +- A hostile cleanup control threw after an assertion failure. The result retained the assertion as `cause` and the cleanup error in `AggregateError.errors`. Collection cleanup still ran. +- The permanent capacity, non-leading throttle, and frozen-options tests failed on the unchanged baseline and pass with the repair. Capacity receipts are observed at named virtual-clock cuts so an unsettled promise produces an assertion failure rather than a test timeout. + +## ORC-001 through ORC-011 + +| Requirement | Outcome | +| --- | --- | +| ORC-001 authority and limits | Pass for documented queue order and bounded admission, explicit debounce trailing and throttle edges, optimistic rows, and persistence settlement. Omitted defaults, failed writes, and broader generated schedules remain outside this finite owner. | +| ORC-002 independent judgment | Pass. The expected trace is computed from a virtual appointment list, a quiet-period grouping rule, and a leading/trailing window rule. No pacer-lite code or production scheduler enters the model. | +| ORC-003 five responsibilities | Pass. The opening states the law and limits; `queueStarts`, `debounceStarts`, and `throttleStarts` are the model; action arrays are the finite grammar; `runProduction` is the real driver; exact trace and receipt assertions are the refinement check. | +| ORC-004 grammar controls | Bounded enumeration, not an important generated property. It reconstructs all four queue position combinations, zero queue wait, queue capacity zero/one with waiting and in-flight writes, debounce reset, both throttle edge forms, and held queue settlement. Distinct IDs isolate queue admission; a focused same-key witness checks that overflow rollback leaves an admitted update intact through first-write settlement. A deferred custom queue checks the void-return compatibility boundary. Negative waits and broader capacities are outside the claimed grammar. | +| ORC-005 path and observation | Pass. Real strategy instances drive the public manager and Collection; callback times and payloads, public rows, returned transaction identity/state, and persistence receipts are observed at virtual-clock cuts. Rejected receipts carry `QueueCapacityExceededError`; admitted same-key state survives the preceding write's settlement. | +| ORC-006 checker calibration | Pass for hostile queue extraction, disabled debounce trailing, disabled throttle leading, early non-leading throttle, ignored capacity, false-green overflow admission, and caller-option mutation controls. Each reaches and fails its corresponding checker. | +| ORC-007 fixed/random campaigns | Not triggered: this owner is bounded finite enumeration, not an important generated property. | +| ORC-008 model minimality | Pass. The queue's ready-list order distinguishes later extraction; due time distinguishes a call before/after eligibility. Debounce pending IDs and quiet-period due distinguish which transaction persists and when. Throttle pending IDs and next edge distinguish merged trailing output from a fresh leading call. | +| ORC-009 vocabulary mapping | Pass. The opening maps model pending IDs, ready list, and due appointment to production concepts without importing their implementation. It uses the glossary terms optimistic transaction and settlement. | +| ORC-010 failure fidelity | Pass. The driver compares the complete trace before cleanup. `withCleanup` stops strategy timers and cleans the Collection. When comparison and cleanup both fail, it retains the comparison as the cause and reports cleanup errors separately. A hostile cleanup test checks both errors and Collection cleanup. No shrinking or external capture occurs. | +| ORC-011 second formulation | Not triggered for the documented finite schedule laws: the model's appointment/list formulation differs from pacer-lite's timers and promise chain, and no named shared semantic fault has a meaningful second formulation. Broader capacities and schedules remain open. | + +ORC-012 is this versioned record, tied to the reviewed implementation commit and the coverage-map owner. The sampled finite histories do not prove every legal schedule. + +## Review and loss audit + +The original audit accurately found absent queue option coverage and no paced-mutation oracle. It correctly rejected a pacer-lite differential as permanent authority. It missed the pending transaction on queue rejection and the early non-leading throttle start. Hire recommendation: hire for issue discovery with a reproduction and contract-authority gate; good signal and prioritization, incomplete edge/settlement analysis. + +The source ledger has six claims. GAP04-1, GAP04-3, and GAP04-4 are fixed for the finite domain described above, including bounded capacity and hostile capacity controls. GAP04-2 has a maintainer decision and a tested implementation. GAP04-5 is accepted and implemented: the copied pacer-lite differential is not the oracle. GAP04-6 is deferred to the proposed MUT-05 replacement. The source claims stay distinct in the task-local ledger. + +The follow-up review found two oracle gaps: the same-key witness did not observe the admitted update after the first write settled, and direct receipt awaits could hang before cleanup. Both were fixed by holding writes across a settlement cut and recording receipt outcomes before asserting them. A simplifier removed a duplicate queue transaction reference; a separate non-leading timer remains necessary because pacer-lite 0.2.1 measures its first wait from epoch zero. + +The repair adds 37 net production lines across the queue admission result, a named rejection reason, targeted rollback, caller-option cloning, and the trailing-only timer. The tests and contract documents grow separately. Each added production branch supports a public law demonstrated by a RED witness and a GREEN oracle run. + +## CodeRabbit review 5345009177 + +CodeRabbit reviewed `abf7740b921de88589c5b4a1ef53d6ea832a9145` and posted one inline finding. Its claim was accurate: `if (!admitted)` rejected a custom queue strategy that admitted work and returned `void`. The previous `QueueStrategy.execute` contract allowed that return. The new Boolean-only type also broke source compatibility for that strategy. + +The permanent deferred-callback witness failed on the reviewed commit at the admission checkpoint: the returned transaction was `failed` instead of `pending`. DB typecheck rejected the void-returning strategy. The repair treats only explicit `false` as rejection and restores the prior void and promise return types. The same witness then passed, and the bounded queue witnesses still rejected overflow. The focused DB suite passed 18 oracle and 13 existing tests. DB typecheck, ESLint, Prettier, and diff checks passed. + +The review had one actionable finding and one non-blocking suggestion to run a local CodeRabbit CLI review. The finding is `fixed-now` in the task ledger. The CLI suggestion is `deferred` to the normal CodeRabbit PR re-review after push. No other technical item appeared in the raw review body, inline comments, or footnotes. Loss audit: 2 raw items = 1 fixed-now + 1 deferred. No evidence gap remains. + +Reviewer assessment: 1 of 1 actionable findings was correct and specific. The suggested conditional fixes the runtime bug. The reviewer did not mention the related type compatibility break, which the RED typecheck exposed. The review had high signal and a narrow but useful analysis. Hire recommendation: hire for targeted PR review, with an oracle and typecheck gate before accepting fixes. + +## PR #1930 batch review follow-up + +The review in `pr-1918-1934-review-summary.md` examined HEAD `0654b2aa574b760e8d6935cccafff9ce9d8d208b` and contained five numbered findings plus six contextual claims. The task-local append-only ledger preserves all eleven in source order. + +Finding 1 exposed a real omitted-leading throttle gap. Pacer-lite 0.2.1 defaults both edges only when both are absent. With `{ wait: 10, trailing: true }`, `leading` remains undefined; its first trailing timeout subtracts the epoch-based elapsed time and fires at virtual time 0. The new public `createPacedMutations` oracle case failed before the fix: actual starts included `{ at: 0, ids: [1] }` while the independent non-leading model expected `{ at: 10, ids: [1, 2] }`. The same case passed after changing the guard to `leading !== true && trailing === true`. Explicit non-leading, explicit leading, and both-omitted default cases pass alongside it. The pre-fix implementation is the hostile timing control at the exact start-time checkpoint. The repair is one changed production line. + +Finding 2 correctly flags a new public error and observable overflow change. It is not a TypeScript source incompatibility: the export is additive and old source still compiles. The old silent drop left an optimistic row and pending persistence receipt, so relying on it as a completed no-op was not a coherent public contract. The prior maintainer decision, patch changeset, guide, and bounded RED/GREEN capacity oracle cover the intended behavioral change. + +Finding 3 is accurate. Pacer-lite checks `items.length >= maxSize` before insertion and processing, so zero rejects even the first mutation. The existing public oracle verifies three rejected transactions for `maxSize: 0`; the focused suite passed it. The guide, option type, and reference text now state the zero boundary and rejection semantics. + +Finding 4 accurately identifies a separate timer path. It does not show a second behavioral fault or a safe local root fix: pacer-lite owns a private epoch-based clock, and this branch has no public way to reset it. Keep the narrow trailing-only timer until an upstream pacer-lite repair can provide the same documented schedule; the oracle's explicit and omitted-leading cases are the migration checks. This maintenance idea is deferred here, not dropped. + +Finding 5 is partly imprecise: the comment said “captured transaction” rather than the literal removed identifier `capturedTx`, and the concept was still correct. Naming `txToReturn` directly makes the comment match the code; that one-line comment edit is complete. + +The scoreboard, opening attention note, and cross-PR observation correctly call this a production behavior change with a new public API. The three named changes are bounded queue rejection and rollback, explicit trailing throttle timing, and caller-options immutability. The omitted-leading correction closes one adjacent configuration. These claims are recorded in the changeset, guide, oracle, and this review record. The PR remains a narrow bug-fix release despite exceeding the batch's test-only shape. + +Reviewer assessment: the key omitted-leading finding was precise, reproducible, and paired with the correct one-line classifier fix. The capacity and stale-reference notes identified useful documentation cleanup. The compatibility note overstates source breakage, and the timer note is a design preference without a concrete alternative. Signal-to-noise is good; prioritization is sound. Hire recommendation: hire for focused review, with a public oracle and compatibility terminology check. + +Verification after the fix: DB paced oracle 20/20, existing paced tests 13/13, React paced hook 6/6, DB typecheck, changed-file ESLint, Prettier, and diff whitespace checks. Loss audit of the raw review: 11 claims = 3 fixed-now (findings 1, 3, 5) + 6 already documented contextual claims + 1 refuted as phrased (source incompatibility) + 1 deferred maintenance idea (upstream timer repair). No item lacks evidence. Other edge combinations, failed persistence, broader capacities, and generated schedules remain outside this finite oracle owner. + +## CodeRabbit review 5345661866: queue cleanup + +CodeRabbit reviewed `bad0a0091e6914ba60d67e9875f640a05e9e7238` and posted one inline finding at `docs/guides/mutations.md:1231-1232`. Its claim was accurate: `queueStrategy.cleanup()` stopped the timer and cleared admitted waiting callbacks. Their optimistic transactions stayed pending, their persistence receipts did not settle, and their optimistic rows stayed visible. This behavior predates the PR. The guide promised that every admitted queue mutation is attempted before and after the PR. + +A controlled public probe held the first write and called cleanup while the next item waited. After 20ms, the second write had not started, its transaction and receipt stayed pending, and its optimistic row remained visible. The permanent FIFO oracle case failed on the reviewed commit at its start-order checkpoint: actual `[1]`, expected `[1, 2]`. The owner previously ran cleanup only after every admitted call settled, which explains the false-green coverage. + +The repair stops new admission and lets the existing LiteQueuer timer drain admitted callbacks at the configured wait interval. Cleanup still returns synchronously. The held first write gates later starts. FIFO and LIFO oracle histories check exact start order, transaction and receipt settlement, a repeated cleanup call, and direct strategy rejection of a new callback after cleanup. An additional public clock history checks two immediate writes: starts remain `[0]` at the first cut and become `[0, 10]` after the wait. All three histories pass. The prior stop-and-clear implementation failed the drain checker. A `stop(); flush()` candidate passed settlement but failed the timing checker with starts `[0, 0]`. Both are hostile controls at their intended checkpoints. + +The reviewer suggested weakening the guide to say cleanup can discard admitted work. That would contradict the documented admission guarantee. The guide and changeset instead describe a graceful drain. The repair adds one disposal flag and keeps the existing timer until the queue empties. It adds no second timer or recovery path. A separate public probe called `mutate()` after cleanup. Its transaction failed and its receipt rejected with `QueueCapacityExceededError`. That error reason is inaccurate for disposal. The direct strategy test only asserts no new callback is admitted. A future owner must decide and witness the public post-cleanup call contract before changing its error path. The coverage map records this limit. + +Verification on the repaired worktree: DB paced oracle 23/23, existing paced tests 13/13, React paced hook 6/6, DB and React typechecks, changed-file ESLint, Prettier, and diff checks. The raw review had one finding and one optional local CodeRabbit CLI suggestion. Loss audit: 2 items = 1 fixed-now + 1 deferred to automatic PR re-review. No review item lacks evidence. + +Reviewer assessment: the single actionable finding was technically accurate and found a real lifecycle gap. The proposed documentation-only fix would preserve stranded optimistic transactions and weaken a public promise. The reviewer did not check the prior guide or the transaction receipt contract. Signal was high, analysis depth and fix quality were mixed. Hire recommendation: hire for defect discovery with contract review and public-path RED/GREEN gates. + +## PR #1930 rereview follow-up + +The rereview in `pr-1918-1934-rereview-status.md` checked `16d84046bc0152e2d7e6f50c1e57e6670dfa83ca`. Its five prior findings, three new claims, and overall verdict are retained separately in the task-local source-order ledger. + +The earlier epoch-zero throttle fix, named overflow error, documented `maxSize: 0` rejection, and comment correction all remain on the branch. The separate trailing-only timer still has an upstream pacer-lite migration owner. The reviewer correctly observed that cleanup now lets admitted writes run after a separately cleaned-up Collection. A public controlled-clock probe called both `strategy.cleanup()` and `Collection.cleanup()`, then observed the admitted second network callback start at t=10 and both receipts fulfill. This is the documented queue-drain behavior: the strategy has no Collection lifecycle signal, and the guide promised every admitted write would be attempted even before the PR. The old stop-and-clear behavior stranded an optimistic transaction and receipt. The guide now states the bounded Collection-cleanup interaction explicitly. This does not establish cancellation semantics for a Collection-owned adapter or guarantee that an external network client remains usable after its own teardown. + +Two other claims were product bugs. Before the fix, a new public oracle case for `throttleStrategy({ wait: 10, leading: false })` failed: expected starts at t=10 and t=20, actual starts `[]`, with optimistic transactions unpersisted. Pacer-lite 0.2.1 only defaults both edges to true when both options are omitted; with `leading: false` alone it schedules neither edge. Its documented `leading: false` behavior waits for a trailing execution, and the public paced-mutation contract requires settlement. The strategy now enables the trailing-only window for this omitted-trailing combination. The prior explicit-leading, explicit-trailing, omitted-leading, and both-omitted witnesses remain green. + +Before the other fix, a public call after queue cleanup rolled back and rejected its receipt with `QueueCapacityExceededError`. The new oracle expected a disposal reason and failed at that exact receipt assertion. The queue now throws `QueueDisposedError` at the admission boundary; `createPacedMutations` catches that specific error, rolls back only the new optimistic transaction, and returns its rejected receipt. A previously admitted write still completes, while no post-cleanup network callback runs. No recovery state was added. Direct strategy calls also report disposal instead of capacity. + +RED: the two new oracle cases failed separately on the intake head, with actual throttle starts `[]` and receipt error name `QueueCapacityExceededError`. GREEN on the repaired working tree: paced oracle 26/26, existing DB paced tests 13/13, React paced hook 6/6, DB typecheck, changed-file ESLint, Prettier, and diff check. The original implementations are the hostile controls for both new assertions. Both-edge-disabled throttle, failed persistence, and broader generated schedules remain outside the finite owner. The newly described Collection-cleanup interaction is a bounded observation, not a generic cancellation law. + +Reviewer assessment: two new actionable defects were accurate and supported by specific paths. The cleanup behavior observation was also accurate, but the recommendation to reopen its policy overlooked the existing admission guarantee and the prior RED cleanup witness. Technical accuracy and signal are high; fix quality is good for the throttle default and weaker for cleanup because it omitted the stranded-receipt trade-off. Hire recommendation: hire for focused review with contract and public-observation checks. + +Loss audit at the intake commit: nine raw items = four already addressed prior findings, one deferred timer-maintenance idea, one refuted cleanup blocker with its true observation retained, two fixed-now bugs, and one verdict duplicated by those findings. No raw item is omitted. Recheck the final diff and evidence after publication because this paragraph describes the working tree before the follow-up commit. + +### Broadened throttle and cleanup verification + +The first two RED cases exposed a larger legal option class. Pacer-lite 0.2.1 defaults both edges together only when both are omitted. `trailing: false` with omitted leading therefore started nothing; explicit leading started the first write but silently stranded a call inside its wait window; both edges disabled stranded every optimistic transaction. A separate epoch-zero probe found that even explicit leading with either trailing setting failed to start the first call at virtual time zero. Controlled public witnesses failed at exact start, transaction-state, optimistic-row, and receipt checkpoints. The supported `trailing: false` meaning is to drop calls inside a window, so the manager now rejects those calls immediately with `ThrottleCallDroppedError` and rolls back their optimistic state. A held first write plus a same-row dropped update proves that rollback preserves the admitted write. Omitting leading when trailing is false enables the leading edge; omitting trailing when leading is false enables the trailing edge. The oracle also retains both-omitted, explicit leading/trailing, and omitted-leading explicit-trailing schedules. + +The two-path LiteThrottler plus custom trailing timer could not expose a dropped-call result or reliably place the first edge at epoch zero. One local timer and one next-allowed timestamp now handle all four edge choices. This removes the dual-path maintenance concern from the earlier review. The implementation stores the most recent pending callback, so later calls in one trailing window merge into the transaction at that edge. An independent reviewer challenged option combinations, first/later boundaries, callback replacement, and same-row rollback and found no new counterexample. + +That reviewer identified a separate cleanup history: `leading:false,trailing:true`, `mutate(1)` at t=0, cleanup at t=4. The former timer cancellation left the optimistic transaction and receipt pending forever. The new public oracle failed at t=10 before the repair: no callback started. Cleanup now leaves the admitted trailing timer in place; the callback starts at t=10, and the transaction and receipt complete after a separately cleaned-up Collection. This matches the queue's documented choice to drain admitted work at the configured pace, while preserving the throttle's distinct merged-write behavior. Queue cleanup stops later admission and reports `QueueDisposedError`. No throttle admission-stop contract is documented; calls after throttle cleanup remain outside this finite owner and must not be inferred from the queue rule. Failed persistence and wider generated schedules are also open. + +The now-stale working-tree summary above is superseded in two dispositions: R30-4's dual-timer concern is fixed now, and R30-7 covers the adjacent legal edge combinations and epoch-zero first call. At this stage, the primary oracle passes 33/33. The final surrounding checks, production diff, commit SHA, and CI receipt will be recorded after the last review pass. + +Final pre-push verification on the expanded working tree: DB paced oracle 33/33, existing DB paced suite 13/13, React hook suite 6/6, DB and React package typechecks, changed-file ESLint, Prettier, and diff whitespace checks all pass. The last change corrects a timer comment after independent review. The cleanup oracle shows the throttle transaction pending at t=9, a single write start at t=10 after both strategy and Collection cleanup, and a fulfilled receipt. New throttle calls after cleanup remain undefined by the throttle contract and outside this oracle. The full PR's production source diff from merge base `33a19494` is +113/−29, net +84 lines. The new lines serve three named rejection reasons, targeted optimistic rollback, serial queue admission and drain, and one timer for every supported throttle edge combination. An independent simplification pass found no duplicate state or branch worth another rewrite. + +The final raw-source audit found nine items. The source lists prior findings in order 1, 5, 2, 3, 4 before the three new claims and its verdict. The task-local ledger retains stable finding-number IDs and records this exact order. Four prior items remain already addressed, three items received fixes in this follow-up, the cleanup blocker is refuted by the documented drain contract, and the overall verdict duplicates those claims. The reviewer accurately observed that admitted network writes can run after Collection cleanup. The guide now states that interaction. No raw claim lacks evidence. + +## Adjacent debounce cleanup follow-up + +The final cleanup audit found the same stranded-receipt pattern in the default debounce strategy. The guide promises that the final merged state persists after inactivity, and the public mutation function returns an optimistic transaction with a persistence promise. Before this follow-up, `debounceStrategy.cleanup()` canceled its pending LiteDebouncer timer. A controlled public probe called `mutate(1)` at t=0, cleaned the strategy at t=4, and observed no write at t=10. The transaction and receipt stayed pending while its optimistic row had been visible before cleanup. + +The permanent oracle now uses two calls at t=0 and t=4. It checks that they return the same transaction and expose both optimistic rows. Cleanup runs at t=8, followed by separate Collection cleanup. The transaction and receipt remain pending at t=13. The single merged write starts at t=14, the last call's quiet edge, with IDs `[1, 2]`; the transaction completes and the receipt fulfills with the returned identity. This test failed against the canceling implementation at the t=14 start checkpoint and passes when cleanup leaves the existing timer scheduled. It also rejects an immediate-flush cleanup because no write may start at t=8 or t=13. + +The debounce, throttle, and queue cleanup witnesses now share the bounded settlement law: cleanup does not strand work that already owns a scheduled write. Each retains its own timing rule. Queue cleanup also stops new admission because its contract says so. New debounce and throttle admission after cleanup has no defined cancellation rule, so this oracle does not claim one. The debounce production edit replaces one cancel call with an explanatory comment and adds no timer or recovery state. The guide, reference, changeset, and coverage map state the drain behavior and its limit. + +### Debounce option class after independent review + +Two independent reviews found that the cleanup repair still left legal debounce options with pending transactions. Pacer-lite 0.2.1 defaults both edges together only when both options are absent. Explicit `leading: false` with omitted `trailing` therefore scheduled no write. Explicit `leading: true` with omitted `trailing` ran the first call but stranded a second call inside the quiet window. With `trailing: false`, a skipped call also kept an optimistic row and pending receipt. Both edges disabled stranded every call. + +Five new public oracle cases failed before the option repair at their write-start, transaction-state, row, or receipt checkpoints. The factory now supplies independent defaults: omitted leading is false, and omitted trailing is true. For an intentionally skipped call when trailing is false, the strategy returns `false`. The manager rolls back that optimistic transaction and rejects its receipt with `DebounceCallDroppedError`. A held first write and same-row dropped update checks that this rollback preserves the first write. The default cleanup case and the explicit-leading, omitted-trailing case retain the pending timer through their quiet edge after separate Collection cleanup. The oracle passes 40/40 with these changes. + +The final review found two new compatibility edges. The manager initially treated every strategy's `false` result as a dropped call. A custom batch strategy may return `false` while retaining its callback under the existing base type. The public batch witness failed before the narrow fix: its transaction was `failed` at the admission cut, and the later callback could not commit. Only debounce and throttle now interpret `false` as an intentional drop. The same witness remains pending until its t=10 callback and then fulfills its receipt. + +The debounce strategy also checked the live caller-owned `options.trailing` after giving LiteDebouncer a copy. A caller could change that exposed option after construction, causing the admission result to disagree with the captured timer setting. A public witness starts a leading-only write, changes `options.trailing` to true, and calls again inside the window. Before the fix, the second transaction stayed pending. The strategy now checks the captured trailing value and rejects that second call. This preserves the construction-time option meaning and prevents a mutable options object from stranding a receipt. + +The final primary oracle passes 42/42. It covers the listed finite clock histories, edge options, public rows, callback starts, transaction state, and receipts at their named cuts. New debounce calls after cleanup, failed persistence, and broader generated schedules remain outside the owner. The primary oracle and coverage map name both the repaired class and its limits. + +Final implementation commit: `7733fc042580ac7b9f4585b077db722e32786697`. On that commit, the paced oracle passed 42/42, the existing DB paced suite passed 13/13, and the React hook suite passed 6/6. DB and React package typechecks, changed-file ESLint, Prettier, and diff whitespace checks passed. The full PR's production source diff from merge base `33a194941c8d51f8f98babb999fef2987dd6ff8b` was +140/−31 lines, net +109. Tests and contract documentation are excluded from that weight. The final independent review's batch and mutable-options counterexamples were RED before their narrow fixes and GREEN afterward. No reachable in-scope counterexample remains in the named finite histories; failed persistence, later strategy admission after cleanup, and broader schedules retain the coverage-map owner. diff --git a/docs/contributing/oracle-reviews/gap-06-ordered-effect-startup.md b/docs/contributing/oracle-reviews/gap-06-ordered-effect-startup.md new file mode 100644 index 0000000000..b33cd2be43 --- /dev/null +++ b/docs/contributing/oracle-reviews/gap-06-ordered-effect-startup.md @@ -0,0 +1,81 @@ +# Ordered Effect startup oracle review + +## Reviewed state + +- Base commit: `33a194941c8d51f8f98babb999fef2987dd6ff8b`. +- Reviewed change: the Git tree containing this record, the ordered-work oracle + extension, and the coverage-map update. The eventual commit and pull request + identify that immutable tree. +- Primary executable owner: + `packages/db/tests/query/ordered-work-oracle.property.test.ts`. + +A file cannot contain the hash of its own commit. This record fixes the +comparison base and the changed tree's contents. + +## Claim and limits + +The live-query architecture requires ordered Effect callbacks to retain the +last complete result while an authoritative repair advances the private D2 +graph. The first callback after startup must represent the completed ordered +window, even if another source's subscription synchronously changes the +ordered source before all subscriptions exist. + +The fixed history begins with two numeric ranks and an indexed width-one +request. The first request installs row 1. Subscribing to an empty sibling +source then updates row 1 from rank 1 to rank 3. The independent final order is +row 2 at rank 2, then row 1 at rank 3. The Effect must emit one `enter` for row +2. A transient `enter` for row 1 violates the callback-publication gate. + +This witness covers one synchronous update through an in-memory on-demand +source, a direct LEFT join, and one indexed ordered request. It does not prove +other source orders, multiple buffered changes, asynchronous changes during +startup, or remote provider ordering. Those nearby histories remain with the +ordered-work owner in the coverage map. + +## ORC-001 through ORC-011 + +| Requirement | Outcome | Evidence | +| --- | --- | --- | +| ORC-001: contract authority and limits | Pass | The architecture's ordered request and Effect callback gate sections authorize repair before callback publication. This record bounds the witness. | +| ORC-002: independent judgment | Pass | The two-row numeric rank order and expected single `enter` come from the source truth Map, not the ordered loader or D2's comparator. | +| ORC-003: distinguishable responsibilities | Pass | The oracle header states the law. The fixed-history comment states the model and schedule. The two Collections and `createEffect` drive production. Exact batches and the repair request are checked after startup and microtasks settle. | +| ORC-004: generated-history grammar controls | Not applicable | This adds a fixed controlled history to an existing oracle; it does not claim generated coverage for this startup schedule. | +| ORC-005: production path and observation | Pass | The sibling `subscribeChanges` hook checks the first ordered request, its one-row limit, and row 1's installed source value before the synchronous rank update. It records that the hook ran. `onBatch` captures every public delta batch. | +| ORC-006: checker calibration | Pass | Replacing the buffered drain's `handleSourceChanges` call with direct `sendChangesToD2` fails the exact batch assertion. With the final provider fixture, the mutant emits `enter` for row 1 instead of row 2. This is an assertion failure at the callback checkpoint. | +| ORC-007: fixed/random campaigns and replay | Not applicable | The new lane is a fixed witness. It does not change the existing generated ordered-work campaigns or replay interface. | +| ORC-008: stateful-model minimality | Not applicable | The witness adds no state to an existing reference model. | +| ORC-009: vocabulary mapping | Pass | Source Collection, ordered request, repair, private D2 graph, and Effect callback use the architecture and project glossary meanings. The source truth Map is fixture data, not a second production lifecycle. | +| ORC-010: failure fidelity and cleanup | Pass | `withHistoryCleanup` preserves a primary startup or batch assertion while disposing the Effect and cleaning both Collections. The recorded mutant reached the batch comparison. | +| ORC-011: independent second formulation | Not applicable | No plausible shared semantic fault in the two-row numeric order needs a second query formulation for this callback-gate claim. The public batch and provider repair request give separate observations of the same startup history. | + +ORC-012 is satisfied by this base-identified review record and its link from +the coverage map. The pull request records the final immutable commit. + +## Verification + +On the unmodified production code, the focused witness passes. Both ordered +owners pass 340 tests. The `@tanstack/db-ivm` and `@tanstack/db` builds, +`tsc --noEmit -p packages/db/tsconfig.json`, and changed-file ESLint pass. +The temporary mutant was restored before the green run. + +## External review follow-up + +The review of local and remote-tracking commit +`72aa5af26513e725dfdaae84e11d810cf42b8070` found three test/release +hygiene issues. The PR adds no published code, so the patch changeset was +removed under the repository's release guidance. + +The startup test used one `flushPromises()` turn before comparing batches. An +adversarial callback that queued a duplicate batch through two nested timers +passed that comparison. Draining the controlled timer queue before the exact +batch comparison made the same wrong callback fail. The synchronous provider +fixture schedules no later external work, so that queue drain is the test's +callback observation cut. + +The sibling-subscription hook also applied its rank update on every call. +One extra subscription after Effect startup failed at the row-rank setup +assertion, and one before startup failed at the ordered-request setup assertion. +The hook now applies the update only on its first invocation after the initial +ordered request. Both extra-subscription probes pass with that guard, while +the existing `changedDuringStart` and exact batch assertions still require the +intended history and public result. diff --git a/docs/contributing/oracle-reviews/group-representative-exact-value.md b/docs/contributing/oracle-reviews/group-representative-exact-value.md new file mode 100644 index 0000000000..8ca0b50386 --- /dev/null +++ b/docs/contributing/oracle-reviews/group-representative-exact-value.md @@ -0,0 +1,248 @@ +# Group representative work and exact-value choice review + +Evidence by revision, on `perf-aggregate-representatives`: + +- First campaign: tests `ef9d4dad4`, production change `9b021bbdc`. The RED + results ran on `ea51b67b0` with the new tests. +- Review follow-up: tests `871bdea11`, production fix `e5449edc2`. The RED + results ran on `9b021bbdc` and the GREEN and mutant results on `e5449edc2`. + `f67789cd3` merges `origin/main` without changes to these files. + +## Laws and authority + +1. **Work law.** The work that a grouped aggregate does for one inserted or + deleted member does not depend on the number of members that the group + holds. The law applies to a `groupBy` aggregate and to an aggregate inside + an include, which the compiler groups by its correlation route. Authority: + incremental view maintenance. A count changes by the multiplicity of the + delta, so the result does not need a re-read of each member. +2. **Route law.** One representative carries the whole correlation route. + The route has two fields, `correlationKey` and `parentContext`. Members of + one route share the correlation key instance and the parent context + instance, so the representative's identity is the exact identity of those + two instances. The first campaign used the parent context's equality + identity instead, which does not distinguish exactly different parent + values (review finding F4). +4. **Positive-contributor law.** A projected group value, and a `min` or `max` + result, is an exact value that a currently positive member holds + (`ARCHITECTURE.md`, "Value identity"). D2 consolidates contributions whose + hashes match, its hash treats `-0` as `0` and equal Dates as one value, and + a consolidated entry keeps the record of its latest change. Thus a + contribution must carry a key that separates every value it can supply. A + primitive's key is its exact value. An object's key holds its row key, + because a rebuilt argument is a new instance each time. +3. **Group-value law (revised by a product decision).** When several members + are equal under query equality but differ exactly, the projected value comes + from the member with the smallest exact value: + - another number before `-0`, and every primitive before an object; + - objects by an explicit type tag: `Buffer`, `Date`, a Temporal type, then + `Uint8Array`. The tags do not come from constructor names, so + minification cannot change the order (review finding F7). + + `-0` and `NaN` are never equal under query equality, so their order is not + observable. Members of one tag are equal in content, and any positive + instance can be projected. The previous law selected the member with the + smallest row key. + +## Old and new predictions + +The six cases in `group-by.test.ts` (`groups %s by query equality`, autoIndex +off and eager). Each case observes three checkpoints: both members present, +after the delete of row 1, and after the reinsert of row 1. Arrival order is +row 1, then row 2. + +| Members (row 1, row 2) | Old prediction | New prediction | +| --- | --- | --- | +| `Date(0)`, `0` | Date, `0`, Date | `0`, `0`, `0` | +| invalid Date, `NaN` | Date, `NaN`, Date | `NaN`, `NaN`, `NaN` | +| `-0`, `0` | `-0`, `0`, `-0` | `0`, `0`, `0` | + +The test now also runs each case with the members in reversed order. A +`Buffer` and a `Uint8Array` with the same bytes passed under the old code only +in the original order. In the reversed order, the old code projected the +`Uint8Array`. + +## Evidence + +- **Work law.** `group-by-work.test.ts` counts the Map and Set iterator steps + in one synchronous commit, at 10, 100, 1,000 and 5,000 members. On + `ea51b67b0`, a `groupBy` count took 157 steps at 100 members against 67 at 10 + members, and an include count took 219 against 129. On the branch, each count + takes the same number of steps at every size: 55 and 117 in the probe runs. + The published count is also checked at each size. +- **Route histories.** The same file deletes the member that arrived first, + empties and refills a route, adds a null correlation key, moves a member + between routes, and updates the parent. Each history checks the published + count. The histories pass on `ea51b67b0` and on the branch, because the + change keeps the result and changes only the work. +- **Group-value law.** The test model `smallestExact` orders values by kind and + type name without production code. On `ea51b67b0`, 8 cells fail. For + example, `expected 'date' to be 'number'`. + +## Mutants + +| Mutant | Outcome | +| --- | --- | +| Row key restored in the route representative | Assertion failure: the include work law (219 against 129) | +| Row key restored in the `groupBy` representative | Assertion failure: the `groupBy` work law and 6 group-value cells | +| Largest exact value selected | Assertion failure in 8 group-value cells | +| First member that arrived selected | Assertion failure in 8 group-value cells | + +## ORC-012 requirement audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Applicable. The three laws and their authority are above. The group-value law is a product decision of this change. | +| ORC-002 | Applicable. The expected counts come from the fixture sizes. The expected group value comes from the test model `smallestExact`, which does not use production identity or serialization. | +| ORC-003 | Applicable. Both test files state each law, its observation and its checkpoint before the assertions. | +| ORC-004 | Applicable to the finite matrices only. No generated grammar is claimed. The sizes 10, 100, 1,000 and 5,000 bound the work law. | +| ORC-005 | Applicable. The tests use the public live-query Collection and its published rows at the return of the commit. | +| ORC-006 | Applicable. The four mutants above fail at the intended checkpoints. | +| ORC-007 | Inapplicable. These tests are finite, not generated properties. | +| ORC-008 | Inapplicable. No stateful reference model changes. | +| ORC-009 | Applicable. "Iterator step" is a test observation, not a production concept. "Exact value" is the value relation that `ValueIdentity.exact` keeps: primitives by value, with `-0` and `NaN` kept apart, and objects, including Dates, binary arrays and Temporal values, by instance. The group-value order compares objects by type tag only; the earlier text of this row said that exact identity compares them by content, which was false. | +| ORC-010 | Applicable, with a gap. Cleanup runs in `finally` blocks, so a cleanup error can replace an assertion error. | +| ORC-011 | Inapplicable. No shared fault was named. | +| ORC-013 | Applicable. The work law is a scaling law. Four sizes over two orders of magnitude separate a constant cost from a cost per member. | +| ORC-014 | Inapplicable. No controlled provider supplies a premise. | + +## Review follow-up + +A high-effort review of `9b021bbdc` raised ten findings. Probes and the +extended oracle confirmed the product defects: + +| Finding | Verdict | Evidence on `9b021bbdc` | Disposition | +| --- | --- | --- | --- | +| F1 `min`/`max` returns a deleted row's value | Confirmed | Rows `0` and `-0`, delete `0`: `min` and `max` return `0`. On `main`: `-0`. Two equal Dates: the deleted row's instance. | Fixed: contributions carry the exact identity of each min or max input. This row first named sum and avg too; the medium follow-up removed them, and the targeted follow-up keys object inputs by row | +| F2 projected value is a deleted row's instance | Confirmed | Two `Date(0)` instances, delete row 1: the deleted instance is projected | Fixed: the representative key holds the instance identity | +| F3 oracle compares only a type label | Confirmed | The F2 defect passed the old oracle | Fixed: the oracle requires a positive member's instance | +| F4 route identity uses parent equality | Evidence gap | An include aggregate cannot project a parent field, so no public observation was found. The mutant that restores the equality identity survives | Uses the parent context instance; recorded as unobserved | +| F5 Date and binary correlation keys never consolidate | Confirmed, accepted | Reference identity keeps distinct key instances apart | Limit below: correctness needs instance identity | +| F6 work law over-promises | Confirmed | Only identical inputs consolidate | The law, changeset and architecture text now state the scope | +| F7 order depends on constructor names and serialization | Confirmed by source | — | Fixed: explicit type tags | +| F8 binary contents serialized into each key | Confirmed | A 1 MiB group value is encoded twice per insert (12.6 MB); `main` encodes it once | Fixed: once, which is the group key's existing cost | +| F9 counter misses array walks | Confirmed by source | — | Fixed: the counter also counts array iteration and callbacks | +| F10 architecture text contradicted | Confirmed | — | Rewritten | + +Measured with the investigation probe (`NODE_ENV=production`, an include +count over one issue, median per comment insert, two runs each): + +| Comments | `main` `2c98b4992` | Branch `f67789cd3` | +| ---: | ---: | ---: | +| 10 | 0.109 / 0.125 ms | 0.168 / 0.162 ms | +| 100 | 0.096 / 0.095 ms | 0.129 / 0.113 ms | +| 1,000 | 0.175 / 0.153 ms | 0.098 / 0.108 ms | +| 5,000 | 0.557 / 0.439 ms | 0.101 / 0.091 ms | + +The 10-comment size runs first in each process, so it includes warm-up. + +Review mutants on `e5449edc2`: + +| Mutant | Outcome | +| --- | --- | +| No exact-input identity (merged retraction kept) | Assertion failure, 4 tests | +| No instance identity in the group-value representative | Assertion failure, 4 tests | +| Binary contents in the order key | Assertion failure, 1 test (binary encoding) | +| Parent context equality identity in the route | Survived: no public observation (F4) | +| `NaN` ordered before `-0` | Equivalent: the two never share a group | + +## Limits + +- The work counter observes Map, Set and array iteration and array callbacks. + A walk through an indexed `for` loop would not count. +- Contributions consolidate only when their representative keys and min or + max inputs are identical. A min or max over objects keeps one contribution + per member. A min or max over distinct values, and an include + correlated on Date, binary, or Temporal key instances, keep one contribution + per distinct input or instance. +- The group key serializes a large binary group value's contents once per + member. That cost predates this change. +- A `groupBy` over values whose exact identity is a reference, such as plain + objects, still has one contribution per distinct object. Equality for those + values is also by reference, so each such group holds one value. +- The fixed per-change overhead from #1740 is a separate cost and is outside + this change. + +## Medium review follow-up (2026-10-08) + +Reviewed head `14d5cd49b`. Fix commits `8db25f3b0` (laws) and `b86aaa7e2` +(production). Ledger: `review-medium-ledger.md` in the task scratch notes. + +- **Rebuilt min/max argument.** An inline subquery rebuilds projected objects + each time it runs, so the retraction of a row carried a new argument + instance. The min or max contribution was keyed by that instance and did not + cancel its insert. RED on `14d5cd49b`: after inserting and deleting a row + with `x: 7`, `max` stayed `7`. The base commit before this pull request did + not have the fault. The identity now comes from the value the min or max + compares (`minMaxInput`), and the work law keeps cycle work constant over + 300 insert and delete cycles. +- **Sum and avg** no longer carry exact inputs. Their reduce adds coerced + numbers, so merging equal inputs cannot change the result. +- **Equal instances.** Among content-equal object group values, the member with + the smallest row key supplies the instance. RED on `14d5cd49b`: when rows + arrived as 2, 1, row 2's instance was projected. The choice no longer depends + on arrival order. +- **Test model.** `exactRank` now matches the documented order: another + primitive, then `-0`, then objects by type tag read with + `Object.prototype.toString`. +- **Include work law** covers insert, an update that moves a member out and + back, and delete. + +| Mutant | Outcome | +| --- | --- | +| Exact input from the raw argument (`14d5cd49b`) | Assertion failure: `max` keeps the deleted value | +| No min or max exact inputs | Assertion failure, 4 tests | +| Instance token as the tie among equal objects (`14d5cd49b`) | Assertion failure: arrival order 2, 1 | +| Row key in the route representative | Assertion failure: include work law | + +Not changed: + +- A parent field in an include aggregate's select is not projected into the + result on this commit or the base, so the route's parent context stays + without a public observation. +- A min or max argument compiles twice per query, once for the aggregate and + once for its exact input. This is a compile-time cost only. +- db-ivm merges hash-equal values by design. Choosing which instance remains + needs per-instance state, which the compiler identity supplies, so the fix + stays in the compiler. + +## Targeted review follow-up (2026-10-08) + +Reviewed head `28a2f8c79`. Ledger: `review-targeted-ledger.md` in the task +scratch notes. + +- **Rebuilt Date min/max (regression).** The medium follow-up keyed a min or + max contribution by the exact identity of the compared value. A rebuilt Date + is a new instance at each evaluation, so a retraction did not cancel its + insert, and the Index kept the deleted row's instance. RED on `28a2f8c79`: + 6 cells, for example `expected [ 'Date(2000)' ] to include 'Date(5000)'`. + The base before this pull request passes all cells. An object input is now + keyed by its row key, as a group value is. +- **Law.** `min and max equal the remaining members' values` deletes members + one at a time. After each delete, the published `min` and `max` must be + exactly one of the remaining members' values that ties for the extreme. + The matrix covers `max` alone, `min` and `max` of one argument, and of two + arguments; signed zero and Dates; stored and rebuilt arguments. +- **Realm.** `group-by.test.ts` runs in the `node` environment. Under jsdom a + Node `Buffer` is not an instance of jsdom's `Uint8Array`, so the binary case + could not see the `Buffer` tag. + +| Mutant | Outcome | +| --- | --- | +| Production of `28a2f8c79` | Assertion failure, 6 cells (rebuilt Dates) | +| `max` without exact inputs | Assertion failure, 8 cells (signed zero) | +| No `Buffer` tag | Assertion failure, 2 tests (`expected 'uint8array' to be 'buffer'`) | +| Object input keyed by instance, not row | Assertion failure, 6 cells | + +Not changed, and recorded as proposals: + +- An include whose parents are equal under query equality but differ exactly + shares one route, so a parent field can come from another parent. This + predates this pull request. +- A single-group aggregate drops every non-aggregate select field, also in an + include, while the same select with `groupBy` throws + `NonAggregateExpressionNotInGroupByError`. This predates this pull request. +- db-ivm `min` and `max` ignore multiplicity, and the Index keeps the latest + record. The compiler's exact inputs work around both. +- The route representative is not covered by a deterministic-choice law. +- The work counter does not count indexed loops. diff --git a/docs/contributing/oracle-reviews/index-unit-retirement-round-1.md b/docs/contributing/oracle-reviews/index-unit-retirement-round-1.md new file mode 100644 index 0000000000..f570ee4ed7 --- /dev/null +++ b/docs/contributing/oracle-reviews/index-unit-retirement-round-1.md @@ -0,0 +1,94 @@ +# Index unit-test retirement: two-block ledger + +Base: `f6857fee9de59dc82cbe596ca7df4a18152004ed` (`origin/main` at worktree creation). +This ledger names the exact legacy assertions proposed for retirement. The +receiving owner is `packages/db/tests/index-update-oracle.property.test.ts`. +The other blocks in each legacy file remain outside this change. + +## Upper-only range ignores an absent lower bound + +- **Legacy source:** `packages/db/tests/btree-index-undefined-values.test.ts`, + `should not drop the minimum key when an upper-only range is exclusive on + the (absent) lower bound` (original line 252). It inserts values 1, 5, 10, + calls `rangeQuery({ to: 10, fromInclusive: false })`, and requires all three + keys. Its observation is the returned direct BTreeIndex set. +- **Law and authority:** An absent bound denotes the start of the index + (`packages/db/src/query/live/ARCHITECTURE.md`, Identity); the documented + `IndexInterface.rangeQuery` accepts separate bounds and inclusivity flags. + An inclusivity flag cannot exclude a value at a bound the caller did not + supply. This law says nothing about internal tree traversal or runtime cost. +- **Independent model and history:** The receiving oracle's source `Map` keeps + key/value ownership. For every upper bound -2, 0, 2, it filters the Map with + `value <= upper`; it makes no lower comparison for an absent lower bound. + Paired reads with an explicit exclusive `from` require `value > lower`, + preserving the distinction stated in the old test's comment. + The existing fixed and random campaigns apply add, same-key update, delete, + and full build to both BasicIndex and BTreeIndex, then check the upper-only + and explicit-bound reads after each action, including empty and + changed-minimum states. +- **Distinction and checkpoint:** A design that applies + `fromInclusive: false` to the implicit minimum drops a modeled key. The + comparison checks the exact returned set after each direct index action. + One wrong-output control removes the minimum from the upper-only read; + another adds the excluded boundary to an explicit-bound read. Both must + fail their intended comparison. +- **Retirement gate:** The new owner and both wrong-output controls passed. + `/root/review_absent_bound` attacked the original and revised claims, + executed the model and controls, and found no blocking survivor in the + bounded numeric scope. The named legacy block was removed; its sibling + blocks remain. + +## Comparator-equal update moves exact membership + +- **Legacy source:** `packages/db/tests/index-update-short-circuit.test.ts`, + `does not use comparator equality to skip an update` (original line 91). + With a case-insensitive comparator, it changes one key from `A` to `a` and + checks that exact `A` lookup becomes empty and exact `a` lookup owns the key. +- **Law and authority:** An ordered index keeps separate exact equality + buckets for values at one comparator position + (`packages/db/src/query/live/ARCHITECTURE.md`, Identity; the existing + comparator-group index oracle). Comparator equality alone does not authorize + skipping a source value replacement. This does not promise bucket object + identity or prohibit harmless maintenance work. +- **Independent model and history:** A source `Map` retains exact strings; + fixed input facts put `A/a` in one order group and `B/b` in another. The + receiving oracle checks both `equalityLookup` and `lookup('eq')`, plus + comparator-group range sets, after each direct action on BasicIndex and + BTreeIndex. The fixed trace updates a singleton `A → a → A` before adding + peers, then checks populated same-group and cross-group updates. Its + existing fixed/random + custom-comparator property now also replaces an object with a fresh object + in the same group before crossing to another group. The group IDs and exact + object handles are input facts, not production classifications. +- **Distinction and checkpoint:** A wrong design that skips a comparator-equal + `update` leaves the old exact lookup populated at the returned-action cut. + A singleton no-op update control must fail the same source-Map comparison. + A second control makes `lookup('eq')` return the comparator group while leaving + `equalityLookup` intact; it must fail as well. The fixed string trace + preserves the original value-domain and lookup-dispatch path; the generated + object lane probes the general distinction after representative retirement. +- **Retirement gate:** The string and generated witnesses and both controls + passed. `/root/review_equal_update` found two gaps in the first draft: it + checked only `equalityLookup`, and it updated only a populated index. The + revised witness exercises `lookup('eq')` and the original singleton path; + the reviewer re-ran the relevant cases and found no remaining blocker. + The named legacy block was removed; its sibling blocks remain. + +## Execution and workflow result + +The post-retirement run of the receiving oracle and both remaining legacy +files passed: 397 tests, zero type errors. Before retirement, the same three +files passed with both old blocks present. The new numeric and string controls +deliberately inject wrong returned results or skipped work and each reaches an +assertion failure at the intended public observation. No production defect was +demonstrated in these two blocks, so production code was not changed. + +This round exposed two useful review gates for the broader cleanup. A +same-group update on populated data did not cover the original singleton +history, and checking `equalityLookup` did not cover `lookup('eq')` dispatch. +Both gaps survived the author's first green run and were found by an +independent reviewer. The generated object fixture also initially reused a +retired key; distinct reuse keys repaired that model-fixture error before the +final run. Future blocks should record the exact public method, smallest +state, old comment's law, and a wrong-design control before deletion. A +passing test count by itself is not a retirement gate. diff --git a/docs/contributing/oracle-reviews/index-unit-retirement-round-2.md b/docs/contributing/oracle-reviews/index-unit-retirement-round-2.md new file mode 100644 index 0000000000..e14bad4bfd --- /dev/null +++ b/docs/contributing/oracle-reviews/index-unit-retirement-round-2.md @@ -0,0 +1,92 @@ +# Index unit retirement: nullish exact membership + +The source-isolated loss audit used baseline `73a5d4ec4`. The receiving owner is +`packages/db/tests/index-update-oracle.property.test.ts`. No source block below +was an exact witness in that baseline: its generated update values were numeric, +and its fixed nullish case read ranges and cursors rather than equality buckets. + +## Transfer ledger before retirement + +| Legacy block | Smallest source state and direct action | Exact source observation and law | Plausible wrong result | +| --- | --- | --- | --- | +| `index-update-short-circuit.test.ts`, `does not conflate undefined and null` | BasicIndex and BTreeIndex: fresh `a=undefined`; update `a→null` | At update return, `lookup('eq', undefined)=∅` and `lookup('eq', null)={a}`. The title states distinct exact membership. | A loose-nullish shortcut leaves `a` in the old bucket; a dispatch that returns the comparator group includes it in both. | +| `btree-index-undefined-values.test.ts`, `should update item from defined to undefined value` | BTreeIndex: fresh `a=1`; update `a→undefined` | At update return, `equalityLookup(1)=∅` and `equalityLookup(undefined)={a}`. | The old key retires but the new undefined key is lost. | +| Same file, `should update item from undefined to defined value` | BTreeIndex: fresh `a=undefined`; update `a→1` | At update return, `equalityLookup(undefined)=∅` and `equalityLookup(1)={a}`. | The new key arrives but the undefined membership remains. | +| Same file, `should update item from undefined to undefined (same value)` | BTreeIndex: fresh `a=undefined`; update `a→undefined` | At update return, `equalityLookup(undefined)={a}`. This checks conservation, not bucket identity. | Remove without re-add drops `a`. | +| Same file, `should handle mixed undefined and null values` | BTreeIndex: fresh adds `null1=null`, `undef1=undefined`, `num1=1` | The two exact lookups return their respective singleton sets. The block checks no ordering despite its enclosing heading. | Returning the shared nullish comparator group for either value includes the other key. | + +The BTree file's opening infinite-loop and sentinel comments concern traversal; +they do not establish an equality law. `IndexInterface` exposes both lookup +methods, and the established direct-index tests distinguish nullish exact +values. The receiving oracle already states the separation between exact +membership and comparator position. This transfer is limited to direct index +methods. A live-query WHERE comparison with a nullish operand follows +three-valued logic, so it is outside this law. + +## Receiving law and evidence to check + +A plain source `Map` retains each key's exact value. The oracle recomputes +equality buckets without index normalization or comparator classification. +It drives each source action through BasicIndex and BTreeIndex and checks +`keyCount`, indexed keys, `equalityLookup`, and `lookup('eq')` after return. +Five fresh singleton or coexisting-add histories reconstruct the source cases; +an additional `null→undefined` singleton and fixed/random put/delete histories +challenge nearby legal transitions. The generated domain is three keys and +`undefined`, `null`, `1`, `2`. A direct update of an absent source key is +excluded; the driver uses `add` for that source action. + +A skipped `undefined→null` update and a wrong `lookup('eq')` union are intended +to fail the model comparison. Both controls must reach the relevant method and +fail at the returned-action lookup assertion. The nullish scope does not imply +ordered, range, `IN`, live-query, bucket-identity, or instrumentation coverage. + +## Execution and hostile review + +The reviewed code head is `0d21a22ec56bc06a47776690d7cdbfded0ae9acc`. +The five ledger blocks were removed at that head; no other test block in either +source file was retired. The oracle and both remaining source files passed +411/411 targeted tests with zero type errors. The named replay manifest's +selector tests passed. Fixed seed `20261012` and a seedless campaign each run +the same nullish property with 100 histories per index type. Guarded replay at +seed `20261012`, path `0` selected only the BasicIndex and BTreeIndex random +lanes and recorded one completed 100-run witness for each. + +The in-test skipped-update and unioned-lookup controls fail at the intended +Map-versus-lookup assertion. For replay failure fidelity, a temporary wrong +expected rule treated every `undefined` bucket as empty. It failed the +`equalityLookup(undefined)` comparison with seed `20261012`, shrink path +`0:1:1:1:1:1:0`, and counterexample `put(a, undefined)`. The guarded replay +reached one BasicIndex witness, marked it failed, and exited nonzero. After +restoring the oracle, that exact replay passed. This was a temporary model +fault for replay calibration, not a production mutant or a production bug. + +The independent hostile reviewer `/root/review_nullish_membership` first found +that the new property lacked a checked replay manifest entry. That setup +failure was not counted as an oracle kill. The manifest entry, selector, and +async property reporter were repaired. The reviewer rechecked the committed +head after the commit hook and found no remaining semantic blocker. No +production defect was demonstrated, so production code did not change. + +## Oracle guide audit at the reviewed head + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | The established direct-index tests and `IndexInterface` exact-lookup methods supply the law. The claim excludes live-query WHERE nullish equality and the other observations listed above. | +| ORC-002 | A native source Map computes exact buckets without production normalization, comparator classification, or index storage. | +| ORC-003 | Opening prose states the law and limits; adjacent prose names the Map rule, legal actions, real index methods, observed sets, and returned-action checkpoint. | +| ORC-004 | Five fresh histories reconstruct the old witnesses; the adjacent `null→undefined` case and generated put/delete histories add nearby states. Removing either nullish value loses the distinguishing law; removing defined values loses defined/nullish moves; one key loses surviving-peer histories. The domain is three keys, four values, and at most 40 generated actions. An absent-key direct `update` is excluded because the source driver calls `add`. | +| ORC-005 | Both index types call real `add`, `update`, and `remove`, then compare exact returned sets and key count after each action. The fixed histories reach all five source states. | +| ORC-006 | A skipped `undefined→null` update and a unioned `lookup('eq')` result each cause an assertion failure at the exact-membership check. | +| ORC-007 | Fixed and seedless campaigns share one property, generator, recorder, comparison, and 100-run budget. The named guarded replay reaches the requested seed/path, and a captured controlled failure replays as a failed witness. | +| ORC-008 | The Map keeps key/value ownership because a later update or delete can distinguish sources with equal bucket sizes but different owners. | +| ORC-009 | `put` is a model source action mapped to index `add` or `update`; `delete` maps to `remove`. The shared terms `history`, `observation`, and `checkpoint` follow the glossary. | +| ORC-010 | The wrong-result spies restore in `finally`; the replay guard retains the failure witness separately from process status. No external resources are acquired. | +| ORC-011 | No plausible shared semantic fault requiring a second formulation was identified for the bounded nullish Map rule. Ordered comparator behavior is deliberately outside it. | +| ORC-012 | This record names the exact reviewed code head, audit result, controls, execution, and remaining scope. | +| ORC-013 | The `undefined→null` singleton and coexisting null/undefined adds distinguish no-op update and bucket-union alternatives at returned-action lookup checks. | +| ORC-014 | No controlled provider or host premise supports a claim about a real provider or host. | + +The next nullish candidates need their own observations: ordered and cursor +reads, `IN` union, custom comparator arguments, and the public live-query +path. The remaining source tests continue to own their exact assertions until +those transfers pass the same gates. diff --git a/docs/contributing/oracle-reviews/index-unit-retirement-round-3.md b/docs/contributing/oracle-reviews/index-unit-retirement-round-3.md new file mode 100644 index 0000000000..93d4543428 --- /dev/null +++ b/docs/contributing/oracle-reviews/index-unit-retirement-round-3.md @@ -0,0 +1,97 @@ +# Index unit retirement: exact and IN membership + +The source-isolated Field Lab loss audits used `b56d1a45c` as the frozen +baseline. The receiving owner is +`packages/db/tests/index-update-oracle.property.test.ts`. Its prior nullish +Map law checked `equalityLookup` and `lookup('eq')`, but explicitly excluded +`IN`. The four legacy blocks below were retired at reviewed code head +`d880f3139bbbba7141dc658d84d3602217c2960f`. + +## Transfer ledger before retirement + +| Legacy block in `btree-index-undefined-values.test.ts` | Smallest source history and returned-add observation | Plausible wrong result | Receiving witness | +| --- | --- | --- | --- | +| `should find items with undefined indexed value` | Add `a=active`, `b=undefined`, `c=inactive`, `d=undefined` on `status`; `equalityLookup(undefined)={b,d}`. | A lookup drops one of the two owners or includes a string peer. | The fixed four-add string history computes every exact set from a source Map after each add. | +| `should return empty set when no undefined values exist` | Add `a=1`, `b=2`; `equalityLookup(undefined)=∅` in a nonempty index. | A missing-bucket fallback returns a defined key. | The fixed all-defined history reads the absent group after both adds. | +| `should handle undefined in inArrayLookup` | Add `undef=undefined`, `one=1`, `two=2`, `three=3`; `inArrayLookup([undefined,1])={undef,one}`. | The method ignores the undefined operand, returns only one group, or includes a distractor. | The fixed four-add history and mixed-list probe compare the full set from the Map. | +| `should handle inArrayLookup with only undefined` | Add `undef1=undefined`, `undef2=undefined`, `one=1`; `inArrayLookup([undefined])={undef1,undef2}`. | An undefined-only list returns empty or loses one owner. | The fixed three-add history and singleton-list probe compare the full set from the Map. | + +The `IndexInterface` exposes direct exact and `IN` methods. A source Map owns +keys and values; equality selects one exact-value group, while `IN` selects +keys whose source value occurs in its operand list. The expectation does not +call either production lookup, normalized-key helper, or comparator. The +driver checks both direct methods and `lookup('eq')`/`lookup('in')` for +BasicIndex and BTreeIndex after each source action returns. A generated +three-key, five-value put/delete grammar checks nearby transitions with empty, +repeated, both orders of a mixed list, and nullish-only operand lists. The +fixed four-key and string histories preserve distinctions outside that domain. + +This is a direct-index set-selection law. It does not specify live-query WHERE +or `IN` nullish semantics, ordered traversal, range lookup, bucket identity, +or instrumentation. The `index-update-short-circuit.test.ts` block that also +calls `lookup('in')` stays: its `performance.now` non-call assertion cannot be +observed by this result oracle. The BTree file's traversal, range, custom +comparator, storage, and live-query blocks also stay. + +## Execution and hostile review + +The four ledger blocks were removed; the BTree file now has 12 tests instead +of 16. The receiving oracle, the remaining BTree file, and the short-circuit +file passed together: 423/423 tests and no type errors. The receiving oracle's +fixed-seed and seedless campaigns use the same 100-run property for each index +type. The guarded replay for `index-update.nullish-membership` at seed +`20261012`, path `0` reached two executions, one per index type, with 100 runs +each. The named replay manifest checks passed. + +The in-test wrong-result controls make a lookup lose one undefined owner, +drop the trailing undefined operand in `[1, undefined]`, lose one owner from +`[undefined]`, and return only the first group through `lookup('in')`. Each +reaches the relevant method and fails the Map comparison. The earlier +skipped-update and unioned-equality controls still pass their calibration. + +For failure replay, a temporary wrong expected rule cleared the Map result +for `[undefined, 1]`. The random lane failed at the direct `inArrayLookup` +comparison on `put(a, undefined)` with seed `20261012` and shrink path +`0:1:0:1:1:1:1:0`. Guarded replay reached both index lanes, recorded a failed +one-run witness for each, and exited nonzero. After removing the temporary +fault, that exact replay passed both lanes. This was a model fault for checker +and replay calibration, not a production mutant or a production defect. + +The fresh hostile reviewer `/root/review_in_membership` inspected the source +blocks, production implementations, ledger, oracle, and coverage map. It found +a position-sensitive survivor: all original mixed probes had undefined first. +Adding `[1, undefined]` and a control that drops its trailing undefined +operand killed that survivor. The reviewer independently ran the nullish +membership slice (36 passed, no type errors) and found no remaining semantic +blocker to retiring the four named blocks. It did not run a production mutant. +No production change was justified by this round. + +The reviewer rechecked committed head +`d880f3139bbbba7141dc658d84d3602217c2960f` after the commit hook. The +commit removed exactly the four ledger blocks and retained the reviewed oracle +and coverage claims. The reviewer reran all three targeted files: 423/423 +passed with no type errors. `git show --check` was clean. No new blocker arose. + +## Oracle guide audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | `IndexInterface` and the established direct-index tests supply exact and `IN` set-selection obligations. The claim excludes WHERE semantics, ordered and range reads, bucket identity, timing, and arbitrary JavaScript values. | +| ORC-002 | A source Map supplies ownership. Expected exact sets come from Map grouping; expected `IN` sets come from operand membership over source rows. Neither uses production normalization, comparator classification, or another production lookup. | +| ORC-003 | Prose beside the model names the law, grammar, direct index driver, returned sets, and after-action checkpoint. | +| ORC-004 | The four fixed histories reconstruct every retired source state and operand. The generated grammar has three keys, five numeric/nullish values, up to 40 put/delete actions, and empty, repeated, mixed, reversed-mixed, and nullish-only lists. The fixed string and four-key histories cover values and width outside that grammar. Removing undefined loses the reported case; removing null loses nullish distinction; removing defined values loses mixed selection and moves; one key loses peer retention. Direct update of an absent source key is excluded. | +| ORC-005 | Both real index classes execute `add`, `update`, and `remove`. The checker compares `equalityLookup`, `lookup('eq')`, `inArrayLookup`, `lookup('in')`, key count, and indexed keys after each action returns. | +| ORC-006 | Four new wrong-result controls fail at the intended comparison; the temporary wrong expected rule also fails the generated `IN` comparison. No production mutant was run. | +| ORC-007 | Fixed and seedless campaigns share one generator and check with 100 runs each per type. Guarded seed/path `0` replay reached both lanes; the captured `0:1:0:1:1:1:1:0` failure replayed as failed, then passed after restoration. | +| ORC-008 | The Map retains each key's value because a later update or delete can distinguish states with the same bucket sizes but different owners. No model state was removed. | +| ORC-009 | `put` maps to index `add` or `update` according to source ownership; `delete` maps to `remove`. `IN` operands are lists of source values, and the after-action comparison is the checkpoint. | +| ORC-010 | Wrong-result spies restore in `finally`. Guarded replay records the property failure separately from child exit and cleans its temporary witness channel. The temporary model fault was removed after replay. | +| ORC-011 | No plausible shared semantic fault requiring a second independent formulation was identified for this bounded primitive-value Map rule. The review's order-sensitive survivor was an enforcement gap and is now checked. | +| ORC-012 | This versioned ledger records source assertions, law limits, execution, hostile review, and remaining scope against exact reviewed code head `d880f3139bbbba7141dc658d84d3602217c2960f`. | +| ORC-013 | `[undefined, 1]` versus `[1, undefined]` distinguishes operand-order dependence; `[undefined]` with two owners rejects first-owner-only selection; `[]` and `[1, 1]` check empty and duplicate boundaries. The wrong-result controls fail at returned-set comparisons. | +| ORC-014 | No controlled provider or host premise is used to claim real-provider or host behavior. | + +The remaining legacy blocks keep their distinct observations. The +short-circuit timing assertion, BTree traversal and range behavior, custom +comparator arguments, storage representation, and public live-query result +still need their own owners before retirement. diff --git a/docs/contributing/oracle-reviews/index-unit-retirement-round-4.md b/docs/contributing/oracle-reviews/index-unit-retirement-round-4.md new file mode 100644 index 0000000000..114c47c58e --- /dev/null +++ b/docs/contributing/oracle-reviews/index-unit-retirement-round-4.md @@ -0,0 +1,120 @@ +# Index unit retirement: undefined ordered reads and ranges + +The source-isolated Field Lab loss audits compared the remaining direct +traversal and range blocks in `btree-index-undefined-values.test.ts` against +`index-update-oracle.property.test.ts` at frozen head +`df18f41bba07589c4aa8a4b17ca676be7d39dc15`. The receiver checked one-owner +undefined cursors and numeric ranges, but its generated nullish lane checked +only exact and `IN` membership. The table freezes the source assertions before +changing the receiver. A proposed witness is not retirement evidence until it +executes, rejects a wrong result, and survives hostile review. + +| Legacy block | Source assertion at the returned-action checkpoint | Loss in frozen receiver | Proposed transfer | +| --- | --- | --- | --- | +| `take(n, undefined)` versus `takeFromStart(n)` | After `a=undefined,b=1,c=2`, `takeFromStart(3)` has three keys and `take(3, undefined)=[b,c]`. | The explicit cursor has a singleton analogue; the default-policy endpoint cut is absent. | Sort source rows with nullish values first; compare both exact arrays after each add, with a fixed three-row history. | +| `takeReversed(n, undefined)` versus `takeReversedFromEnd(n)` | In a fresh copy of the same state, the endpoint returns three keys and the explicit cursor returns none. | The explicit reverse cursor has a singleton analogue; the nullish endpoint cut is absent. | Compare both exact reverse arrays in the same fixed history. | +| Store and retrieve three undefined owners | After `a,b,c=undefined`, `takeFromStart(10)=[a,b,c]` and `equalityLookup(undefined).size=3`. | Two-owner exact membership is fixed; three-owner ordered output is absent. | Pin all three owners and compare exact arrays and source-derived equality sets. | +| Remove one of two undefined owners | After adding `a,b=undefined` and removing `a`, exact lookup has size one and contains `b`. | Reachable in generated put/delete histories, but the two-owner deletion cut is not pinned. | Pin the removal and compare the complete exact set after it returns. | +| Iterate past multiple undefined owners | After `undef1,undef2,undef3=undefined`, then `one=1,two=2`, `take(10,undefined)=[one,two]`. | One-owner cursor and multi-owner membership checks never meet. | Pin the five-row history and compare exact cursor arrays after each add. | +| Explicit undefined versus omitted lower range bound | After `undef=undefined,one=1,two=2,three=3`, `{from:undefined,to:2}` inclusive returns exactly `{undef,one,two}`; `{to:2}` returns three keys. | Numeric bounds and nullish upper-only reads are separate fixtures. | Source-derived range sets check explicit and omitted lower bounds, plus exclusive variants that distinguish them, after a fixed four-row history. | +| Double undefined range bound | After `a,b=undefined,c=1`, `{from:undefined,to:undefined}` inclusive returns exactly `{a,b}` and excludes `c`. | No double-bound range check with two undefined owners. | Pin this state and compare the full set against source-derived bounds. | + +The proposed law follows `DEFAULT_COMPARE_OPTIONS` and `defaultComparator` +in `packages/db/src/utils.ts` and `packages/db/src/utils/comparison.ts`, the +direct cursor comments in `packages/db/src/indexes/btree-index.ts`, the +key tie-order contract exercised by `deterministic-ordering.test.ts`, and the +frozen legacy assertions. `IndexInterface` establishes the direct methods but +its reference page does not specify these ordering details. The bounded law +uses default ascending order with nullish values first, positive numbers +ascending, and row keys ordered within one comparator position. Its domain is +`undefined`, `null`, and the numbers 1–3; negative numbers are outside it. +`null` and `undefined` occupy one comparator position for ordered reads while +remaining distinct exact lookup groups. A source Map retains key ownership. +The model sorts and filters that Map; it does not call the index comparator, +normalization, cursor, or range machinery to compute expected results. +`take` and `takeReversed` exclude the cursor's comparator position. Endpoint +methods have no cursor. A range bound exists only when its field is supplied; +an inclusivity flag without that bound has no effect. The production path is +the direct `IndexInterface` on both BasicIndex and BTreeIndex. Unfiltered +ordered arrays at n=1, 3, and 10 and full sets for ten selected forward range +shapes are compared after each add, update, or remove returns. The owner does +not claim all bound combinations or filtered takes. + +The default-order and string-order tests inspect `orderedEntriesArray`, a test +view of internal entries. Those two blocks remain until their private +observation has a receiving owner or the test's representation claim is +explicitly retired. The custom-comparator argument, `valueMapData` storage, +and public live-query blocks also remain. This direct-index law makes no claim +about those observations or live-query scheduling. + +## Execution and hostile review + +The seven ledger blocks were removed after the hostile review. The BTree file +now has five tests. The receiving oracle and that file passed together: +415/415 tests. The package TypeScript check and `git diff --check` passed. +The fixed-seed and seedless nullish campaigns run the same source actions, +checker, and 100-run budget for both index classes. A guarded replay at seed +`20261012`, path `0`, reached both classes with 100 successful runs each. The +named replay harness passed its 36 tests. + +Five wrong-result controls force an endpoint read to lose the undefined owner, +an explicit cursor to repeat one of three undefined owners, an inclusive +explicit lower bound to drop undefined, a double undefined range to lose a +peer, and a null bound to drop its undefined comparator peer. Each fails the +intended source-Map comparison for both index classes and restores its spy in +`finally`. The earlier exact and `IN` controls continue to run. + +For failure replay, a temporary wrong model rule cleared the expected set for +an explicit `undefined` lower bound and upper bound 2. The fixed campaign +failed at the forward range comparison on `put(a, undefined)`, seed `20261012`, +shrink path `0:0:0:0`. Guarded replay at that seed and path recorded one failed +run for each index class and exited nonzero. After removing the temporary rule, +the same guarded replay passed both classes. This was checker calibration, not +a production mutant or product defect. No production code changed. + +The fresh hostile reviewer `/root/review_ordered_range_round4` read the full +legacy file, ledger, changed oracle, coverage map, reference material, and both +index implementations. It identified a surviving wrong design in the broader +claim: a range implementation could treat a `null` bound as an exact-null +bucket because no probe used null as a bound. Inclusive and exclusive null +probes plus a wrong-result control closed that gap. It also required the +contract authority and domain limits to be precise. Its final block-by-block +verdict found no lost source assertion among the seven retired blocks. It did +not run a production mutant. The default-order internal-entry, string-order +internal-entry, custom-comparator argument, storage-view, and live-query tests +remain in the source file. + +## Oracle guide audit + +| Requirement | Evidence and limit | +| --- | --- | +| ORC-001 | The default comparator, BTree cursor comments, deterministic key tie ordering, and frozen direct tests support the bounded law. The interface supplies the methods, not the full semantics. The claim is limited to default options, values `undefined`/`null`/1–3, unfiltered takes at n=1/3/10, and ten selected forward range shapes. | +| ORC-002 | A source Map owns rows. A fresh value-position sort predicts arrays; separate model-bound objects filter source rows for ranges. Neither calls production normalization, comparator, or traversal to compute an expected result. | +| ORC-003 | Opening and adjacent prose distinguish the law, source Map, action grammar, direct index driver, returned arrays/sets, and after-action comparison. | +| ORC-004 | Fixed three-row endpoint, five-row repeated-owner, two-owner removal, four-row range, and double-bound histories reconstruct all retired assertions. The generated put/delete tail has three owners and five values over 1–40 actions. Removing an endpoint method loses the absent-cursor distinction; one owner loses peer traversal; removing null loses comparator-position overlap; removing numeric successors loses cursor continuation; omitting bound presence loses the exclusive comparison. Negative numbers, arbitrary types, filtered takes, reverse ranges, and direct update of an absent owner are excluded. | +| ORC-005 | BasicIndex and BTreeIndex direct methods run after returned add, update, or remove. Exact arrays and complete sets preserve omissions, duplicates in arrays, order, and wrong membership. | +| ORC-006 | Five injected wrong results fail at the intended comparisons. The temporary wrong model rule failed at the forward range comparison and was removed. | +| ORC-007 | Fixed and seedless lanes use the same generated property and 100-run budget. Guarded seed/path `0` replay reached both lanes; captured failure path `0:0:0:0` failed both lanes under the temporary rule and passed after restoration. | +| ORC-008 | No model state was added: the existing Map retains owner-to-value association, needed for later update and delete. Sorted rows and bounds are recomputed observations. | +| ORC-009 | A model `put` maps to add or update according to source ownership; `delete` maps to remove when owned. Model comparator position combines null and undefined only for order/range, while exact membership keeps them distinct. | +| ORC-010 | Spies restore in `finally`; the guarded runner recorded the primary mismatch separately from process exit and removed its private witness channel. | +| ORC-011 | The reviewer found a missing probe, not a fault shared by the model and production. The added null-bound probe distinguishes the model rule from an exact-bucket interpretation. | +| ORC-012 | This versioned ledger holds source assertions, transfer, limits, execution, hostile verdict, and the exact committed-head recheck below. | +| ORC-013 | Explicit inclusive versus exclusive undefined and null bounds, paired with omitted bounds, distinguish presence and comparator-position grouping. The wrong-result controls fail at those comparisons. | +| ORC-014 | No host or external provider behavior is claimed. | + +The public live-query issue regression remains the only test in this file that +crosses the Collection/query path. The other four retained tests observe +private BTree ordering or storage and custom-comparator arguments; direct +source-Map arrays and sets cannot replace those observations without a +separate contract decision and receiving witness. + +## Exact committed-head recheck + +The same hostile reviewer rechecked committed code head +`a72a7043d5750b33100ab95b7d3c6a490b15626c` after the commit hook. The +commit deletes exactly the seven reviewed blocks and keeps all five intended +legacy tests. The committed oracle retains the Map comparisons, null-bound +probes, and five wrong-result controls; no temporary calibration fault remains. +The two targeted files passed 415/415 tests with no type errors, and +`git show --check --oneline HEAD` passed. The reviewer found no new blocker. diff --git a/docs/contributing/oracle-reviews/initial-full-source-sync-readiness.md b/docs/contributing/oracle-reviews/initial-full-source-sync-readiness.md new file mode 100644 index 0000000000..0d13f9c5bc --- /dev/null +++ b/docs/contributing/oracle-reviews/initial-full-source-sync-readiness.md @@ -0,0 +1,143 @@ +# Initial full-source ordered load readiness review + +Evidence by revision, on `fix-full-source-sync-readiness`: + +- First campaign: oracle commit `bc2c9438b`, production fix `a73a35bac`. The + RED results on `main` and the first mutant results refer to this campaign. +- After merging `main` with #2055 (merge `5d4802052`): the eager and + joined-filter witnesses and the merged-code mutant results in "After merging + #2055" were run on `afef46744`, which adds those witnesses to `5d4802052`. +- After merging `main` with #2060 (merge `a0537aa0e`): the ordered-lifecycle + and loader oracles (332 tests) and every CI test group passed on + `a0537aa0e`. The production gate is unchanged from `afef46744`. + +## Contract and evidence + +#1896 made an ordered, limited live query ready in the call that creates it +when every acquisition for its initial window returns literal `true` after +its establishing receipts apply. Its stated exceptions are explicit window +moves, repair, truncate replay, and framework render timing. + +A plan that requires the full source (an inner or right join, a functional or +residual predicate, grouping, `having`, `distinct`, an indirect order, or a +custom string comparator) loads its ordered source with one filtered +full-source request from `OrderedSourceLoader.start()`. The loader classified +every `full-source` request as an authoritative repair, so this initial load +was excluded from the synchronous cut. On `main`, such a query over an eager +source, or over an on-demand source whose `loadSubset` returns `true`, reported +`loading` with no rows at creation. + +The authority for the law is the #1896 cut in +`packages/db/src/query/live/ARCHITECTURE.md` (initial readiness section) and +the #1896 pull request description, which states the rule for every ordered +and limited query whose required source work finishes synchronously. This +change revises that section to name the initial full-source load and a later +full-source fallback explicitly. + +The fix classifies by purpose. The first full-source request of a loader that +has not settled a source request and owes no ordering repair is an initial +load and may settle synchronously. A later full-source request keeps the +asynchronous path. + +## Observations + +| Cell | `main` | Fix | +| --- | --- | --- | +| On-demand, synchronous, 4 features (inner join, `fn.where`, `distinct`, custom collation) | `{"rows":[],"status":"loading"}` | `{"rows":[1,2],"status":"ready"}` | +| On-demand, Promise, 4 features | loading | loading | +| Eager source, 4 features | `{"rows":[],"status":"loading"}` | `{"rows":[1,2],"status":"ready"}` | +| Truncate replay, 4 features | prior window at the replay call, replacement a task later | same | +| Warm readiness: full-source fallback after a settled ordered request (existing case) | loading | loading | +| React first layout commit, `fn.where` + `orderBy` + `limit` | see review head | `{ ids: ['1','2'], status: 'ready' }` | + +## Mutant results + +Each mutant ran against `tests/query/ordered-lifecycle-oracle.property.test.ts`. + +| Mutant | Outcome | +| --- | --- | +| Gate keyed on request kind: every `full-source` request settles synchronously | Assertion failure: warm readiness case, `expected 'ready' to be 'loading'` | +| Promise results may settle synchronously | Assertion failures in 145 tests | +| Initial full-source load still excluded (the `main` behavior) | Assertion failures in the 4 synchronous on-demand cells | +| Initial-load test without the settled-request condition | Assertion failure: warm readiness case | + +The truncate replay cells do not distinguish the kind-based mutant: replay +publishes through the subscription's replay barrier, not this loader gate. The +warm readiness case is the distinguishing neighbour for that design. + +## After merging #2055 + +**Main check.** Before the merge, this branch's witnesses ran on `main` at +`25201da6c` (which includes #2027 and #2055). All 8 synchronous full-source +cells and the React first-commit witness still failed, for example +`Expected {"rows":[1,2],"status":"ready"}, received {"rows":[],"status":"loading"}` +and `expected { ids: [], status: 'loading' } to deeply equal { ids: [ '1', '2' ], +status: 'ready' }`. #2027 changed the infinite-query hooks' `startSync`, not this +gate, so the gap remained. + +**Combined gate.** An initial load of any kind, and an eager bounded +ordered-prefix repair (#2055), settle synchronously on literal `true`: + +```ts +(!isAuthoritativeRepair || + isInitialFullSource || + (this.readsBoundedPrefix && !isFullSource)) +``` + +**Changed expectation.** #2055 added `keeps an eager full-source ordered window +loading until its load settles`, pinning `main`'s asynchronous timing while its +own scope stayed narrow. Recorded predictions before the edit: `main` and #2055 +predict `{loading, 0 rows}` at creation; this branch's law predicts +`{ready, [4, 3]}`. The authority is #1896's rule and the user's decision to close +this gap. The test is now `publishes an eager full-source ordered window at +creation`. + +**Joined-filter interaction.** `keeps the first eligible joined rows after a +synchronous full-source load` combines a function filter with #2055's +LEFT-joined filter history: a label-only update to distant row 10, then a delete +of visible row 2. The window moves from `[2]` to `[4]`. A function filter makes +the plan read its whole source, so the graph holds every row and the top-K can +always reach the next eligible row. A mutant that sends this plan through the +bounded prefix path (and ignores the joined filter) survives: it is equivalent +within this domain, because #2055's hazard needs a bounded read that left an +eligible row out. `main`'s gate fails the witness at creation. + +**Mutants on the merged code** (ordered-lifecycle, loader, ordered-work and +pagination oracles, 724 tests): + +| Mutant | Outcome | +| --- | --- | +| Every `full-source` request settles synchronously | Assertion failure, 1 test: warm readiness, `expected 'ready' to be 'loading'` | +| No settled-request check | Assertion failure, 1 test: warm readiness | +| Promise results settle synchronously | Assertion failures, 275 tests | +| Initial full-source excluded (`main`) | Assertion failures, 9 tests | +| Bounded prefix repair disjunct dropped (pre-#2055) | Assertion failures, 2 tests (#2055's loader and NaN pagination cases) | +| Full-source repair on a bounded-prefix plan settles synchronously | Survival, permitted by the contract. #1896's cut "does not apply to repair", which scopes the promise rather than requiring asynchronous timing. The narrow gate keeps `main`'s timing by choice. | + +## ORC-012 requirement audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Applicable. The law and its authority are above. The claim is limited to the initial full-source load; later full-source fallback, repair, replay, explicit window moves, and framework render timing are unchanged. | +| ORC-002 | Applicable. The expected observation is the same two-valued finite reference as the #1896 cells: ready with ids [1, 2] for literal `true`, loading with no rows for a Promise. The ids come from a three-row source ordered by rank, not from the production comparator. | +| ORC-003 | Applicable. The new section opens with the law, the model, the driver's request-shape check, and the excluded neighbouring cases. The file's opening prose states the revised cut. | +| ORC-004 | Applicable. A finite matrix: 4 features × 2 settlements on an on-demand source, 4 eager cells, and 4 replay cells. Each cell is enumerated; the full-source request shape is asserted, so a plan that silently took the ordered path fails. The residual-predicate feature is not user-constructible and is outside this grammar. | +| ORC-005 | Applicable. The driver calls `preload()` on a real live query and observes `toArray` and `status` at the same call, before awaiting. | +| ORC-006 | Applicable. The `main` behavior and three hostile mutants fail at the intended checkpoint (table above). | +| ORC-007 | Not applicable. The new cells are a finite matrix, not a generated property. | +| ORC-008 | Not applicable. No stateful reference model changes. | +| ORC-009 | Applicable. "Initial full-source load" means the first `full-source` request from `start()` before any source request settles. It is not a production type; the loader derives it from `hasSettledSourceRequest` and `needsOrderingRepair`. | +| ORC-010 | Applicable. The drivers use the file's `finishOracleCleanup`, which keeps the primary failure and every cleanup error in an `AggregateError`. | +| ORC-011 | Not applicable. No shared-fault hypothesis was named. | +| ORC-013 | Not applicable. No threshold or range law. | +| ORC-014 | Applicable, limited. The on-demand cells use a controlled provider. The eager cells and the React cell use real eager Collections. No adapter package (Query Collection, Electric) is exercised here. | + +## Unresolved + +- A full-source fallback inside the initial ordered chain, such as the warm + readiness case's inexpressible null boundary, stays asynchronous. #1896's + oracle pins that timing; making it synchronous would be a separate decision. +- `groupBy` and `having` set `requiresFullSource` but were not added as cells. +- No law fixes the timing of a full-source repair on an eager source. The gate + keeps `main`'s asynchronous timing; making it synchronous would be a separate + decision with its own publication evidence. diff --git a/docs/contributing/oracle-reviews/issue-1589-live-electric-opfs.md b/docs/contributing/oracle-reviews/issue-1589-live-electric-opfs.md new file mode 100644 index 0000000000..17ac013cda --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-1589-live-electric-opfs.md @@ -0,0 +1,81 @@ +# Issue #1589 live Electric OPFS oracle review + +- Reviewed implementation and oracle commit: `2c5e83cb2f448d214e86f67c96557553d936acc4`. +- Comparison main: `8df353a68b841ce55f76230b1197c3bf580d43fe`. +- Executable owner: `packages/browser-db-sqlite-persistence/e2e/electric-resume-two-tab.opfs.spec.ts`. +- Controlled order witness: `packages/browser-db-sqlite-persistence/tests/browser-coordinator.test.ts`. + +## Contract and observed histories + +Issue #1589 reports two connected failures. Collections with different schema +versions can route a write through the wrong adapter and reset each other's +rows. A stale Electric resume marker can then certify an empty local baseline +as ready. The current contract requires each Collection to keep its own schema +and durable rows. An incomplete baseline cannot authorize a non-initial +Electric cursor. + +The fixed browser grammar has two Collections with schema versions 1 and 2. +Each has an independent PostgreSQL table. A Chromium leader loads both. A +follower opens the same OPFS database, then takes leadership. The test adds a +source row to each table and checks public and durable rows. It next creates +one of two legacy pre-key-ledger states for Collection A: one lost row or all +rows lost. Both states retain the old resume marker. On reopen, Collection A +must request a fresh Electric snapshot from offset `-1` and recover the exact +PostgreSQL rows. Collection B must retain its rows throughout the history. + +The independent expected result comes from the test's PostgreSQL row list. +The test compares complete sorted public and durable rows at each settled +checkpoint. It also compares schema versions, leader state, reset epochs, and +the first recovery request. An unchanged reset epoch detects a destructive +schema reset during the checked follower-open and takeover history. This test +does not sample transient public states between those checkpoints. + +An ordinary source commit has a separate order law: it enters the shared +regular scheduler before it requests the database-wide writer lock. The +controlled Browser test sends a real wrapped Collection commit through the +coordinator. It records scheduler entry, writer-lock request, persistence +apply, and scheduler exit in that order. The live OPFS histories then check +progress with the real scheduler, Web Locks, worker, and Electric provider. + +## RED and GREEN evidence + +During initial live-host development, the recovery history timed out before +the lock-order repair. One writer lock was held while another request waited. +That observation is a timeout, not a row-assertion failure. The development +run did not retain an immutable pre-fix oracle commit, so this record does not +use it as the only checker calibration. + +At the reviewed commit, the controlled source-commit test passed with the +exact four-event order above. A temporary mutant restored the old call-site +bypass by passing `this.persistence.adapter` into +`applyBufferedSyncTransactionUnsafe`. The test then failed at the intended +order assertion. It observed `writer-lock-request, apply` without scheduler +entry or exit. The mutant was removed before verification. + +The full Browser coordinator suite passed 154 tests. The live Chromium suite +passed both recovery histories and two failure-fidelity tests. Each recovery +history also passed four repeat runs in addition to focused runs. The package +TypeScript check, changed-file ESLint, Prettier, and `git diff --check` passed. +ESLint reported only warnings on unchanged lines in the coordinator suite. + +## ORC-001 through ORC-011 + +| Requirement | Outcome | +| --- | --- | +| ORC-001: contract authority and limits | Pass for the bounded #1589 browser history. The reporter's rows-plus-resume failure supplies the example. Existing persistence and Electric contracts supply the row, schema, and cursor laws. The test does not establish React rendering, Firefox/Zen, React Native, arbitrary large datasets, or exclusive OPFS ownership. | +| ORC-002: independent judgment | Pass. The expected rows come from the PostgreSQL fixture data, not the SQLite snapshot or Electric response. The expected recovery offset is the initial Electric snapshot offset, not a persisted cursor read back from production. | +| ORC-003: distinguishable responsibilities | Pass. The spec states the row and resume laws, two fixed loss histories, real browser driver, and settled public/durable checks. The companion browser fixture supplies OPFS and Electric. The controlled coordinator test owns the order assertion. | +| ORC-004: generated-history grammar controls | Not triggered. The suite enumerates two fixed loss cases and makes no generated-history coverage claim. The cases distinguish partial from total baseline loss. Neither case permits a missing resume marker. | +| ORC-005: production path and observation | Pass. Two real Chromium pages use OPFS, Web Locks, BroadcastChannel, PostgreSQL, and the installed Electric SDK. The test observes exact public and durable rows, ready status, schema versions, reset epochs, leadership transfer, and recovery request offset. The controlled test reaches the wrapped source-commit call site and the writer-lock request. | +| ORC-006: checker calibration | Pass for the order law. The old-bypass mutant reached persistence apply and failed at the exact event-order assertion. The earlier live pre-fix run timed out and is classified separately as a progress failure. The host test does not prove every row assertion rejects a separate mutant. | +| ORC-007: fixed/random campaigns and replay | Not triggered. These are fixed host histories, not an important generated property. The table names and database IDs vary only to isolate test resources. | +| ORC-008: stateful-model minimality | Not triggered. The test adds no stateful reference model. The PostgreSQL row list is fixed expected data. | +| ORC-009: vocabulary mapping | Pass. Source rows, public rows, durable rows, Collection readiness, leadership, resume marker, and writer lock keep their production meanings. The test-only event list records call order; it does not model scheduler state. | +| ORC-010: failure fidelity and cleanup | Pass for the tested failure modes. An injected diagnostic failure leaves the navigation error as the cause. Injected cleanup and fallback-close errors remain separate. The harness attempts page cleanup, drops both PostgreSQL tables, and closes the client. An `AggregateError` preserves a primary test failure as its cause when cleanup also fails. A process crash remains outside this guarantee. | +| ORC-011: independent second formulation | Not triggered. No plausible shared semantic fault between the fixed PostgreSQL expectation and the SQLite/Electric result was identified. Mocked Electric recovery tests provide a different boundary, not a substitute for this live host path. | + +ORC-012 is satisfied by this versioned record and the exact reviewed commit. +The review supports only the named two-tab Chromium history and controlled +order law. It does not close every #1589 environment or the separate +exclusive-handle topology question. The issue should remain open until the PR +lands and maintainers decide whether the remaining host limits need follow-up. diff --git a/docs/contributing/oracle-reviews/issue-1700-index-suggestions.md b/docs/contributing/oracle-reviews/issue-1700-index-suggestions.md new file mode 100644 index 0000000000..3f6bbb6064 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-1700-index-suggestions.md @@ -0,0 +1,56 @@ +# Collection-size index suggestions: review record + +The executable implementation reviewed here is commit `64c79bd8` (parent +`ae2eb3fbf3a314a7f043f0d963fd336d8c1f1ec8`). The raw report is +[issue #1700](https://github.com/TanStack/db/issues/1700), including its +non-blocking performance aside; it had no comments at evaluation time. + +## Law, reach, and observations + +`IndexDevModeConfig.collectionSizeThreshold` documents size-based index +suggestions, and the emitted message asks a developer to add a field index. +For a queried source Collection over the threshold, advice is useful when the +field is not indexed and the Collection does not create an index itself. It is +redundant when `autoIndex: 'eager'` creates the matching index. + +The bounded oracle in `packages/db/tests/index-suggestion-oracle.test.ts` +uses a public filtered live-query Collection at initial readiness. Its model +is the stated size/mode/matching-index rule. The history grammar crosses +explicit manual mode, default manual mode, eager mode, 1,000 versus 1,101 +rows, no/matching/unrelated index, and advice enabled/disabled. It checks the +actual suggestion metadata, exact public result row, and index count. The +unrelated-index control rejects a classifier that suppresses advice merely +because some index exists. + +On unchanged `ae2eb3fb`, the final oracle was **RED** in four of seven cells: +explicit manual mode, default manual mode, and manual mode with an unrelated +index each recorded zero suggestions where one was expected; eager mode +recorded one where zero was expected. The threshold, matching-index, and +disabled-advice controls passed. On `64c79bd8`, all seven cells passed; the 17 +existing auto-index tests passed, +and package Vitest reported zero type errors. The change keeps the production +file at 5 added versus 6 removed lines. The report's proposed single-guard +inversion was insufficient because `ensureIndexForExpression` had its own +manual-mode early return; the fix changes both gates. + +## Oracle guide audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Public dev-mode configuration and suggestion message supply the policy; this owner limits itself to initial filtered-query readiness and size advice. | +| ORC-002 | Expected advice follows the declarative size/mode/index rule, without importing production's guard or index-search helper. | +| ORC-003 | Opening prose states the contract, model, grammar, driver, and checkpoint; `expectedSuggestions` and the public query assertions keep them visible. | +| ORC-004 | Not triggered: the seven-cell matrix is bounded enumeration, not a generated-history coverage claim. | +| ORC-005 | `createLiveQueryCollection` executes the reported WHERE path; the test checks public rows and captured `IndexSuggestion` metadata after readiness. | +| ORC-006 | The actual pre-fix implementation fails at both intended advice assertions, demonstrating checker sensitivity. | +| ORC-007 | Not triggered: no important generated property was added. | +| ORC-008 | Not triggered: the model has no evolving reference state. | +| ORC-009 | Not triggered: the model uses the Collection, row, index, and readiness concepts directly, with no combined lifecycle abstraction. | +| ORC-010 | The fixed matrix does not shrink or capture asynchronous failures. Each fixture cleans up both Collections and reports assertion plus cleanup failures together, preserving the assertion as the cause. | +| ORC-011 | No named plausible fault shared by production and this independent declarative advice rule requires a second formulation. | +| ORC-012 | This record names the reviewed commit, RED/GREEN observations, controls, and limits. The bounded closure is manual/default/eager × initial filtered-query setup × public suggestion/index/result observations. It does not claim all query shapes, repeated warning volume, post-GC histories, slow-query timing, index work, or production-build execution. | + +The coverage map names the owner and remaining limits. The report's post-GC +repetition was not separately executed; the fixed eager branch cannot emit a +size suggestion regardless of whether cleanup cleared an index first. The +report's timing figures remain background context, not a verified work law. diff --git a/docs/contributing/oracle-reviews/issue-1752-cold-snapshot-batching.md b/docs/contributing/oracle-reviews/issue-1752-cold-snapshot-batching.md new file mode 100644 index 0000000000..57f9699d93 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-1752-cold-snapshot-batching.md @@ -0,0 +1,93 @@ +# Cold SQLite replacement batching: value-and-work review + +- Reviewed executable commit: `5a333993bc6edbb7c28d3ee4596f385522f2bd48`. +- Owner: `packages/db-sqlite-persistence-core/tests/sqlite-resume-snapshot.test.ts`. +- Report: [issue #1752](https://github.com/TanStack/db/issues/1752), a measured cold-start persistence storm on a shared browser OPFS driver. + +## Contract and evidence + +The bounded law concerns `SQLiteCorePersistenceAdapter.applyCommittedTx` for +one full replacement with distinct, non-delete keys. At its fulfilled return, +the rows, row metadata, collection resume metadata, key-set evidence, and +applied position must agree with the input transaction. The replacement must +not make database calls proportional to its row count. A failure in a later +batch must leave the prior generation intact. Duplicate-key and delete +histories retain their ordered sequential semantics. + +The fixed driver history seeds an older row and resume marker, then applies +205 distinct `update` mutations under `truncate: true`. Independent expected +values come from the input rows and a parity rule for direct versus later row +metadata. The test compares the complete durable snapshot, exact expected-key +membership, and applied transaction records. It counts query/run calls only +during `applyCommittedTx` and requires at most 40. The previous implementation +failed this assertion at **1,033 calls**. The new path uses at most 100 rows per +SQL statement, below SQLite's older 999-binding limit. + +A separate fault-injection history rejects the second multi-row insert after +the first chunk applied inside the transaction. It observes two reached bulk +row inserts, then compares the complete prior snapshot and schema projection +after rollback. On the previous implementation this test failed because no +bulk insert existed and the transaction fulfilled. Fixed duplicate-key and +delete histories separately check the sequential fallback, including merged +row value, key evidence, and a delete tombstone. + +The temporary in-memory 14,000-row probe, removed after measurement, counted +**70,015** database calls before the change and **295** afterward on the same +path, including its final snapshot read. It timed approximately 268 ms versus +42 ms on `node:sqlite`. Neither time is an OPFS or browser measurement. The +package suite passed **615 tests** with two TODOs; the build, changed-file +ESLint, Prettier, and diff checks passed. Vitest and Vite emitted non-fatal +warnings about missing Expo example tsconfigs in the filtered install. + +## ORC-001 through ORC-011 + +| Requirement | Outcome | +| --- | --- | +| ORC-001: authority and limits | Pass for the fixed cold-replacement law. Atomic persisted rows and stream position are established by the persistence contract; issue #1752 supplies the work concern. The law is limited to one transaction with unique non-delete keys. It does not promise browser latency or resolve every queueing cause in the issue. | +| ORC-002: independent judgment | Pass. Expected rows and metadata derive from fixed input data and a parity rule, not the adapter's batching or metadata-folding code. Exact expected-key membership derives from input keys. | +| ORC-003: distinguishable responsibilities | Pass. The oracle header and adjacent tests state the contract and limits, fixed transaction histories, independent expected snapshot, production `applyCommittedTx` driver, and comparisons at fulfilled-return or rejected-rollback checkpoints. | +| ORC-004: generated-history grammar controls | Not triggered. These are fixed histories and a deterministic width, with no generated-history coverage claim. | +| ORC-005: production path and observation | Pass. The test invokes the real core adapter over `node:sqlite`, records its driver calls during the write, and compares durable rows, metadata, evidence, and position after return. The injected failure reaches the second bulk row statement before rollback comparison. | +| ORC-006: checker calibration | Pass for the work and rollback claims. The old sequential design failed the work bound at 1,033 calls. It also failed the later-batch test at the expected rejection because no second bulk statement existed. These were assertion failures, not setup failures or timeouts. The exact-value checks would reject missing or mis-folded row metadata; no separate production value mutant was run. | +| ORC-007: fixed/random campaigns and replay | Not triggered. The new cases are focused fixed tests, not an important generated property. The existing generated histories in this owner remain unchanged. | +| ORC-008: stateful-model minimality | Not triggered. No reference-model state was introduced, combined, or removed. | +| ORC-009: vocabulary mapping | Pass. A full replacement is a persisted transaction with `truncate: true`; its durable snapshot is distinct from a Collection public snapshot. The call counter is a test-only work measure, not a product state. | +| ORC-010: failure fidelity and cleanup | Pass for exercised paths. The existing close helper preserves the primary failure and separate cleanup diagnostics. The fault-injection transaction rolls back before the complete prior snapshot is compared. | +| ORC-011: second formulation | Not triggered. No plausible shared semantic classifier between the input-derived expected values and the adapter result has been identified. Raw expected-key and applied-transaction reads supplement the adapter snapshot. | + +This record supplies the ORC-012 audit for the executable commit above. An +actual Chromium/OPFS run with the reported multi-collection workload is still +needed before claiming a user-visible latency improvement or closing #1752. + +## PR #1916 scan-order review follow-up + +- CodeRabbit review `5343977574` targeted `d850cfc27795be1b0cdcf7dfc1da8c5d0409a6b4` and posted one inline finding, `4126510406`. +- Reviewed follow-up executable commit: `ae7048d8bdbd26c707fc626bf865dd4f073f866e`. + +The review correctly found that the 205-row value assertion depended on scan +order even though `loadResumeSnapshot` issues no `ORDER BY` for this request. +A temporary reversal of the correct observed rows failed the original +assertion at its value checkpoint, with all rows and metadata unchanged. +The follow-up sorts exact observed and expected arrays by key. It retains the +alternate-scan-order challenge in the executable test; the same reversed +rows then passed. The complete snapshot test file passed 36 tests with two +TODOs and clean type checks. This is a test-integrity fix, not a change to +SQLite row ordering or product behavior. + +The task-local loss audit accounts for the one inline finding, an optional +CodeRabbit CLI suggestion, and two advisory PR-summary warnings. The PR body +now follows the repository template, a patch changeset records the published +core change, and the touched test driver's purpose has a JSDoc. The optional +extra CLI review remains deferred to the normal PR review process. No +reviewed item remains unclassified; later CodeRabbit reviews at new heads +require separate evaluation. + +## Cloudflare bound-parameter follow-up + +- Reviewed executable commit: `33907be70dbb987fcca4e502e5b0b4d7ab3f0a4e`. +- Cloudflare's [Durable Objects limits](https://developers.cloudflare.com/durable-objects/platform/limits/) permit at most 100 bound parameters per SQL query. A full replacement inserts four bound values per row and two per expected key. The prior 100-row chunk could therefore exceed the host cap at 26 rows. +- The Cloudflare driver now declares that fixed host limit. The core adapter captures the driver's capability before it wraps the driver for scheduling, and uses at most 25 replacement rows per statement for Cloudflare. Drivers without a lower declared limit retain the 100-row default; the core 205-row work test observes a 400-binding row statement to pin that distinction. This is a driver capability, not a user option. +- Contract × history × path × observation: for unique-key, non-delete `truncate: true` replacements of 25, 26, and 205 rows, `applyCommittedTx` through the Cloudflare driver must fulfill with every input key durably present and a consistent expected-key set. At each `sql.exec` call, the binding count must be at most 100. The fixed test uses a Node SQLite storage seam that rejects calls above Cloudflare's documented cap; it does not execute inside Workers. +- RED on the prior PR head `f20e84b2971d4e018b6c2c45f3b11af92ba47d00`: the 25-row case passed; 26 and 205 rows rejected at the host-cap seam. This was an assertion/path failure, not a test setup failure. GREEN on `33907be7`: all three cases passed. The old 100-row implementation is the hostile control for the boundary assertion; a global 25-row change would fail the generic driver's 400-binding assertion. +- Verification: the complete core suite passed 340 tests with one TODO; the focused Cloudflare suite passed three tests. Both packages built, and changed-file ESLint, Prettier, and Git whitespace checks passed. The local full Cloudflare suite could not collect its three older files because the filtered checkout lacks the `better-sqlite3` dev dependency. Nonfatal Expo example tsconfig warnings also appeared. CI remains the full host-package check. +- The test owns the documented Cloudflare binding boundary, not all possible host restrictions, native Workers execution, or Chromium/OPFS latency. The issue was closed by maintainer decision without making a latency measurement a closure prerequisite. diff --git a/docs/contributing/oracle-reviews/issue-1754-live-electric-hydration-straddle.md b/docs/contributing/oracle-reviews/issue-1754-live-electric-hydration-straddle.md new file mode 100644 index 0000000000..15f1eb321f --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-1754-live-electric-hydration-straddle.md @@ -0,0 +1,96 @@ +# Issue #1754 live Electric hydration-straddle oracle review + +- Reviewed oracle commit: `1ad6fcef9c705d2d3edb4d8003ba116e8a878ea0`. +- Comparison main: `f09868ffba3ea401a9b182cc90282435bf832e3c`. +- Executable owner: `packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle.opfs.spec.ts`. +- Host: Chromium with PostgreSQL, Electric, Web Locks, and the OPFS worker. + +## Contract and fixed history + +Issue #1754 reports a source transaction that begins during persisted hydration +and commits after the hydration scope closes. A late commit must apply and settle. +It must not enter a hydration buffer whose flush already ran. The existing core +persistence tests cover this rule with a controlled adapter. This test adds a +live Electric and OPFS host witness. + +The fixed history starts with one PostgreSQL row and loads it through an +on-demand Electric Collection. The test then starts a second subset demand for +an absent key. It holds that demand after the OPFS subset read, while the +persisted hydration scope remains active. A PostgreSQL update starts a real +Electric source transaction during the hold. The fixture parks its row-bearing +commit. The test releases hydration, observes the scope exit and the unchanged +old row, then releases the commit. The expected new row comes from the +PostgreSQL update. The test compares complete public and durable rows after the +commit, after follower takeover, and after reopen. + +The empty subset demand cannot deliver the updated key through its own remote +snapshot. The source transaction is the only tested path for that update. The +reach flags distinguish fixture setup from the reported straddle: the source +began during the hold, its row commit was parked, and the hydration scope exited +before the commit was released. + +## RED and GREEN evidence + +The final test passed four focused repeats. The full live Electric suite passed +twice, with 10 of 10 tests green. TypeScript, changed-file ESLint, Prettier, and +`git diff --check` passed. + +A temporary mutant removed `&& runtime.isHydratingNow()` from the late-commit +condition in `packages/db-sqlite-persistence-core/src/persisted.ts`. The mutant +queued the transaction after hydration closed. The focused browser test reached +all timing flags, including a settled empty subset demand. Its updated-row poll +then timed out with the old row in both public and durable state. This is an +assertion timeout at the intended settled checkpoint, not a setup failure or an +unreached path. The mutant was removed. The focused browser test passed again. + +## ORC-001 through ORC-011 + +| Requirement | Outcome | +| --- | --- | +| ORC-001: authority and limits | Pass for the fixed #1754 live-host history. The issue supplies the reported begin/commit straddle. Existing core persistence tests and Collection sync settlement supply the law. This does not prove Firefox/Zen, eager startup, other mutation shapes, or arbitrary Electric fetch schedules. | +| ORC-002: independent judgment | Pass. PostgreSQL fixture values define the old and new rows. Neither the Electric stream nor the persisted SQLite snapshot computes the expectation. | +| ORC-003: responsibilities | Pass. The spec states the contract, fixed history, public and durable checkpoints, and limits. Its named companion fixture drives the real source, hydration scope, and OPFS adapter. | +| ORC-004: generated grammar | Not triggered. This is one fixed history with unique resource IDs, not a generated-history coverage claim. | +| ORC-005: production path and observation | Pass. The real Electric source calls the persisted wrapper. Reach flags prove begin during hydration and commit after scope exit. Exact public and durable rows are checked before release, after settlement, after leadership transfer, and after reopen. | +| ORC-006: calibration | Pass. The old late-buffer decision left the original row in both observations at the intended checkpoint. The failure was an assertion poll timeout with every required timing flag reached. | +| ORC-007: fixed/random replay | Not triggered. The test has no important generated property. Random IDs isolate resources, but do not generate semantic histories. | +| ORC-008: model minimality | Not triggered. The test adds no stateful reference model. | +| ORC-009: vocabulary | Pass. Hydration scope, source transaction, public rows, durable rows, and leadership use their production meanings. The test-only gates record timing and do not model product state. | +| ORC-010: failure fidelity | Pass for this harness. Cleanup releases both gates, closes the pages and database, and drops the PostgreSQL table. An `AggregateError` keeps the primary failure as its cause when cleanup also fails. Process termination remains outside this guarantee. | +| ORC-011: second formulation | Not triggered. No shared semantic classifier between the fixed PostgreSQL expectation and the Electric/SQLite implementation was identified. Core controlled-adapter tests cover adjacent settlement orders at a different boundary. | + +ORC-012 is satisfied by this versioned record and the exact reviewed oracle +commit. The bounded host witness does not by itself close every environment or +history in #1754. The coverage map assigns adjacent settlement histories to +the core persistence tests and names the remaining live-host limits. + +## Fixture failure-fidelity follow-up + +- Follow-up code commit: `7c149f25efb825d32e4ef65c42a3f1c458640724`. +- The accepted review findings were diagnostic, not production-code defects. + The parked source commit now records the first failure and rethrows its + applied receipt. A terminal probe failure stops the row poll immediately. + Empty error messages receive a nonempty fallback, and page errors remain + available to diagnostics after probe startup. +- Before the change, a controlled probe of the exact fixture interceptor made + `params.commit` throw and reject after release. Both receipts rejected, but + the probe still reported `phase: ready` with no failure cause. After the + change, both receipts still reject while the probe reports `phase: failed` + and the original cause. An empty `Error` retains its name. The same probe + showed a late page error missing from diagnostics before the change and + present afterward. +- Three focused Playwright checks cover empty failure messages, immediate + reporting of a terminal commit failure, and page errors after startup. The + terminal-failure check observes one failed row read and one fresh diagnostic + read, rather than repeated row polling. The live Electric/OPFS straddle + history passed two focused repeats; the full live suite passed 8 of 8 tests. + Package TypeScript, changed-file ESLint, Prettier, and `git diff --check` + passed. +- No exactly-one row-bearing commit assertion was added. A second eligible + commit can bypass the fixture's park, but no legal Electric trace has shown + the exact old/new row assertions passing while the target update bypasses + the intended straddle. A no-write commit is deliberately excluded. The + per-poll resume snapshot remains unchanged: it is one full-row read per + observation, but the one-row fixture has no demonstrated slow-host failure. + These two questions remain limits of this fixed history, not evidence of + product correctness bugs. diff --git a/docs/contributing/oracle-reviews/issue-1796-gc-clock.md b/docs/contributing/oracle-reviews/issue-1796-gc-clock.md new file mode 100644 index 0000000000..7b84fbad92 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-1796-gc-clock.md @@ -0,0 +1,83 @@ +# Issue #1796: Collection GC clock review + +Reviewed implementation: `39d68cd6` (base `ae2eb3fb`). This record covers the +clock correction and oracle added at that commit. The source report is +[issue #1796](https://github.com/TanStack/db/issues/1796); it had no comments +when read on 2026-09-29. The full claim ledger is retained in the task record. + +## Contract and bounded result + +`BaseCollectionConfig.gcTime` promises collection cleanup after the configured +number of milliseconds without active subscribers. `docs/guides/live-queries.md` +documents the live-query variant. Where `performance.now()` is available and +monotonic, moving the wall clock must not move an already scheduled GC deadline. +The Collection's normal idle callback still follows the queue deadline. + +The queue reads the current Performance clock for scheduling, timer selection, +and processing. The previous `Date.now()` path remains the fallback where +Performance is unavailable. That fallback still inherits backward wall-clock +adjustments. Some browsers pause Performance time through OS sleep despite the +intended monotonic-clock contract; this review did not test suspend/resume and +does not claim sleep-inclusive GC timing. + +## RED and GREEN + +With the new oracle on unmodified `ae2eb3fb`, both the fixed-seed and random +campaign failed. The smallest shrink was a zero-delay appointment followed by +a one-millisecond backward wall-clock step: delivery occurred at elapsed time +one instead of zero. The public live-query fixture likewise retained one source +subscriber ten elapsed milliseconds after a one-millisecond GC appointment and +a 1,000-millisecond backward step. The source subscriber count was `1`; the +contractual count was `0`. + +At `39d68cd6`, the same two suites pass. The focused cleanup/restart and +Collection lifecycle suites also pass: 98 tests across four files, no Vitest +type errors. `pnpm --filter @tanstack/db build` succeeds. The production change +is five added lines and three removed lines: net **+2**. Tests and coverage +documentation are counted separately. + +## Oracle guide audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | The elapsed GC law comes from `BaseCollectionConfig.gcTime`; this review limits the clock-step claim to runtimes with monotonic Performance time. | +| ORC-002 | The reference stores elapsed appointments. It never imports the queue's deadline or reads its chosen clock. | +| ORC-003 | The queue oracle names the contract, pure appointment model, action grammar, real queue driver, and callback-delivery comparison. The focused Collection test observes source subscription ownership. | +| ORC-004 | Fixed histories reconstruct backward delay and forward early-delivery risks. Removing wall-step actions loses both. Delays are integers 0–20 ms; steps are integers −1000–1000 ms. Invalid non-positive/non-finite Collection `gcTime` is excluded because lifecycle disables GC for it. | +| ORC-005 | The driver calls the real `CleanupQueue`; the integration witness calls public Collection and live-query APIs and compares `source.subscriberCount` after elapsed GC. | +| ORC-006 | Unmodified `ae2eb3fb` is the hostile wall-clock design and fails at delivery/subscriber observations. Existing cancel, replacement, duplicate, and late controls remain green only when their deliberate faults are rejected. | +| ORC-007 | The same 100-run property executes with fixed seed `20260913` and a seedless random lane. `oraclePropertyOptions` accepts seed and shrink path for direct replay. | +| ORC-008 | No reference state was added: a wall step leaves elapsed appointments unchanged. A later elapsed advance distinguishes due from pending appointments. | +| ORC-009 | Model `now` is elapsed time; `wallClockOffset` is a driver-only correction used to record elapsed callback time from Vitest's fake Date. Neither is a Collection lifecycle state. | +| ORC-010 | The oracle resets its singleton, timers, error spy, and real timers in `finally`. The public fixture explicitly cleans both Collections and restores timers. The recorded RED assertion was observed before cleanup and was not replaced by a cleanup failure. | +| ORC-011 | The public source-subscriber witness is a second formulation of the queue delivery law. It does not copy the queue's appointment model. | +| ORC-012 | This record ties the bounded evidence and limits to implementation commit `39d68cd6`. | + +## Limits and next owner + +The queue oracle covers bounded appointment histories, including cancellation +and replacement around wall steps. Callback-reentrant scheduling remains with +`packages/db/tests/cleanup-queue.property.test.ts` as an omitted grammar axis. +Suspend/resume behavior and runtimes without `performance.now()` also remain +with that owner. The focused live-query test covers one eager source, one live +query, and source subscription release after GC; it does not prove React render +or unmount scheduling. + +## Follow-up: timer clock replacement + +Reviewed PR head: `1c2d53a0`. Verified repair: `d0f3ca00`. The original queue +captured a Vitest fake `Performance` object in its singleton. After the test +restored real timers and installed fresh fake timers, a later GC appointment +read the stale object's time while the current fake timer advanced. The focused +two-installation test failed on `1c2d53a0`: its second callback was called zero +times after its one-millisecond delay. The full reproduction was 12 failures in +three existing test files, including two 10,000-timer loops and ten collections +that remained ready or in error past their GC deadline. + +`d0f3ca00` reads the currently installed Performance object at each queue clock +read. The same focused test, all 12 previously failing tests, and the adjacent +GC/live-query tests passed: 262 tests across ten files. The production diff +against `ae2eb3fb` is now net **+1** line. The replacement witness only swaps +the global clock after the previous appointment has drained. Replacing the +global clock while an appointment remains pending is outside this tested +contract; normal runtimes keep one Performance clock for that interval. diff --git a/docs/contributing/oracle-reviews/issue-1880-ordered-repair.md b/docs/contributing/oracle-reviews/issue-1880-ordered-repair.md new file mode 100644 index 0000000000..1ed06859ae --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-1880-ordered-repair.md @@ -0,0 +1,108 @@ +# Issue #1880 ordered-repair oracle review + +## Reviewed state + +- Base commit: `4c5a8de61843d6964a2580aded8a2d027b78e135`. +- Reviewed change: the Git tree containing this record and the accompanying + issue #1880 ordered-repair production, test, and architecture changes. +- Primary executable owner: + `packages/db/tests/query/pagination-oracle.property.test.ts`. +- Adjacent owners: + `ordered-work-oracle.property.test.ts`, + `ordered-source-loader-state.test.ts`, `ordered-default-work.test.ts`, and + `ordered-demand-retirement.test.ts`. +- Query-backed application owner: + `packages/query-db-collection/tests/load-subset-lifecycle-oracle.test.ts`. + +This state description is intentional: a file cannot contain the hash of a +commit that already contains that file. The eventual commit or pull request +identifies the exact immutable tree; this record fixes its comparison base. + +## Claim and limits + +The reviewed oracle checks that a settled, expressible ordered window repairs a +visible delete or first-provider-term change with an inherited-predicate, +normalized-order, zero-transport-offset prefix followed by the required tie and +refill requests. It also checks row recomputation, publication, failure upgrade, +and acquisition ownership around that path. + +It also checks two termination boundaries that the original grammar missed. An +underfilled indexed repair must retire superseded finite leases when its next +page repeats the last count and boundary. An unindexed repair started by a +window operation must do the same when its prefix count repeats and no more +data is needed. A Query-backed refetch must remain pending while +`deferDataRefresh` prevents its result from being applied, for both existing +and newly observed cached demands. + +The follow-up review also extends the boundary around that repair. A mutation +that supersedes an in-flight repair must start its replacement before the old +chain issues more tie or refill work. Effect callbacks must survive an obsolete +repair abort during truncate replay, suppress asynchronous initial ordered rows +under `skipInitial`, and preserve real updates that contain object-valued Sets. +Query-backed refetches must wait for post-write authority, hide the internal +`refetch` control from query-function metadata, and reject the released caller. +An authoritative refetch still replaces an in-flight shared Query attempt; that +is required to revalidate the demand rather than reuse stale work. + +The authority is the Ordered loading and Atomic window publication contract in +`packages/db/src/query/live/ARCHITECTURE.md`, plus the applied-settlement and +acquisition-release contract in `docs/guides/collection-options-creator.md`. +The evidence is bounded to the test grammar, in-memory on-demand adapter seam, +and real QueryClient test boundary. It does not certify backend query plans, +network volume, latency, complete SQL predicate semantics, or every +`requiresFullSource` compiler branch. + +## ORC-001 through ORC-011 + +| Requirement | Outcome | Evidence | +| --- | --- | --- | +| ORC-001: contract authority and limits | Pass | The pagination-oracle header names the architecture authority and its limits. The collection-options guide defines applied settlement and acquisition release. This record narrows the repair claim to the tested grammar, adapter seam, and QueryClient boundary. | +| ORC-002: independent judgment | Pass | `referenceWindowRows` filters/sorts/slices plain arrays independently of the ordered loader. Exact request checks use literal normalized paths, directions, field absence, and reference predicate evaluation rather than the production request classifier. | +| ORC-003: distinguishable responsibilities | Pass | The file keeps scenario arbitraries, array recomputation, the real Collection driver, request/publication recorders, and refinement helpers separate. `PendingMutationResult` carries the work observation from the driver to both generated campaigns. | +| ORC-004: generated-history grammar controls | Pass | The bounded grammar crosses insert/update/delete, resolve/reject, before/after settlement, ranks, directions, and limits. Named products reconstruct every mutation/outcome/timing cell; the `[0,1,1,2]` case pins tie plus refill, and missing-row deletion is rejected. Existing fault/ablation cases show the contribution of publication, window, settlement, callback, delete-value, and final-value observations. | +| ORC-005: production path and observation | Pass | The pagination driver uses `createLiveQueryCollection` over a real on-demand Collection. It compares visible rows and callback cuts, records the complete post-mutation adapter request sequence, and observes acquisition releases before cleanup. The Query DB driver uses a real QueryClient and observes transport calls, promise settlement, and applied Collection rows for existing and cached demands. | +| ORC-006: checker calibration | Pass | `rejects a malformed bounded-repair request trace` supplies the old checker's plausible survivor: missing predicate, wrong same-length order terms, `offset: 99`, and an extra request. The exact trace checker rejects it. The preserved RED runs show that the former implementation produced correct final rows while releasing zero superseded leases, and settled both deferred refetch forms before application. Existing captured-history faults calibrate value/publication checks. | +| ORC-007: fixed/random campaigns and replay | Pass | The fixed-seed and random/replayed `pagination.pending-mutation` properties invoke the same arbitrary, `runPendingMutationScenario`, `PendingMutationResult` recorder, request-work checker, and run budget. `oracleRandomParameters` accepts the configured seed and shrink path for direct replay. | +| ORC-008: stateful-model minimality | Not applicable | The repair adds a request-trace result, not new reference-model state, and does not merge or remove model states. | +| ORC-009: vocabulary mapping | Pass | In this record, a repair prefix is an ordered request without a cursor or transport offset; a tie is a predicate-only request for the first provider term; a refill is an ordered cursor request. Full-source recovery omits `orderBy`, `limit`, `cursor`, and `offset`, but still inherits the subscription `where` predicate. The work checker distinguishes a filtered full-source predicate from a tie by evaluating it over distant ranks. `mutationRepairRequests` means adapter requests started after the modeled source mutation, not rows delivered locally. | +| ORC-010: failure fidelity and cleanup | Pass | `PendingMutationTraceAssertionError` retains the violated checkpoint and delivered-row trace. `withHistoryCleanup` preserves the primary failure while settling subscriptions, outstanding requests, the live query, and source cleanup. Partial-write rejection and repeated-continuation termination have direct-loader ownership witnesses. The Query DB cases release their barrier and settle refetch before cleanup. | +| ORC-011: independent second formulation | Pass | The public pagination history and direct `OrderedSourceLoader` state test distinguish the shared-fault risk that correct final rows could hide a malformed or over-broad provider request. Ordered-work parity separately compares Collection and Effect publication behavior. | + +ORC-012 is satisfied by this versioned, base-identified closeout record and its +link from `docs/contributing/oracle-coverage.md`. + +## Calibration and known omissions + +The hostile trace proves that checking only order-term count, limit, and missing +cursor would accept a semantically wrong request. The capped-provider case +proves the deterministic repair chain `prefix -> tie(rank 1) -> cursor refill -> +tie(rank 2)`. A semantic predicate check distinguishes a repair tie from a +filtered full-source fallback. The partial-write integration witness proves the +next request is exactly full-source; the direct-loader ownership test proves +that lease releases occur in order `[3, 0, 1, 2]` after bounded repair fails. + +The underfilled integration witness proves that a three-row source under a +ten-row limit releases all three superseded finite acquisitions before cleanup. +Direct loader witnesses separately cover repeated indexed-page and unindexed- +prefix continuations, then start another repair to prove the loader is usable. +The Query DB witness holds result application after refetch transport succeeds; +both an existing observer and a cached observer must remain pending. Additional +Query DB witnesses cover post-write authority, metadata isolation, and final- +owner release. Ordered-work witnesses cover repair replacement before stale +continuation, truncate replay handoff for Effects, and asynchronous +`skipInitial`. The Effect and utility owners pin object-valued Set updates and +Set equality independently. + +The complete `requiresFullSource` classifier is documented in architecture but +is not enumerated here branch by branch. Adapter-specific transfer and query +execution remain adapter conformance concerns. Effect failure publication is +covered by source-error disposal plus direct failure ownership evidence; this +review adds the previously missing held-success callback witness. + +## Verification + +Closeout requires the focused changed owners, both pending-mutation campaigns, +the complete `@tanstack/db` and `@tanstack/query-db-collection` test suites, both +package builds, changed-file lint and format checks, and `git diff --check`. +Exact final counts belong in the pull request or issue receipt because they +describe the immutable execution, not the oracle contract. diff --git a/docs/contributing/oracle-reviews/issue-1882-custom-local-collation.md b/docs/contributing/oracle-reviews/issue-1882-custom-local-collation.md new file mode 100644 index 0000000000..162580cabf --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-1882-custom-local-collation.md @@ -0,0 +1,82 @@ +# Issue #1882 custom local collation oracle review + +## Reviewed state + +- Base commit: `f473a362`. +- Reviewed change: the Git tree containing this record and the accompanying + issue #1882 custom local collation production, test, and architecture changes. +- Primary executable owner: + `packages/db/tests/query/pagination-oracle.property.test.ts`. +- Focused supporting owners: `comparison.property.test.ts`, + `collection-indexes.test.ts`, `cursor.test.ts`, + `live-query-options.test.ts`, `query/builder/order-by.test.ts`, + `query/expression-helpers.test.ts`, and + `query/ir-stable-identity.test.ts`. + +This state description is intentional: a file cannot contain the hash of the +commit that already contains it. The eventual commit or pull request identifies +the exact immutable tree; this record fixes its comparison base. + +## Claim and limits + +The reviewed lane checks that a custom local string comparator is preserved by +collection defaults and per-order configuration, applies only to string pairs, +and orders both scan and auto-index paths in ascending and descending direction. +Comparator-equal rows retain ascending public-key order. Runtime QueryIR, +load-subset demand, live-query config, and local index compatibility use the +exact comparator function reference. + +For an on-demand source, any custom order term makes the first acquisition one +filtered full-source request for both bounded and unbounded queries. The request +retains the query predicate and omits `orderBy`, `limit`, `offset`, and cursor. +Hostile provider order cannot become a local ordering authority. Ordinary range +predicates keep their existing lexical meaning, do not call the custom +comparator, and cannot use custom string index range traversal. + +The authority is the Ordered requests, continuation, and recovery contract in +`packages/db/src/query/live/ARCHITECTURE.md` and the public +`StringCollationConfig` API. Evidence is bounded to the declared C-01 through +C-09 rows, in-memory on-demand adapter seam, and focused identity/index tests. +It does not establish a stable cross-runtime collation identifier, provider +capability negotiation, custom cursor pagination, comparator-aware ranges, +backend SQL fidelity, function serialization, or comparator exception and +non-finite-result policy. + +## ORC-001 through ORC-011 + +| Requirement | Outcome | Evidence | +| --- | --- | --- | +| ORC-001: contract authority and limits | Pass | The pagination-oracle header and this record name the local custom-order law, architecture authority, exact request shape, and deferred remote/range behavior. | +| ORC-002: independent judgment | Pass | C-01 through C-09 use a declared rank relation and literal expected public-key sequences. They do not use production `makeComparator`, index matching, cursor classification, or loader request classification to compute the expected rows or request shape. | +| ORC-003: distinguishable responsibilities | Pass | The opening oracle prose states the contract and limits; `customCollationRanks` is the reference relation; the nine named bounded/unbounded, single/multi-source, and output-default cells are the fixed input grammar; the Collection/live-query scenarios are the production driver; and their row, path, and exact request assertions are the refinement check. | +| ORC-004: generated-history grammar controls | Not applicable | The new lane is a fixed bounded semantic/path matrix, not a generated-history property. Existing generated pagination campaigns and their grammar are unchanged. | +| ORC-005: production path and observation | Pass | The driver uses `createCollection` and `createLiveQueryCollection`, observes exact ordered public rows after `preload()`, proves scan versus eager auto-index reach, and records every provider request including predicate and omitted window fields. | +| ORC-006: checker calibration | Pass | The hostile provider emits filtered rows in a different physical and tie order. The original RED run reported 11 failures and 487 passes when the five bounded cells exposed legacy locale order (`[3,2,1,4]` ascending and `[4,1,2,3]` descending). A later boundary challenge added C-06 and C-07: both produced the correct local rows but failed because the provider request leaked `orderBy`, preserving an exact 2-failure/5-pass signal. C-08 reproduced the same leak when a later ordered source inherited the leading source's custom default. C-09 then produced the correct local rows but failed its exact request-shape assertion in a targeted run (1 failed, 249 skipped): expected no provider `orderBy`, but received normalized `label` order after the output Collection selected lexical collation. All nine cells pass after provider hint admission was separated from the output Collection's downstream default. | +| ORC-007: fixed/random campaigns and replay | Not applicable | ORC-007 is triggered only by an important generated property. This change adds fixed cells and leaves the existing pagination generated campaigns, seeds, replay interface, recorder, and budgets unchanged. | +| ORC-008: stateful-model minimality | Not applicable | The fixed relation adds no model state and does not combine, split, or remove states in the existing pagination history model. | +| ORC-009: vocabulary mapping | Pass | A custom comparator is the caller's local string/string relation. A filtered full-source acquisition retains `where` but omits provider order/window fields. Comparator identity means exact runtime function reference; it is not a remote collation identity. These terms match the architecture and production boundaries. | +| ORC-010: failure fidelity and cleanup | Pass | The driver asserts rows and request shape before cleanup, then always cleans the live query and source. Existing `cleanupAll` keeps the primary Vitest assertion visible while releasing both resources. The RED receipts preserved both wrong ordered vectors at the public-row checkpoint and leaked provider `orderBy` at the request-shape checkpoint. | +| ORC-011: independent second formulation | Pass | Literal C-cell vectors judge the end-to-end Collection path, while focused comparator, parser, identity, cursor, and index tests independently pin callback dispatch and boundary preservation. Scan and auto-index cells distinguish a false agreement confined to one local ordering path. | + +ORC-012 is satisfied by this versioned, base-identified closeout record and its +link from `docs/contributing/oracle-coverage.md`. + +## Calibration and omissions + +The provider order puts `+3 power` before `3` even though the custom relation +declares them equal, and places `pillow fort` before `pillowfort` despite the +declared custom order. The filtered-out row shares a comparator position with a +kept row. These controls make predicate loss, provider-order trust, comparator +loss, descending tie reversal, duplicate/omitted acquisition, and scan/index +disagreement observable. + +Distinct but extensionally equivalent comparator functions are deliberately +different query, demand, config, and index identities. The implementation does +not serialize a function identity or claim cross-runtime reuse. A mutable +closure can invalidate an already-built index, so the API contract requires a +deterministic comparator whose behavior remains immutable for its lifetime. + +The final focused GREEN run completed 502 tests across eight files with no type +errors. Exact final full-suite, build, lint, and format receipts belong in the +pull request because they describe an immutable execution rather than this +oracle contract. diff --git a/docs/contributing/oracle-reviews/issue-1883-opfs-open-deadline.md b/docs/contributing/oracle-reviews/issue-1883-opfs-open-deadline.md new file mode 100644 index 0000000000..4e58aef4a3 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-1883-opfs-open-deadline.md @@ -0,0 +1,101 @@ +# Issue #1883 OPFS open-deadline review + +- Reviewed executable commit: `ad6815812e42aa093c3dd7397a948061988e2a7a`. +- Base: `6e151b0e57d63e2535cdf8e02518690d453214bc`. +- Owners: `packages/browser-db-sqlite-persistence/tests/opfs-page-lifecycle-oracle.test.ts` and `packages/browser-db-sqlite-persistence/e2e/open-timeout.opfs.spec.ts`. + +## Contract and evidence + +An OPFS open must not wait indefinitely when another tab cannot release the +database. Opening now has a 30-second default deadline, an override with `0` +meaning no deadline, and an optional caller `AbortSignal`. A rejected pending +open terminates its worker. The signal and timer cease to affect the connection +after opening settles. + +The fixed unit histories drive the public open function with a silent worker. +They check default and overridden deadlines, caller abort, pre-abort, disabled +deadline, invalid durations, one settlement, listener removal, and a late +worker response. Before the production change, deadline assertions failed and +the signal API was absent. With the change, the package's full unit and type +suite passed: 18 files, 380 runtime tests, and no type errors. + +The Chromium fixture holds the exact VFS Web Lock in one tab. The contender's +public open queues on that lock, then rejects with `TimeoutError`. Its queued +lock request disappears before the holder releases the lock. A new open then +reads successfully. All four configured Chromium OPFS tests passed. A temporary +mutant that omitted worker disposal on timeout left the contender's queued +lock request present; the test failed at its `toBe(false)` assertion. The +mutant was removed, and the test passed again. Changed-file ESLint, Prettier, +TypeScript, and `git diff --check` passed. + +This is a controlled lock-holder history, not a reproduction of the reported +Chrome 152 CDP freeze. The fixture ran on local Chrome 153. It does not +establish behavior in other browsers, for a frozen contender tab, or for every +OPFSCoopSyncVFS failure. The issue's separate VFS-error-naming request remains +outside this change. + +## ORC-001 through ORC-012 + +| Requirement | Outcome | +| --- | --- | +| ORC-001: authority and limits | Pass. Issue #1883 requests a bounded open and late-open cleanup. The README states the new public option contract. The limits above and the coverage map bound the claim. | +| ORC-002: independent judgment | Pass. The unit expectations come from the timeout and abort contract. The browser check reads Chromium's Web Lock inventory, not the implementation's request map. | +| ORC-003: responsibilities | Pass. The existing lifecycle oracle states the law and fixed histories beside its controlled driver and exact settlement checks. The browser spec states its lock history, real entry point, checkpoints, and limits. | +| ORC-004: generated grammar | Not triggered by this change. The new histories are fixed; the pre-existing generated pagehide grammar is unchanged. | +| ORC-005: path and observation | Pass. Both owners invoke `openBrowserWASQLiteOPFSDatabase`. Unit checks observe exact errors, settlement count, worker termination, and listener ownership. The browser checks the queued native lock before and after rejection, then a successful reopen. | +| ORC-006: calibration | Pass. Omitting timeout disposal left the real browser's queued lock present and failed the intended assertion. The timeout unit histories were red before the production change. | +| ORC-007: fixed/random replay | Not triggered by this change. No new important generated property was introduced. The existing lifecycle campaigns retain their replay controls. | +| ORC-008: model minimality | Not triggered. The change adds no reference-model state. | +| ORC-009: vocabulary | Pass. Open, settlement, abort, worker disposal, and Web Lock refer to their production or browser boundaries; the fixture's state is only an observation holder. | +| ORC-010: failure fidelity | Pass for the bounded fixture. It releases the held lock, closes pages, and retains a primary failure as the cause of an `AggregateError` when cleanup also fails. The unit harness releases held responses and restores globals. | +| ORC-011: second formulation | Pass. The fake silent worker checks the API and resource ownership. The Chromium fixture separately checks the native queued-lock effect that the fake cannot observe. | +| ORC-012: review evidence | This record identifies the exact reviewed executable commit, calibration result, applicable requirements, non-applicable triggers, and remaining limits. | + +The bounded repair claim is: default or overridden deadline × silent-init and +held-lock histories × the public browser OPFS open path × rejected open, +worker disposal, and absence of a later queued lock. A signal-abort history is +also covered by the controlled worker. The coverage map owns the remaining +frozen-tab and browser-matrix witness gaps. + +## Follow-up review at `b1c615868974273e1b2dfb9bd5e5e0e45b01fc9d` + +The external review of PR #1936 raised an untested synchronous-abort cut and a +possible false failure in the Chromium lock fixture. This follow-up changes no +production code. It adds an abort history to the lifecycle oracle, gives the +Chromium fixture a 10-second pending-lock setup window before its 15-second +open deadline, and clarifies the previously unbounded default in the changeset. + +The new history aborts a real `AbortSignal` while reading the caller's +`vfsName` option. This happens after the initial signal check and before the +abort listener is installed. A silent init response and `timeoutMs: 0` make a +missed abort observable: the open would remain pending. The oracle checks the +exact rejection, one worker termination, listener removal, one init request, +and no second settlement after a late response. Removing the post-registration +abort check caused an assertion failure at the expected settlement checkpoint +(`[]` instead of the `AbortError` rejection). Restoring it made the focused test +pass. + +A controlled Chromium timing probe on the prior fixture queued a lock at 4.6 +seconds, before its 5-second open deadline. The 4.5-second pending-lock poll +had already failed, even though the open later rejected with `TimeoutError`. +The revised fixture gives worker startup more room without changing the +timeout, queued-lock disappearance, or successful-reopen assertions. Its new +window reduces this timing risk; it does not establish a maximum worker startup +time on every CI host. The native Chromium fixture passed with the revised +deadlines. The browser package suite passed with 382 runtime tests and no type +errors; changed-file ESLint, Prettier, and `git diff --check` passed. + +| Requirement | Follow-up outcome | +| --- | --- | +| ORC-001: authority and limits | The README's pending-open cancellation contract remains the authority. The added getter history covers a valid synchronous abort; the lock fixture remains one Chromium history. | +| ORC-002: independent judgment | The expected abort reason and resource release come from the public abort contract, not the production request map. | +| ORC-003: responsibilities | The existing lifecycle oracle retains its contract, model, fixed-history grammar, production driver, and settlement/refinement checks. | +| ORC-004: generated grammar | Not triggered. The generated pagehide grammar is unchanged; the new abort history is fixed. | +| ORC-005: path and observation | The new history calls the public opener and observes exact settlement, init reach, worker termination, and late-response behavior. The Chromium fixture still observes the native queued Web Lock and reopen. | +| ORC-006: calibration | Deleting the post-registration abort check failed the new oracle at the intended settlement assertion. The earlier native queued-lock disposal mutant remains recorded above. | +| ORC-007: fixed/random replay | Not triggered by this fixed history. Existing generated campaigns and replay controls are unchanged. | +| ORC-008: model minimality | Not triggered. No reference-model state changed. | +| ORC-009: vocabulary | Abort, open settlement, worker disposal, and queued Web Lock retain their production/browser meanings. | +| ORC-010: failure fidelity | The oracle's cleanup still releases held responses and restores globals. The Chromium fixture still preserves its primary failure and cleanup diagnostics. | +| ORC-011: second formulation | The controlled abort history and native lock fixture judge different boundaries; no new shared semantic fault requires another formulation. | +| ORC-012: review evidence | This addendum ties the focused mutant, suite receipts, and remaining timing limit to the exact executable commit above. | diff --git a/docs/contributing/oracle-reviews/issue-1891-async-cleanup.md b/docs/contributing/oracle-reviews/issue-1891-async-cleanup.md new file mode 100644 index 0000000000..eec76f9c79 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-1891-async-cleanup.md @@ -0,0 +1,297 @@ +# Issue #1891 asynchronous cleanup oracle review + +Reviewed semantic head: `21ed924352e91e47f0643de6d2542051ddd12d20` + +Scope: the cleanup-settlement repair in +`packages/db/tests/collection-cleanup-restart-oracle.test.ts` and the adjacent +focused refinements in +`packages/db-sqlite-persistence-core/tests/persisted.test.ts` and +`packages/powersync-db-collection/tests/load-hooks.test.ts`. This documentation +closeout does not change the reviewed semantic tree. + +## Claim and limits + +The public `Collection.cleanup()` contract, the `CleanupFn` contract, and the +contributing glossary authorize the core law. Cleanup invalidates the old sync +run and starts local teardown synchronously. If that run owns adapter cleanup +work, the work settles before the Collection publishes `cleaned-up`. The +terminal status event admits a new sync run. A continuation attached to the +public cleanup Promise runs later. + +Concurrent callers receive one Promise. A rejected adapter cleanup still ends +the old sync run once, publishes `cleaned-up`, and rejects each waiter with the +adapter error as the `SyncCleanupError` cause. It does not retry the retired +callback. A later sync run remains admissible. Cleanup treats an incidental +non-Promise return from a contextual-void callback as synchronous. It awaits a +runtime Promise-like return even when the callback is statically typed to +return `void`. + +The core owner is a partial oracle. It does not establish full demand or replay +histories, provider transport shutdown, persistence-wrapper behavior, or +general row-publication laws. The adjacent persistence and PowerSync tests +refine the same settlement boundary for their wrappers. They do not expand the +core owner's row or transport claims. + +The five oracle responsibilities are visible as follows: + +- **Contract:** the oracle opening and this record state the cleanup and restart + law, authority, observation cuts, and limits. +- **Model:** cleanup is a closed admission interval. Same-stack invalidation + starts it. Adapter settlement and terminal status publication end it. The + terminal event admits restart before public Promise continuations run. +- **History grammar:** the core matrix crosses abort versus release reentry, + nested cleanup, and one versus two restart attempts. Focused cases add + fulfillment, rejection, runtime return shape, concurrent waiters, and event + versus awaited restart. +- **Production driver:** the tests call the public Collection entry points. The + adjacent refinements drive the real persisted wrapper and a real PowerSync + Node SQLite database through public Collections and live queries. +- **Refinement check:** the tests compare exact status, event and continuation + order, Promise identity and outcome, error cause, callback counts, ownership, + subscriber counts, unhandled-error channels, and later restart behavior at + named gates. + +## ORC-001 through ORC-011 + +| Requirement | Outcome | +| --- | --- | +| ORC-001: contract authority and limits | Pass. `Collection.cleanup()`, the `CleanupFn` documentation, and the glossary's cleanup and restart terms authorize the expected result. The oracle opening and this record exclude full demand/replay, provider transport, wrapper-wide persistence, and general publication claims. | +| ORC-002: independent judgment | Pass. The expected order comes from the public contract: adapter settlement, terminal status publication, then public Promise continuation. Held gates and sentinel errors supply the expected observations without importing lifecycle transitions, cleanup classifiers, or adapter settlement helpers from production. | +| ORC-003: distinguishable responsibilities | Pass. The responsibilities are listed above. The executable owner keeps the contract and model in its opening, the bounded `scenarios` matrix beside the focused histories, public Collection drivers in each test, and exact refinement assertions at controlled checkpoints. | +| ORC-004: generated-history grammar controls | Not applicable. The repair makes no generated-history coverage claim. Its matrix is bounded enumeration, and the adapter refinements are fixed controlled histories. | +| ORC-005: production path and observation | Pass. Core tests call public cleanup, start, preload, and status subscription entry points. Persistence tests use `persistedCollectionOptions`, same-resource replacement, and real coordinator ownership. PowerSync tests use a real Node SQLite database, source Collection, and live queries. Gates prove that public cleanup remains pending; exact status, error cause, call count, ownership, and global error-channel assertions preserve the promised violations. | +| ORC-006: checker calibration | Pass. The R4 ordering mutant resolved the public cleanup Promise before scheduling terminal status. The gated core test reached the intended checkpoint and failed its exact order assertion with the public continuation before the `cleaned-up` event. This was an assertion failure, not a timeout, setup failure, or unreached path. The mutant was fully restored. Pre-fix witnesses also produced early core restart, duplicate persistence ownership, skipped teardown, early PowerSync settlement, and unhandled adapter rejection at their intended checkpoints. | +| ORC-007: fixed and random campaigns with replay | Not applicable. The repaired owner and adjacent refinements contain no important generated property. | +| ORC-008: stateful-model minimality | Not applicable. The repair does not add, remove, combine, or split state in a stateful reference model. The bounded relation still distinguishes pending cleanup, terminal cleanup, and a later sync run because restart legality and public settlement distinguish those cuts. | +| ORC-009: vocabulary mapping | Pass. The model-only phrase `cleanup admission interval` maps to the time from same-stack sync-run invalidation through terminal `cleaned-up` publication. `Sync run`, cleanup, restart, logical owner, physical acquisition, acquisition lease, and Promise settlement retain their glossary meanings. Adapter cleanup settlement and public cleanup Promise continuation are separate observations. | +| ORC-010: failure fidelity and cleanup | Pass. The tests do not shrink or normalize traces. Exact sentinel identity, `SyncCleanupError.cause`, ordered observations, and unhandled-error arrays preserve the primary violation. Persistence uses `cleanupPersistedOracle`; PowerSync uses `withTestCleanup`. Both attempt every cleanup and retain secondary diagnostics. Core gates are released in `finally`, and retired callbacks are not retried. | +| ORC-011: independent second formulation | Pass. The named shared-fault hypothesis is that a wrapper starts cleanup but discards a returned Promise. The core in-memory driver checks the public lifecycle relation. Persistence independently checks same-resource replacement, retired remote-owner fencing, and all-task settlement after a synchronous runtime failure. PowerSync independently checks eager provider disposal and already-acquired on-demand hooks through real SQLite. These paths share only the cleanup settlement and once-only release law; row ordering, projection, duplicates, and empty results are not part of this cleanup claim. | + +ORC-012 is satisfied by this versioned record. It identifies the exact reviewed +semantic head, records every ORC-001 through ORC-011 outcome, and is linked from +the coverage map. + +## Calibration and semantic-head receipts + +The R4 mutant changed only terminal ordering. The driver had already attached a +continuation and then released the adapter gate. Production reached adapter +settlement and both public observations. The exact comparison rejected the +wrong sequence `adapter -> public continuation -> cleaned-up event`; the +required sequence is `adapter -> cleaned-up event -> public continuation`. + +Additional RED/GREEN controls reached these missing laws: + +- An incidental numeric return from a contextual-void cleanup incorrectly + delayed core restart. Runtime Promise-like classification repaired it. +- A persisted source retired during synchronous entry later registered remote + ownership. A same-ID replacement exposed the duplicate owner. +- A synchronous persisted runtime-teardown failure settled public cleanup + before held source cleanup and skipped later release attempts. +- Eager PowerSync cleanup settled before held trigger disposal. The rejection + lane escaped through the global error channel. +- Active on-demand PowerSync cleanup settled before held hook cleanup, swallowed + a synchronous throw, and failed to await every reentrant release. + +At the reviewed head, the core focused owner campaign passed 349 tests with no +type errors. Persistence passed 292 tests with one existing todo and passed its +type check. PowerSync passed 154 tests and its type, lint, format, and package +build checks. + +## Known limits and tracked follow-up + +PowerSync cleanup now awaits eager trigger disposal and every already-acquired +on-demand hook. Two on-demand histories remain outside this repair: + +- an `onLoadSubset` hook that is pending when cleanup starts and returns its + cleanup callback only after retirement; +- provider trigger disposal created by that pending on-demand acquisition. + +A held on-demand trigger disposer proved that public cleanup can settle before +disposal. A candidate that delayed terminal `cleaned-up` until that work settled +made the disposer checks pass, but violated the existing staged-subset baseline +law: a held dependent preload could fulfill before source-cleanup publication. +The candidate was removed. This is a confirmed design gap, not green evidence. + +The durable destination is one core cleanup-start/intermediate signal distinct +from terminal `cleaned-up`. Adapters can use that signal to retire and await +pending or late hooks and on-demand trigger disposal without allowing a staged +dependent preload to fulfill during cleanup. That destination must be designed +with the pending/late on-demand hook history; this review does not claim it is +implemented. + +## Dual-failure reconciliation + +Reviewed semantic head: `228b68f7` + +A later review found two sibling cleanup paths that awaited every teardown but +reported only one rejection. The persisted wrapper preferred source cleanup and +dropped runtime teardown. Eager PowerSync preferred the load-hook cleanup and +dropped trigger disposal. The PowerSync loss was deterministic once both +resources existed; callback completion order did not change the winner. + +Controlled pre-fix probes reached both production paths. The persisted probe +observed only the exact source error. The PowerSync probe observed only the +exact load-hook error in both controlled completion orders. The repaired paths +keep source cleanup and trigger disposal primary, respectively. Each now uses +an `AggregateError` to retain the secondary diagnostic. Lone failures keep their +prior identity. + +The coverage gap was a missing dual-rejection history. Existing tests exercised +each cleanup source separately. The persisted-history owner now crosses source +and runtime rejection in one public cleanup. The PowerSync load-hook refinement +crosses hook and trigger rejection in both completion orders. Both tests assert +the public `SyncCleanupError`, aggregate cause, ordered error identities, +once-only callbacks, and terminal status where applicable. + +This reconciliation adds focused histories, not a generated-history or new +state-model claim. ORC-001, ORC-002, ORC-005, ORC-006, and ORC-010 are satisfied +by the established cleanup contract, independent sentinel errors, public +Collection drivers, recorded pre-fix assertion failures, exact error identity, +and bounded resource cleanup. ORC-003, ORC-004, ORC-007, ORC-008, ORC-009, and +ORC-011 are not triggered by these focused refinements. This versioned section +satisfies ORC-012 for the reconciliation. + +## Design-grammar and loss-audit closeout + +Reviewed semantic head: `ccef1e2ea6f6ea04740a27d7de6efde3424ecca6` + +This closeout adds the Query DB cleanup helper and public Query-backed +Collection witness to the reviewed scope. It also re-audits the core cleanup +oracle, persisted wrapper, and PowerSync wrapper after the failure-retention +repairs. A later documentation-only receipt commit does not change this +semantic tree. + +### Intended law and minimal grammar + +The retained model has five primitives: + +1. An active sync run owns cleanup obligations. +2. Same-stack cleanup invalidation creates one shared retirement ticket. +3. An ordered obligation ledger waits for every acquired cleanup obligation. +4. A deterministic failure algebra preserves every failure, its role, and its + ownership order; a lone failure keeps its identity. +5. The public observation trace is invalidation, obligation settlement, + terminal `cleaned-up` publication, public retirement Promise continuation, + and only then an ordinary replacement run. + +The wrappers refine that grammar rather than adding unrelated Cartesian axes: + +| Refinement | Primary role | Later retained roles | +| --- | --- | --- | +| Core | adapter cleanup | local teardown | +| Persistence | source cleanup | runtime releases in release order | +| PowerSync eager | trigger disposal | load-hook cleanup | +| PowerSync on-demand | first acquired cleanup obligation | later obligations in ledger order | +| Query DB | adapter cleanup | QueryClient unmount | + +A contextual-void callback may return an incidental non-Promise value; that is +synchronous completion. A native Promise or structural thenable is an +obligation and must settle. Fulfillment and rejection both close the old run; +rejection remains observable after terminal publication. Completion timing +never chooses the primary failure. + +### Loss-audit retention decisions + +The audit retained the distinctions that can change a promised observation: + +- active run, retirement pending, terminal publication, public Promise + settlement, and replacement run remain separate cuts; +- zero, one, and multiple obligations remain distinguishable; +- incidental values, native Promises, and structural thenables remain separate + runtime return classes; +- synchronous throw, asynchronous rejection, and multiple failures remain + separate settlement shapes; +- abort and release reentry, nested cleanup, and repeated restart attempts + remain explicit grammar coordinates; +- one and multiple wrapper-owned resources remain separate because the latter + exposes dropped diagnostics and order-dependent precedence; +- wrapper priority is a named refinement mapping, not an implicit scheduling + accident. + +The audit deliberately did not import demand replay, row publication, provider +transport, or adapter-specific startup into the cleanup grammar. A pending +PowerSync hook discovered after cleanup starts belongs to the cleanup-start +signal design in PR #1897. Those behaviors may share code paths, but no cleanup +observation here can judge their full contracts. + +### Grammar reconstruction, ablation, range, and exclusion + +**Reconstruction.** Every known valid cleanup witness in scope maps to the five +primitives: synchronous and asynchronous cleanup, structural thenables, +fulfillment and rejection, concurrent and nested cleanup callers, terminal +event and awaited restart, and each wrapper's resource roles. No known witness +requires a sixth lifecycle state or a wrapper-specific copy of the model. + +**Ablation.** Each declared coordinate has a named contribution. Removing +abort or release loses one reentry boundary. Removing nested cleanup loses the +shared-ticket check. Removing the second attempt loses repeated early-admission +pressure. Collapsing return classes recreates the truthy-number or hidden- +thenable bugs. Collapsing settlement shapes loses rejection ordering and lone- +error identity. Collapsing ownership count loses later failures. Removing a +wrapper row loses its public resource mapping and precedence law. The +executable `2 × 2 × 2` admission calibration proves eight unique cells and one +execution coordinate per declared cell. + +**Range.** The bounded admission axes are abort or release, nested cleanup on +or off, and one or two restart attempts. Focused margins cover zero/one/many +obligations, incidental/native/structural returns, synchronous/asynchronous +failure, both completion orders, and one/two wrapper failures. No independently +supplied held-out marginal case was available after construction, so formal +external range validation remains untested. A later range evaluation should +supply a behavior-preserving witness without changing the grammar first; the +model succeeds only if that witness reconstructs from the existing primitives +and the checker reaches its named public checkpoint. + +**Exclusion and negative controls.** The grammar rejects restart before +terminal publication, duplicated physical release, a dropped secondary +failure, treating a truthy non-Promise as a Promise, and ownership published by +a retired run. Pre-fix production reached and failed the intended checkpoints: +Query DB threw `TypeError: result.then is not a function`; persistence exposed +only the first runtime release error; PowerSync exposed only the first acquired +hook rejection in both controlled completion orders. These were assertion +failures, not timeouts, setup failures, or unreached paths. + +### ORC-001 through ORC-012 reconciliation + +| Requirement | Outcome at the reviewed semantic head | +| --- | --- | +| ORC-001: contract authority and limits | Pass. The public cleanup contract and glossary authorize settlement, terminal publication, and restart. The oracle opening and this record exclude replay, publication, provider transport, and late-acquisition design. | +| ORC-002: independent judgment | Pass. Held gates, structural thenables, sentinel identities, ordered public observations, and a role table derive expectations without importing production cleanup classifiers or failure selectors. | +| ORC-003: distinguishable responsibilities | Pass. Contract and model are in the oracle opening; named axes form the grammar; public Collections and wrappers are the drivers; exact status, order, identity, count, and ownership assertions are the refinement checks. | +| ORC-004: generated-history grammar controls | Not triggered because this remains bounded enumeration, not a generated-property claim. Reconstruction, ablation, range, and exclusion are nevertheless recorded above, and unique-cell calibration is executable. | +| ORC-005: production path and observation | Pass. Core, persistence, and PowerSync drive public Collections. Query DB directly checks helper composition and separately proves the real public wrapper mounts and unmounts QueryClient exactly once. A supported public seam for injecting arbitrary async internal Query cleanup does not exist; no private seam was added. | +| ORC-006: checker calibration | Pass. The prior R4 ordering mutant remains killed. New pre-fix controls separately killed truthy non-Promise classification and both same-role failure-dropping designs at their intended assertions. | +| ORC-007: fixed/random campaigns and replay | Not triggered. No important randomized property was added or claimed. | +| ORC-008: stateful-model minimality | Not triggered by a state-model shape change. The audit still retained five observable cuts because restart legality and Promise ordering distinguish each adjacent pair. | +| ORC-009: vocabulary mapping | Pass. Active sync run, cleanup, restart, logical owner, physical acquisition, and Promise settlement retain glossary meanings. `Retirement ticket`, `obligation ledger`, and `terminal publication` are model descriptions mapped above to the public cleanup Promise, acquired cleanup work, and the `cleaned-up` status event. | +| ORC-010: failure fidelity and cleanup | Pass. Core and PowerSync hostile harness controls retain a primary mismatch plus every cleanup failure. Persistence now calibrates both modes: it aggregates cleanup failures when there is no primary and records all secondary diagnostics without replacing an existing primary. All actions are attempted. | +| ORC-011: independent second formulation | Pass within scope. The core public lifecycle uses a structural thenable; Query's direct helper isolates wrapper composition while the public QueryClient witness proves real mount/unmount wiring; persisted ownership replacement and real PowerSync SQLite hooks independently test wrapper refinement. Query's public witness does not claim arbitrary async adapter injection. | +| ORC-012: review evidence | Pass. This exact-head record accounts for ORC-001 through ORC-011, including non-triggered requirements and the unresolved held-out-range limitation. | + +### Exact-head receipts + +Environment: Node `v24.19.0`, pnpm `11.1.0`, Vitest `3.2.4`, Darwin +`arm64`. + +At baseline head `efa90005248c7274795e489aa08e494914215483`, the new RED +controls produced one Query failure, one persistence failure, and two +PowerSync failures. At semantic head +`ccef1e2ea6f6ea04740a27d7de6efde3424ecca6`: + +- the core cleanup oracle passed 25 of 25 tests; +- Query cleanup composition passed 8 of 8 tests, including the public wrapper + witness; +- the selected persisted cleanup and harness histories passed 8 of 8 tests + with 240 unrelated tests skipped by the name filter; +- the PowerSync load-hook file passed 25 of 25 tests; +- TypeScript checks passed for DB, SQLite persistence, and PowerSync; +- Query DB's package build, including declaration generation, passed. Its broad + TypeScript configuration also traverses DB collection E2E suites and failed + only because `@tanstack/electric-db-collection` was not resolvable in this + checkout; no changed Query file produced a diagnostic; +- DB, SQLite persistence, and PowerSync package builds also passed, and the + documentation link check found no broken links; +- targeted ESLint reported zero errors. Its 22 warnings are pre-existing + `require-await` warnings in the persisted test file outside the changed + lines; +- Prettier and `git diff --check` passed. diff --git a/docs/contributing/oracle-reviews/issue-1891-cleanup-start-followup.md b/docs/contributing/oracle-reviews/issue-1891-cleanup-start-followup.md new file mode 100644 index 0000000000..8407f23a67 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-1891-cleanup-start-followup.md @@ -0,0 +1,481 @@ +# Issue #1891 cleanup-start follow-up oracle review + +Reviewed semantic head: `14b06080e0881f41b5363b8a4a3ffd0c3b3aed31` + +Base and prior review-record head: +`355554078675c9aaf924b649d2bdcd3636e40d4a` + +Initial implementation and control head: +`2fa7fada018613063c2f0d4a915f244ec68412d1` + +Evaluate Review fix head: `8f0481ad1e5d096a372da7df0002c8d4c2319c9d` + +Scope: the cleanup-start boundary in core, dependent live-query and Effect +invalidation, and the PowerSync late on-demand hook and trigger-disposal repair. +The primary executable owner remains +`packages/db/tests/collection-cleanup-restart-oracle.test.ts`. PowerSync's +`packages/powersync-db-collection/tests/load-hooks.test.ts` is a real-provider +refinement. This documentation closeout does not change the reviewed semantic +tree. + +## Authority, issue history, and selected contract + +Issue #1891 exposed an existing cleanup barrier rather than inventing a new +one. The API and documentation tell callers to await cleanup before starting a +new sync run. The minimal reproduction returned a held Promise from a cleanup +callback and observed public cleanup settle before that Promise continued. At +the time of the report, the callback type and Collection implementation treated +the cleanup as `void` and ignored the runtime Promise. + +That defect matters even when no row assertion fails immediately. If an old and +replacement Collection use the same SQLite resource, old cleanup can affect the +replacement or persist deletes after replacement hydration. Without a cleanup +settlement barrier, an application needs a separate drain or cancellation +barrier before constructing the replacement. + +Two contracts were possible: + +1. Await runtime Promise-like cleanup results before terminal cleanup. +2. Define cleanup as initiation-only and expose a separate settlement hook. + +The project selected the first contract. The initiation-only alternative would +have required explicit documentation and another public barrier; silently using +those semantics does not satisfy the existing cleanup and restart contract. +Issue #1889 is adjacent but distinct. It concerns late startup `markReady` and +initial-query readiness. Issue #1891 concerns settlement and invalidation of an +ending sync run. Cleanup start is not startup readiness, a Collection status, or +proof that adapter resources settled. + +The earlier repair added awaitable cleanup and preserved these laws: + +- concurrent cleanup callers share one Promise; +- adapter rejection ends the old run once, publishes `cleaned-up`, rejects all + waiters with the adapter error as `SyncCleanupError.cause`, and does not retry + the retired callback; +- a later sync run remains admissible; +- incidental non-Promise returns from contextual-`void` callbacks remain + synchronous; +- runtime Promise-like returns are awaited even when the callback is statically + typed to return `void`; and +- the terminal `cleaned-up` event admits restart before a continuation on the + public cleanup Promise runs. + +The previous exact-head review left two PowerSync histories open: an +`onLoadSubset` hook that returns its disposer after cleanup starts, and trigger +disposal created by that pending acquisition. Merely delaying terminal +`cleaned-up` until those resources settled was rejected. In the staged-preload +counterexample, the dependent live query remained active, accepted the late +baseline, and fulfilled preload before source-cleanup publication. The durable +design therefore needed an earlier invalidation boundary distinct from terminal +status. + +At the reviewed head, cleanup start synchronously closes restart admission, +puts each dependent live query in terminal error once, marks dependent Effects +disposed, and detaches subscription demand. Throwing and reentrant observers do +not strand teardown or observer ownership. The source Collection keeps its +prior public status while adapter cleanup is pending. PowerSync owns pending +on-demand loads, late hook cleanup, already released cleanup Promises, and +trigger disposal until they settle. A pending hook-acquisition failure +participates in cleanup failure selection, while an expected transaction-abort +consequence does not replace the primary cleanup failure. Only after all owned +work settles does core publish `cleaned-up` and settle public cleanup. + +## Oracle responsibilities + +- **Contract:** the public cleanup/restart contract, project glossary, and + live-query architecture authorize the two boundaries and state their limits. +- **Model:** `expectedCleanupBoundary` is an independent two-checkpoint + timeline. It predicts restart admission, source status, public cleanup + settlement, and a combined dependent-terminal observation. +- **History grammar:** fixed histories hold adapter cleanup at cleanup start and + settlement. Adjacent bounded cases cover live queries, repeated aliases, + Effects with pending handlers, abort/release reentry, nested cleanup, + concurrent callers, throwing and late observer registration, fulfillment, + rejection, runtime return shape, late hook return or rejection, already + released cleanup, trigger creation and disposal, and combined cleanup + failures. +- **Production driver:** core calls public Collection, live-query, and Effect + entry points. PowerSync uses `@powersync/node` with a real temporary Node + SQLite database, public source Collections, and dependent live queries. +- **Refinement check:** held gates define the observation cuts. Tests compare + exact status, restart admission, Promise identity and settlement, exact error + causes and precedence, dependent state, subscriber and observer ownership, + disposer and report counts, publications, rows, adapter logs, and the process + unhandled-rejection channel. + +The model's `dependent: terminal` field is an explicit abstraction. It combines +two different public observations: live-query status `error` and +`Effect.disposed === true`. It does not claim those dependents have the same +cleanup machinery. Cleanup start maps to the internal synchronous callback +boundary. Cleanup settlement maps to the later public cleanup Promise cut. + +## ORC-001 through ORC-011 + +| Requirement | Outcome | +| --- | --- | +| ORC-001: contract authority and limits | Pass. `Collection.cleanup()`, the await-before-restart documentation, the glossary's cleanup and cleanup-start terms, and the live-query architecture authorize the result. The exact held-Promise reproduction and same-SQLite replacement hazard establish why settlement matters. The owner remains partial: it does not establish full demand/replay histories, provider transport shutdown, wrapper-wide persistence, general row-publication laws, Effect-handler ordering beyond the pending-disposal case, or native hosts other than the tested Node SQLite path. | +| ORC-002: independent judgment | Pass. The expected timeline comes from the approved cleanup contract and rejected staged-preload counterexample. `expectedCleanupBoundary` imports no lifecycle transition, callback registry, adapter collector, or Promise classifier. Held gates and sentinel errors compute observations independently of production's cleanup queues and task sets. | +| ORC-003: distinguishable responsibilities | Pass. The five responsibilities are listed above. The executable owner keeps its contract, limits, pure model, bounded scenarios, public production drivers, recorders, and named cleanup-start and cleanup-settlement checks together. Its repeated-alias, pending-handler, throwing-observer, and late-registration refinements are explicit fixed cases outside the two-checkpoint model. PowerSync keeps provider-specific ownership and failure histories beside its real SQLite driver. | +| ORC-004: generated-history grammar controls | Not applicable. This follow-up makes no generated-history or input-grammar coverage claim. Its scenario products are bounded enumerations, and the late-hook and trigger tests are fixed controlled schedules. Reconstruction, ablation, random range, and exclusion controls are therefore not triggered. | +| ORC-005: production path and observation | Pass. Core reaches public `cleanup()`, `preload()`, `startSyncImmediate()`, live-query status, and Effect disposal. It also observes repeated aliases, pending handler settlement, cleanup-start observer counts, exact observer failures, teardown completion, and later restart. PowerSync reaches its public Collection adapter through a real `PowerSyncDatabase` using Node SQLite, a source Collection, and a dependent live query. Positive witnesses include entered hook gates, disposer call counts, source subscriber and observer counts, and trigger creation/disposal. The recorders retain early settlement, wrong status, duplicate terminal reports, wrong error cause or precedence, unexpected publication or adapter logging, and unhandled rejection at the held checkpoints. | +| ORC-006: checker calibration | Pass. At the initial implementation head, C-CORE-LIVE-START left a held live query `loading` instead of `error` (1 assertion failure, 18 passes); M-CORE-EFFECT-START observed `active` instead of `terminal` (1 assertion failure, 19 skipped); M-PS-TRIGGER-AWAIT settled both disposer lanes early (2 assertion failures, 18 skipped); C-PS-LATE-HOOK settled both late-hook lanes early (2 assertion failures, 18 skipped); and C-PS-RELEASED-HOOK settled both released-hook lanes early (2 assertion failures, 20 skipped). Final review controls reproduced duplicate alias reporting (`expected 1`, received `2`), one leaked late Effect observer (`expected 0`, received `1`), and a throwing late observer poisoning the next cleanup. The final in-flight-disposer mutant invoked without awaiting its Promise; both lanes reached the disposer and then failed the pending-settlement assertion (2 failures, 25 skipped). Every temporary mutation and control was restored. None was a timeout, setup failure, or unreached path. | +| ORC-007: fixed and random campaigns with direct replay | Not applicable. No important generated property, generator, seed, run budget, shrink path, or replay interface changed. The bounded and fixed controls do not claim random coverage. | +| ORC-008: stateful-model minimality | Not applicable. `expectedCleanupBoundary` is stateless recomputation over a prior status and checkpoint. It does not introduce, remove, combine, or split mutable state in a stateful reference model. The distinct cleanup-start and cleanup-settlement inputs preserve the next-action distinction that restart admission exposes. | +| ORC-009: vocabulary mapping | Pass. `cleanup start`, cleanup, restart, sync run, Collection status, settlement, demand, physical acquisition, and acquisition release retain their glossary meanings. The model-only `dependent: terminal` abstraction maps explicitly to live-query terminal error or Effect disposed. Cleanup start is an internal invalidation cut, not the terminal `cleaned-up` status; cleanup settlement is distinct from the terminal event's earlier restart-admission cut. PowerSync's cleanup collector and shared active trigger-disposal Promise express implementation ownership, not new public lifecycle states. | +| ORC-010: failure fidelity and cleanup | Pass. The tests do not shrink or normalize failures. Exact sentinel identity, `SyncCleanupError.cause`, settlement order, raw publication arrays, adapter logs, and unhandled-rejection arrays preserve the violated law and checkpoint. Core releases held gates, completes teardown after an observer throws, and cleans dependents in `finally`. PowerSync attempts all owned work, deterministically prefers trigger-disposal failure over later collector inspection, adopts a hook-acquisition failure without double logging it, and does not promote expected `SyncTransactionAbortedError` collateral to the primary cleanup failure. `withTestCleanup` retains the primary harness failure as `AggregateError.cause` and keeps secondary cleanup errors distinguishable. The mutants were restored before green verification. | +| ORC-011: independent second formulation | Pass. The named shared-fault hypothesis is that core or a wrapper starts cleanup but discards a returned Promise or late-owned task. Core's in-memory timeline, the prior persisted same-resource/retired-owner refinement, and PowerSync's real SQLite hook/trigger path use meaningfully different machinery. Repeated-alias live-query reporting, pending Effect-handler disposal, and throwing or reentrant observer registration independently test cleanup-start invalidation and ownership. These formulations share only the settlement, invalidation, once-only release, and primary-failure laws. They make no equivalence claim about row ordering, projection, duplicates, or nonempty results. The same-SQLite multi-process and mobile-host risks remain outside this review. | + +ORC-012 is satisfied by this versioned record for semantic head +`14b06080e0881f41b5363b8a4a3ffd0c3b3aed31`. It records every ORC-001 through +ORC-011 outcome and is linked from the coverage map. The initial +`2fa7fada018613063c2f0d4a915f244ec68412d1` head remains only as provenance for +the controls and pre-fix review findings described below. + +## Calibration details and counterexamples + +At the initial `2fa7fada` implementation head, the PowerSync trigger mutant +changed `disposeTrackingAfterAbort()` from an owned Promise in +`Promise.allSettled([trackingDisposal, ...pendingLoads])` to fire-and-forget. +Both fulfillment and rejection lanes reached disposer invocation. While the +disposer gate remained held, the public cleanup Promise settled. The restored +implementation stayed pending, then fulfilled or rejected with the exact +sentinel cause after release. + +The initial late-hook control held `onLoadSubset` before it returned a cleanup +callback. Cleanup start had to make the dependent live query terminal +immediately while the source status stayed `ready`. Releasing the hook installed +its disposer without creating a trigger. Public cleanup remained pending until +that disposer settled. The rejection lane produced one `SyncCleanupError` with +the sentinel cause and no unhandled rejection. Rows and publication arrays +stayed empty. + +The already released hook control retired the live query first. Its held hook +cleanup was no longer in the active demand map when Collection cleanup began. +The restored adapter adopted that Promise into the cleanup collector. Both +lanes remained pending, invoked the hook once, and produced no unhandled +rejection. This distinguishes ownership from mere presence in the current +demand map. + +The initial core live-query control kept only the terminal `cleaned-up` +listener. With source cleanup held, the exact cleanup-start checkpoint observed +live-query status `loading` instead of `error`. This is the same missing +distinction that made the staged-preload counterexample possible. + +Final core review reproduced three ownership and once-only failures before the +fixes in `8f0481ad`: + +- two lexical aliases for one source emitted two terminal error reports instead + of one; +- an Effect registered during active cleanup disposed immediately but retained + one cleanup-start observer; and +- an immediately throwing late observer remained registered and rejected the + next cleanup. + +The repaired lifecycle removes a late observer when its immediate registration +callback throws. Effect registration releases the callback when cleanup becomes +active before the unsubscribe handle can be retained. A fatal live query now +ignores later alias notifications. Focused coverage also holds an Effect handler +open while cleanup start disposes the Effect and releases its source +subscription, and proves that a throwing observer still allows adapter teardown, +terminal publication, guard release, and later restart. Omitting cleanup-start +delivery from the final core tree produced three intended assertion failures. + +Final PowerSync review exposed two more failure-loss paths. First, +reconciliation could claim an in-flight trigger disposer after cleanup took its +snapshot. The cleanup-owned call then saw no published disposer and settled +without joining the invocation already in progress. The shared +`activeTrackingDisposal` Promise makes both owners join the same settlement. +The fulfillment and rejection lanes remain pending through disposer invocation; +a mutant that invoked but did not await the disposer failed both pending checks. + +Second, rejection directly from a pending `onLoadSubset` hook was awaited but +discarded. The active cleanup collector now retains that failure. The first +broader repair treated every captured pending-load rejection as a cleanup +failure, but the staged-baseline collateral test showed that this incorrectly +promoted an expected `SyncTransactionAbortedError`. The final repair records the +hook-acquisition failure at its boundary and leaves expected transaction-abort +collateral secondary. A combined probe rejects hook cleanup first and trigger +disposal later: cleanup waits for both, deterministically reports the trigger +error, emits no duplicate adapter log, and creates no unhandled rejection. +Ordinary unload remains synchronous and reports a later unadopted hook failure +exactly once. + +Two earlier controls remain independent evidence rather than final-head +mutants. The prior R4 ordering mutant produced +`adapter cleanup -> public Promise continuation -> cleaned-up event`; the core +oracle rejected it because the terminal event must precede the continuation. +The terminal-only delay candidate awaited late PowerSync resources but left the +dependent live query active. Its staged preload fulfilled before cleanup +publication; the expected outcome was rejection at cleanup start. The assertion +failure removed that candidate in favor of cleanup start. + +## Verification and prior reviewed-head receipts + +At the reviewed head: + +- the cleanup/restart oracle passed 24 of 24 tests; +- its TypeScript check passed; and +- `git diff --check` passed before this documentation closeout. + +At the Evaluate Review fix head `8f0481ad`, before the final behavior-preserving +deduplication simplification: + +- the Effect collateral suite passed 86 of 86 tests; +- the Collection subscription-lifecycle collateral passed 445 of 445 tests; +- DB TypeScript and targeted ESLint checks passed; +- the full PowerSync package passed 170 of 170 tests across 11 files with no + type errors; +- the PowerSync production build and targeted ESLint check passed; and +- the final no-await in-flight trigger-disposal mutant failed both intended + assertions and was restored. + +The initial `2fa7fada` implementation receipts were 32 of 32 focused core and +Effect tests, 22 of 22 focused PowerSync load-hook tests, and 165 of 165 full +PowerSync tests across 11 files, plus the stated DB, DB IVM, PowerSync, lint, +format, and diff checks. One earlier PowerSync package run at that stage raced +its build and could not resolve `dist/esm/index.d.ts`; that was a setup-order +failure, and the post-build 165-test rerun was green. + +## Late live-query registration reconciliation + +Reviewed semantic head: +`889b930170c7c23b75f020297d96fa92f3c31142` + +Review baseline: `9cc05782a035ca737e2c3dc360ae4ed417be2045` + +A later review found that a live query could start while one source's adapter +cleanup was pending. Cleanup-start observer registration correctly delivered +the active boundary synchronously and put the live query in terminal error, but +the builder continued its setup loop. At the baseline, the controlled +first-source history observed one subscription on both the cleaning source and +the later healthy source. An intermediate guard stopped later acquisition but +left one earlier healthy-source subscription when the cleaning source occupied +the second position. The final repair throws the exact terminal setup error +after unregistering the current observers. The existing sync-entry rollback +then releases all earlier source ownership and prevents later acquisition. + +The coverage gap was an inverted lifecycle history. Existing tests created a +dependent before cleanup began or registered an Effect during active cleanup. +They did not start a live query after cleanup start, nor vary the cleaning +source's position in a multi-source query. The permanent two-cell refinement +holds adapter cleanup, places the cleaning source first and second, calls the +public live-query preload path, and checks the exact terminal error, one report, +and zero retained subscriptions on both sources. + +| Requirement | Outcome | +| --- | --- | +| ORC-001: contract authority and limits | Pass. The glossary and live-query architecture require cleanup start to put dependent live queries in terminal error. This focused refinement adds the setup-order consequence: setup that observes that terminal boundary retains no partial source ownership. It does not expand the oracle's transport, row-publication, or full demand-history claims. | +| ORC-002: independent judgment | Pass. A held adapter-cleanup gate creates the boundary. Exact public status, error text, report count, and source subscriber counts supply the expected observations without importing builder state or transition helpers. | +| ORC-003: distinguishable responsibilities | Pass. The existing oracle opening retains its contract, two-checkpoint model, fixed history grammar, public driver, and refinement checks. The new fixed source-position matrix is named as an adjacent ownership refinement outside the small timeline model. | +| ORC-004: generated-history grammar controls | Not applicable. The first/second source product is a bounded two-cell enumeration, not a generated-history coverage claim. | +| ORC-005: production path and observation | Pass. The driver calls real source `preload()` and `cleanup()`, creates a real joined live-query Collection, and calls its public `preload()`. Both cells reach synchronous cleanup-start delivery while adapter cleanup remains held. The checkpoint observes terminal status and exact subscriber ownership before cleanup settlement. | +| ORC-006: checker calibration | Pass. At the baseline, the first-source cell failed with `{ cleaning: 1, healthy: 1 }` instead of zeros. The intermediate stop-only guard made that cell pass but the second-source cell failed with `{ cleaning: 0, healthy: 1 }`. Neither result was a timeout, setup failure, or unreached path. The final rollback repair passes both cells. | +| ORC-007: fixed and random campaigns with direct replay | Not applicable. No important generated property, seed, budget, shrink path, or replay interface changed. | +| ORC-008: stateful-model minimality | Not applicable. No stateful reference-model state was added, removed, combined, or split. Source position is a fixed driver dimension. | +| ORC-009: vocabulary mapping | Pass. Cleanup start, cleanup settlement, live-query Collection, source Collection, subscription, and sync run retain their glossary meanings. Partial source ownership means subscriptions acquired before terminal setup rollback; it is an observation, not a new lifecycle state. | +| ORC-010: failure fidelity and cleanup | Pass. The test preserves exact status, error, log, and subscriber-count observations. `finally` releases the adapter gate, cleans the live query and both sources, and cannot replace the primary assertion failure. | +| ORC-011: independent second formulation | Pass. The existing late-Effect-registration refinement independently proves immediate disposal and observer release. The new live-query path instead exercises joined-source setup rollback and subscription ownership. Both refine cleanup-start invalidation without equating their teardown machinery. | + +ORC-012 is satisfied by this versioned section for semantic head +`889b930170c7c23b75f020297d96fa92f3c31142`. Focused verification passed the +30-test cleanup/restart oracle, the 12-test Effect-disposal oracle, and the +199-test Collection subscription-lifecycle oracle. The DB build, DB TypeScript +check, targeted ESLint and Prettier checks, and `git diff --check` also passed. + +The ancestor record +[`issue-1891-async-cleanup.md`](issue-1891-async-cleanup.md) reviewed semantic +head `21ed924352e91e47f0643de6d2542051ddd12d20` and was versioned by +`355554078675c9aaf924b649d2bdcd3636e40d4a`. Its receipts were 349 core tests, +292 persistence tests with one existing todo, and 154 PowerSync tests, plus the +stated type, lint, format, and build checks. That record classified pending late +hooks and trigger disposal as an open design gap and named cleanup start only as +the destination. This follow-up implements and calibrates that destination; it +does not rewrite the earlier verdict. + +## Design-grammar reconciliation + +Reviewed semantic head: +`20407a501a2441cd3780198809e75c00eb410925` + +The frozen preservation contract required the model to retain cleanup start, +all-owned retirement, observer and adapter failure aggregation, late-registration +rollback, one notification per owner per boundary, terminal dependent ownership +across source restart, and the PowerSync pending-resource refinement. The +smallest reconstruction is a graph of independent laws rather than one model of +every lifecycle manager: + +- a three-cut timeline owns cleanup start, terminal `cleaned-up` publication, + and later public cleanup-Promise settlement; +- observer registration ownership crosses cleanup-boundary identity, so one + registration receives one notification for each boundary until it + unsubscribes; +- dependent ownership crosses sync-run identity, so source restart does not + revive a live query terminalized by the retired run; +- setup rollback owns the inverted history in which a dependent registers while + cleanup is already active; +- a provenance-labelled failure relation keeps adapter failure primary and + observer or local failure secondary; and +- the PowerSync task collector refines the one adapter-cleanup obligation with + active, released, pending, and late hook cleanup plus present or in-flight + trigger disposal. + +The core timeline now models all three observable cuts. At cleanup start, +restart admission is closed, public status is unchanged, and cleanup is +pending. At terminal publication, admission is open and status is `cleaned-up` +while the original cleanup Promise remains pending. Promise settlement follows. +The model still does not reproduce callback registries, manager order, provider +tasks, replay, or publication machinery. + +### Reconstruction, ablation, exclusion, and range + +Every known valid witness reconstructs from the graph above. The timeline owns +the held-adapter case. Separate driver refinements cover live queries, Effects, +repeated aliases, late observers, source position, abort and release reentry, +runtime cleanup return shape, concurrent callers, terminal-event restart, and +failure combinations. PowerSync separately covers each provider task origin and +their consequential overlap. + +One-at-a-time ablation produced these results: + +| Removed coordinate or relation | Lost distinction | +| --- | --- | +| Cleanup start | Dependents remain active while adapter cleanup is pending. | +| Terminal-publication cut | Event-time restart and event-before-Promise order disappear. | +| Cleanup-Promise cut | Adapter settlement becomes indistinguishable from public completion. | +| Sync-run identity | A source restart may revive an already-terminal dependent. | +| Registration by cleanup boundary | Once-per-boundary and once-per-lifetime become indistinguishable. | +| Cleaning-source position | Setup can retain an owner acquired before the cleaning source. | +| Adapter/local failure provenance | Rejection timing may choose the wrong primary failure. | +| Runtime return shape | A contextual-`void` Promise can be mistaken for synchronous cleanup. | +| PowerSync task origin | A late hook, released hook, or in-flight trigger can fall outside adapter ownership. | +| PowerSync task-set growth | Cleanup can snapshot tasks once and settle before a late disposer. | + +The two-attempt reentry coordinate does not justify a full Cartesian expansion +by itself, but it retains one repeated-denial observation without meaningful +runtime cost. The existing eight reentry cells therefore remain. Stable case +IDs now calibrate the complete `abort|release × plain|nested × 1|2` product and +the complete `event|await × source|live-query` product. The executable check +asserts the expected order, completeness, and uniqueness of all twelve IDs. +The source-position matrix and PowerSync result-shape matrices are explicit +one-dimensional enumerations with no derived product or skipped cells. + +Nearby invalid states are rejected or kept outside this owner: + +- an observer registered after cleanup settlement does not retroactively + observe the old boundary; +- restarting or recreating a live-query Collection differs from restarting + only its cleaned source; +- a settled ordinary-unload failure is reported once rather than retroactively + adopted by a later cleanup; and +- provider transport shutdown, demand replay, and general row publication + remain outside this oracle. + +No independent marginal case was supplied after the grammar froze. Range is +therefore untested beyond the current core driver and the Node SQLite PowerSync +refinement. Existing persistence and PowerSync cases shaped the grammar and are +not held-out range evidence. Browser, mobile, multi-process, and provider +transport behavior remain outside the claim. + +### New executable controls + +The core owner adds three focused witnesses: + +1. A cleanup-start observer throws before the adapter cleanup later rejects. + The final `AggregateError` keeps the wrapped adapter failure as `cause` and + the observer failure as its secondary error. +2. One ordinary observer remains registered across two cleanup/restart + boundaries, receives exactly one notification for each, then receives none + after unsubscribe. +3. Restarting a cleaned source leaves the original dependent live query in + terminal error, preserves its prior result, and emits no replacement + publication. + +The PowerSync refinement adds the higher-order overlap omitted by the separate +late-hook and in-flight-trigger cases. One cleanup starts while trigger creation +and another subset hook are both pending. Both later cleanup functions are +owned and invoked once. Hook cleanup rejects first while trigger disposal stays +pending. Cleanup remains pending, then rejects with trigger disposal as the +deterministic primary failure, without duplicate adapter logging or an +unhandled rejection. + +The direct observer-plus-adapter checker was calibrated with a temporary +priority-inversion mutant. The mutant made the observer failure the aggregate +cause and failed the intended test at its exact cause assertion: one failure and +33 skipped tests. Restoring production made all 34 core oracle tests pass. No +production change was required. + +### Reusable boundary-law prompts + +| Prompt | Disposition | +| --- | --- | +| Real-provider conformance | Applicable to the provider refinement. PowerSync runs through `@powersync/node` and a real temporary Node SQLite database. The in-memory core fixture makes no provider-value claim. | +| Minimal ambiguity | Applicable and covered. Incidental non-Promise return, runtime Promise, synchronous throw, asynchronous rejection, and fulfillment have separate witnesses. | +| Name invariance | Not applicable. No user-controlled name or SQL alias selects cleanup classification. | +| Representation symmetry | Applicable only to cleanup return representation. Function and object cleanup forms plus contextual-`void` runtime Promises preserve the same settlement law. | +| Await-boundary transitions | Applicable and covered at cleanup start, terminal publication, adapter settlement, late hook return, trigger creation, and disposer settlement. | +| Local/transport refinement | Boundary only. Core and Node SQLite share lifecycle settlement; provider transport termination remains explicitly unclaimed. | +| Partial-construction cleanup | Applicable and covered for cleaning-source position, pending hook acquisition, trigger creation, and their combined overlap. This does not claim every provider construction step. | +| Value-and-work refinement | Not applicable as a bounded-work law. Exact once counts protect ownership, not performance or transfer cardinality. | + +### Exact-head environment and receipts + +The semantic head ran with Node `24.19.0`, pnpm `11.1.0`, Vitest `3.2.4`, +TypeScript `5.9.3`, `@powersync/node` `0.20.0`, and Darwin `25.6.0` on arm64. + +- The cleanup/restart oracle passed 34 of 34 tests with no type errors. +- The PowerSync load-hook suite passed 33 of 33 tests with no type errors. +- The full PowerSync package passed 176 of 176 tests across 11 files with no + type errors. +- Targeted ESLint and Prettier checks passed. +- `git diff --check` passed. + +The first full DB-package verification executed 6,406 assertions across 216 +files but the type checker rejected the event recorder's broad +`CollectionStatus` annotation. The follow-up semantic head above narrows that +observation from the already-discriminated event value. The targeted oracle and +its type check then passed. The final full DB-package rerun passed all 6,406 +tests across 216 files with no type errors. + +## Multiple cleanup-start failure reconciliation + +Reviewed semantic head: +`eb93f42094a365f5b2b599504ed4ffea4da66bbb` + +Review baseline: `7de64caeeb46c0a6d2b9f5c7e7cf424d7e6193da` + +A final current-head review found one failure-fidelity gap. Cleanup start +attempted every observer, but `runAllCallbacks` rethrew only the first observer +failure and `beginCleanup` retained only one local failure. With two throwing +observers and a rejecting adapter cleanup, the public `AggregateError` retained +the wrapped adapter failure and the first observer failure but silently dropped +the second observer failure. + +The fixed oracle is the smallest distinguishing history: two cleanup-start +observers throw distinct sentinel errors in registration order, adapter cleanup +rejects after a held gate, and cleanup then admits a new sync run. The RED run +reached both observers and failed because `AggregateError.errors` had two entries +instead of three. Production now records every local teardown failure in +execution order. Adapter cleanup remains the primary `cause`; a lone local +failure keeps its identity; multiple local-only failures use the first local +failure as their aggregate cause. Teardown, terminal publication, and later +restart still complete. + +| Requirement | Reconciliation outcome | +| --- | --- | +| ORC-001 | Pass. The existing cleanup contract and ORC-010 failure-fidelity law require each cleanup diagnostic to remain distinguishable. The test does not claim provider transport shutdown or full demand/replay coverage. | +| ORC-002 | Pass. Expected error identity and order come from callback execution order and adapter-primary precedence, not the production aggregator. | +| ORC-003 | Pass. The existing contract, timeline model, fixed failure history, public Collection driver, and exact aggregate comparison remain distinguishable. | +| ORC-004 | Not applicable. This is one fixed controlled history, not a generated-grammar claim. | +| ORC-005 | Pass. The test calls public preload, cleanup, and restart entry points; observer call order, exact error identities, status, and restart are observed. | +| ORC-006 | Pass. Baseline production was the hostile wrong design. It reached both callbacks and failed only the missing-third-error assertion. | +| ORC-007 | Not applicable. No important generated property changed. | +| ORC-008 | Not applicable. No stateful reference model changed. | +| ORC-009 | Pass. Cleanup start, adapter cleanup, local teardown, settlement, and restart retain their glossary meanings. | +| ORC-010 | Pass. The aggregate keeps the adapter failure as `cause` and retains both observer failures in execution order while cleanup releases resources and permits restart. | +| ORC-011 | Not applicable. The named defect is information loss in one concrete aggregator; no meaningfully independent semantic formulation is needed beyond exact sentinel identities and order. | + +ORC-012 is satisfied for this repair by the requirement-by-requirement record +above. The focused RED run failed one of one selected tests with no type errors. +After the repair, the complete cleanup/restart oracle passed 37 of 37 tests, +and the five-suite lifecycle collateral passed 204 of 204 tests with no type +errors. Targeted ESLint, Prettier, and `git diff --check` passed. diff --git a/docs/contributing/oracle-reviews/issue-1898-relation-filter.md b/docs/contributing/oracle-reviews/issue-1898-relation-filter.md new file mode 100644 index 0000000000..fc31194f8c --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-1898-relation-filter.md @@ -0,0 +1,74 @@ +# Issue #1898 relation-filter review + +## Scope and evidence + +Base: `afbeb44eef48d92b6e30ae5fd2843b938ee9163c`. The issue and its +September 25 author comment contain 26 distinct claims, proposals, and open +questions. The executable owner is +`packages/db/tests/query/ordered-work-oracle.property.test.ts`; the ordered +lifecycle and pagination oracles are adjacent owners. This review covers one +direct LEFT join to a Collection with direct root ordering. It does not prove +remote relation filtering or a changed-FK failure. Finite continuation also +requires an order index; without one, the existing full-source fallback can +still apply after an underfilled prefix. + +Recent merged work covered adjacent paths: #1893 fixed indexed multi-term +paging, #1903 replaced fragmented lazy join demand after churn, and #1904 +defined the custom-collation full-source boundary. None removed the +cross-alias full-source branch exercised here. + +Before this change, the new `some`, `none`, joined-only, and to-one work cells +all requested a full root source. The anti-join Effect cell also emitted a +matched root that was absent from the independent result. After the change, +the focused owner passes all 86 tests. A controlled mutant that removes the +joined-demand gate starts three root pages before the child settles where the +oracle permits one; it also publishes an incorrect anti-join window. Restoring +the gate makes those cells pass. A 42-root fixture stops after fewer than ten +requests when the first roots satisfy the joined filter. + +## Lossless issue ledger + +The IDs follow the issue body (`I`) and author comment (`C`) in source order. +`fixed-now` refers to the accompanying implementation. `deferred` and +`design-decision` remain tracked by [issue #1898](https://github.com/TanStack/db/issues/1898). + +| ID | Disposition | Evidence, limit, or next step | +| --- | --- | --- | +| I01 | fixed-now | The original direct LEFT-join query requested an unbounded root snapshot. The oracle now requires a finite first page and checks the independent result. | +| I02 | fixed-now | A controlled unresolved child request plus a disabled-gate mutant proves root pagination can outrun child demand. | +| I03 | deferred | Specify `LoadSubsetOptions.relations`, including the superset rule for adapters that ignore a hint. No hint is emitted in this change. | +| I04 | design-decision | `every(p) = none(not p)` holds for a two-valued predicate and an empty relation. Define nullable expression semantics before making it an adapter law. | +| I05 | deferred | The compiler now permits local finite paging for one direct LEFT join. It does not extract AND conjuncts into remote relation hints or cover joined subqueries. | +| I06 | deferred | Once hints exist, propagate the same hint through initial, refill, repair, and tie requests; test request identity and acquisition release. | +| I07 | fixed-now | Root continuation waits for unsettled lazy demand or a joined subscription load. Settlement and status changes rerun the loader. | +| I08 | fixed-now | A joined-side delete now reruns ordered loading and refills both Collection and Effect windows. | +| I09 | deferred | Include future relation hints in exact demand identity so narrowed and unrestricted acquisitions cannot cover one another. | +| I10 | deferred | The reported honored-hint request and row counts are fixture measurements. Test a real hint-aware adapter after the API exists. | +| I11 | fixed-now | Without a relation hint, local evaluation reaches exact pages from a finite prefix; a 42-root fixture stops early. The author's precise prototype counts are not universal laws. | +| I12 | already-fixed | Eager local Collections already returned the correct result; the oracle retains that control. | +| I13 | deferred | This change proves a `some` child deletion refills the window. Add insert/delete histories for `some`, `none`, and nullable `every` semantics. | +| I14 | fixed-now | The controlled gate mutant overfetches before child settlement. The reported 40 ms and 40-row values are timing-specific observations. | +| I15 | design-decision | Decide whether a future relation hint applies to unlimited queries; this implementation only changes finite windows. | +| I16 | design-decision | The query-wide gate can delay an unrelated include. Measure dependency-specific latency before adding per-join state. | +| I17 | deferred | Joined subqueries and unsupported plans retain full-source fallback. Direct-join `OR`/`NOT` uses the same local prefix law but needs dedicated oracle cells; remote hint extraction must stay conservative. | +| I18 | deferred | Verify cursor and offset coverage when another query has installed root rows and when a backend narrows rows by relation. | +| C01 | fixed-now | A direct to-one PK equality join has the same full-source branch and now pages finitely. A distinct root-FK fixture remains useful. | +| C02 | design-decision | `some(conjunct)` is exact when the conjunct fails on a missing joined row; require a semantic proof and nullable tests before emitting the hint. | +| C03 | design-decision | `none(not conjunct)` for a conjunct that passes on a missing row requires at most one matching child. Multiple matches give a counterexample. | +| C04 | refuted | A sentinel `getKey({id: sentinel}) === sentinel` probe cannot establish uniqueness: a key function can return `tenant:id` for real rows while passing that probe. Use an explicit uniqueness contract. | +| C05 | deferred | The comment reports honored/ignored hints, skip, eager, and live coverage. This change covers local paging, eager control, and some live updates; the full hint matrix remains. | +| C06 | deferred | InstantDB's 30 s and 1.5 s measurements are external observations; no in-repository backend trace verifies them. | +| C07 | design-decision | A filtered prefix of `offset + limit` can cover a cursor window if offset bounds the count of passing rows before the cursor. Prove that premise for each adapter and document the strategy. | +| C08 | confirmed-open | A current-main probe with both children installed and child `loadSubset` returning without writes updated correctly after a root FK change. The exact failing precondition is unknown; a clarification request is posted on the issue. | + +Totals: 7 fixed-now, 1 already-fixed, 10 deferred, 6 design-decision, +1 refuted, and 1 confirmed-open; 26 source items accounted for. + +## Remaining design work + +The largest next step is a safe relation hint. It needs a syntax classifier, a +proof that hints only narrow to a superset of the local result, request plumbing +through tie and repair paths, and exact demand identity. The sentinel key probe +from the comment must not certify to-one joins. A second investigation should +reproduce the changed-FK case under the author's adapter behavior before +altering the lazy-join lifecycle. diff --git a/docs/contributing/oracle-reviews/issue-1912-eager-index-history.md b/docs/contributing/oracle-reviews/issue-1912-eager-index-history.md new file mode 100644 index 0000000000..e1172d5356 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-1912-eager-index-history.md @@ -0,0 +1,85 @@ +# Issue #1912 eager-index history oracle review + +## Reviewed state and claim + +- Base: `f09868ff` (`origin/main` at investigation). +- Reviewed executable head: `106409550d4d6fdc7721fbe1952a59e5891d1f29`. +- Owner: `packages/db/tests/change-event-history-oracle.test.ts`. + +Issue #1912 reports that a same-tick delete and reinsert can leave an eager +auto-index without the reinserted rows. That failure does not reproduce on the +reviewed `origin/main`. This change adds coverage for the affected Collection +mutation path. It does not change production code or claim to fix the reported +version. + +The oracle starts with two rows, builds an eager index through a live query, +deletes both rows, and reinserts both keys in the same turn. The driver runs +this prefix with BasicIndex and BTreeIndex. It also runs legal generated tails +with batched and sequential settlement. In the batched lane it compares public +rows and index equality buckets with an independent row Map immediately after +each mutation returns, without awaiting settlement between mutations. The +batched lane checks again after all transactions persist; the sequential lane +checks after each persisted mutation. At the final checkpoint, fresh indexed +`eq` queries must return the Map's full rows. + +## Generated grammar controls + +- **Reconstruction:** The fixed prefix represents the reported replacement + transition after index construction. The empty tail preserves that prefix. + Both execution modes are fixed cases as well as generated choices. The issue's + initial insert setup, batch `delete`, and string keys are outside this driver's + setup. A separate temporary probe of that exact setup passed on current main; + it is not a checked-in replay. +- **Ablation:** The fixed prefix guarantees the same-turn replacement path. + Without the batched mode, that path is absent. The sequential mode checks + settled intermediate states. Tail keys 3 and 4 admit new rows after the + replacement. `deleteWhenPresent` admits further delete/reinsert histories. + An empty tail checks the fixed prefix alone. Longer tails exercise repeated + changes across both equality buckets. +- **Range:** Initial keys are 1 and 2. Tails contain 0 through 12 actions on + keys 1 through 4. The indexed field has two stable values, `f1` and `f2`. + Both index types run for every generated history; each history selects one + settlement mode. The four fixed cases cross both index types and modes. +- **Exclusion:** The presence grammar inserts an absent key and updates or + deletes a present key. It cannot insert a present key or update or delete an + absent key. The existing bounded grammar has a separate missing-key check. + +## ORC-001 through ORC-011 + +| Requirement | Outcome | +| --- | --- | +| ORC-001: authority and limits | Pass. Issue #1912 supplies the expected public `eq` result after replacement. The existing Collection change-event contract supplies settled row and mirror agreement. The executable opening and coverage map limit this lane to local-only persistence, returned-mutation optimistic state, and settled checkpoints. No bug on current main is claimed. | +| ORC-002: independent judgment | Pass. `expectedRowsAfter` applies insert, update, and delete to a plain Map. Expected buckets filter that Map by the fixture's `fileId`. The model imports no index classifier or mutation state machine. | +| ORC-003: visible responsibilities | Pass. The executable opening states the contract. The Map model, presence grammar, Collection driver, and comparisons remain in the owner file. | +| ORC-004: grammar controls | Pass within the bounded domain above. The fixed prefix reconstructs the relevant replacement transition. The controls name the contribution of each axis, marginal ranges, and forbidden operations. | +| ORC-005: path and observation | Pass. A query builds the eager index before public Collection mutations. The driver verifies the chosen index constructor. It observes public rows and equality buckets after each returned batched mutation, settled public rows and change-message mirror rows, and fresh public query rows. The query check verifies that the named index's `eq` lookup ran. The row recorder retains full values but deliberately does not assert change-message multiplicity or batch shape. | +| ORC-006: checker calibration | Pass for the named comparisons. A temporary production mutant that skipped index updates reached the fixed batched checkpoint; its index count was 1 against 2 model rows and failed by assertion. A temporary mutant that made index add, remove, and update no-ops survived final-only checks when the replacement restored the original keys, then failed the new synchronous prefix count immediately after the first delete (`2` versus `1`). A separate wrong-result control made the named index lookup return no keys after the direct checks; the fresh public query comparison failed with `[]` versus the expected `f1` row. These were assertion failures, not timeouts or setup failures. All temporary faults were removed. The executable checker also rejects a removed live key. These controls demonstrate sensitivity to their specific faults, not detection of a current production bug. | +| ORC-007: campaigns and replay | Pass. The same generated property runs with fixed seed 1912 and with an unseeded random campaign, 35 runs each. A temporary wrong-result control at the final query produced a generated failure with seed `584849211` and path `0:0`, shrinking once to the fixed four-operation batched prefix. Direct replay with `TANSTACK_DB_ORACLE_PROPERTY=collection-state.eager-index-history`, that seed, and that path reproduced the same public query mismatch with zero further shrinks. The same requested replay passed after the control was removed. This is a replay of an injected failure, not a captured failure on main. | +| ORC-008: model minimality | Pass. The tail grammar retains only key presence because that decides whether insert, update, or delete is legal. The expected row Map retains values because row and bucket comparisons distinguish them. No index lifecycle state was added to the model. | +| ORC-009: vocabulary mapping | Pass. `present` means keys eligible for update or delete. The row Map represents expected public Collection rows at the returned-mutation and settlement checkpoints; it does not represent the production index or optimistic transaction machinery. | +| ORC-010: failure and cleanup | Pass. Query cleanup and Collection cleanup run after assertions. When an assertion and cleanup both fail, `AggregateError` preserves the primary failure as `cause` and retains cleanup errors separately. | +| ORC-011: second formulation | Not applicable. Review found no plausible semantic fault shared by the Map model and production that requires a second reference formulation. Direct index buckets and fresh queries are complementary observations, not independent reference models. | + +This record supplies ORC-012 evidence for executable head `10640955`. The +versioned record is being updated after that code commit; no production code +changed in this pull request. + +## Verification and remaining scope + +The change-event owner and replay tests passed: 403 tests. CI exposed type +inference errors in the owner file at the first reviewed commit. The follow-up +commit fixed the generic key type. The owner passed 376 focused tests after the +synchronous-prefix check was added. Changed-file ESLint, Prettier, and +`git diff --check` passed. The package TypeScript check reported no owner-file +errors; it still reports unresolved `@tanstack/db` imports in two conformance +files. The direct failure replay reproduced the injected query mismatch at its +public checkpoint, and passed after the control was removed. + +The oracle does not inspect an index during a change callback. An index error +visible only inside a callback can still escape the returned-mutation and +settled checks. It does not cover adapter cancellation, non-local-only +persistence, or the issue's original seed-by-insert and string-key setup. +Generated tails are structurally reachable, but 35 random runs do not guarantee +any particular adjacent tail. The change-event history owner retains the +callback boundary. The Collection lifecycle owners in the coverage map retain +adapter paths. diff --git a/docs/contributing/oracle-reviews/issue-1935-direct-write-refetch.md b/docs/contributing/oracle-reviews/issue-1935-direct-write-refetch.md new file mode 100644 index 0000000000..2d92361d34 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-1935-direct-write-refetch.md @@ -0,0 +1,199 @@ +# Issue #1935: direct writes and on-demand refetches + +Reviewed implementation commit: `41b9043a76be8ce65fd3287009ae721201383c33`. The regression was +introduced after the prior public contract recorded at `fdcb078a0^`. + +The pre-#1826 guide promised that direct writes “Do NOT trigger automatic query +refetches”; the `QueryCollectionUtils` comments made the same promise for each +`writeX` method. The report's PocketBase adapter uses one subscription per +on-demand subset and applies delivered events through direct writes. The user +confirmed that these utilities are sharp knives: they may leave a scoped Query +cache inconsistent. Exact scoped cache reconstruction is not the direct-write +contract. A later Query result may replace a direct write, and applications +that need exact server authority may explicitly refetch or use a sync engine. + +The original implementation refetched all ten active equality subsets after +one `writeUpsert`. The ownership oracle's real `queryFn` recorder rejected that +implementation at the post-write request checkpoint: expected zero new calls, +observed ten. With this repair, the same check passes after each accepted +`writeUpsert`, `writeUpdate`, `writeDelete`, `writeInsert`, and mixed `writeBatch` +in the ten-subset fixture. An independent category filter computes expected +public rows after each action. The source path is `writeX` → accepted sync +commit → on-demand cache patch; the public observations are live-query rows +and `queryFn` calls. The law covers this bounded history and no automatic +request caused by those writes. It does not prove exact cache membership, +ordering, window replacement, or every lifecycle interleaving. + +The old load-lifecycle case required a post-write fetch to become authoritative. +That was the removed policy, so the case was retired. Adjacent cases still +require explicit refetch to wait for deferred application and reject after final +acquisition release. The ownership oracle retains a direct-write race with an +older in-flight fetch and checks that a later explicit refetch can replace it. +The original production implementation is the hostile control for the no-fetch +comparison; it failed by assertion, not timeout or setup failure. + +| Guide requirement | Review outcome | +| --- | --- | +| ORC-001 | Prior published no-refetch contract and the sharp-knife limit are stated above. | +| ORC-002 | Expected rows come from a standalone category filter; expected request count comes from the direct-write contract, not a Query cache classifier. | +| ORC-003 | The ownership oracle states its law and limits beside the witness, then places reference rows, real production calls, and post-acceptance comparisons together. The load-lifecycle oracle states its separate explicit-refetch law. | +| ORC-004 | Not triggered: this is a bounded fixed history, not a generated-history coverage claim. | +| ORC-005 | The witness calls public `writeX` utilities and compares public live-query rows and provider calls after each accepted write. | +| ORC-006 | The pre-repair all-active-refetch implementation failed the zero-request assertion with ten calls. | +| ORC-007 | Not triggered: the changed witness is fixed, not an important generated property. Existing generated properties are unchanged. | +| ORC-008 | Not triggered: the changed witness does not alter a stateful reference model. The retired post-write generation was production policy, not retained model state. | +| ORC-009 | The witness uses the glossary's Collection, row, and sync-commit terms; its category filter is only local test data. | +| ORC-010 | No shrinking or capture applies. The witness registers Collection and QueryClient cleanup with the existing harness. | +| ORC-011 | No plausible shared semantic classifier is used to judge request count. Scoped-cache correctness remains a separate law, with owners named below. | +| ORC-012 | This record separates the proved request law from the open scoped-cache questions and records each applicable requirement. | +| ORC-013 | The ten active subsets reach the reported all-active premise. All five write forms reject a repair that suppresses requests only for upsert; the old implementation rejects the request boundary itself. The claim remains limited to this bounded history. | +| ORC-014 | The observed boundary is the real QueryClient and its supplied `queryFn`; this record makes no claim about a real network or PocketBase server. | + +The ownership oracle remains responsible for active/inactive and shared-owner +cache histories, including key-preserving edits, moves across scopes, and +observer retention. The cursor-pagination oracle owns a receiving witness for +limited or ordered windows. Those cache-shape risks are documented API limits; +their exact automatic repair is not a promised direct-write behavior. + +The report's four source-order items are preserved in the task's evaluation +ledger. The subscription proposal remains a separate API design decision. The +reported `setQueryData` workaround was not executed here; it remains an +external adapter observation, not evidence for cache safety. + +## Prep-PR review extension + +The independent review of `ebde2066d` found two reachable histories outside +the ten-subset witness. The follow-up implementation commit is +`aaca3ec0af4d3c5ac74c95c1b23942a562d593dc`. + +First, the documented `edges.map(edge => edge.node)` projection has no inverse +cache mapping. A direct write replaced `edges` with node objects. The new +ownership-oracle comparison failed on the envelope shape and production logged +`InvalidQueryResultError`. The repair updates a wrapped cache only when +`select` returns a direct array property by identity. The same oracle now +passes, while the new direct-property on-demand control preserves the patchable +case. A derived cache may be stale; it must remain valid input to `select`. + +Second, a direct write under a held `deferDataRefresh` barrier made one provider +request after barrier release. The new ownership-oracle assertion failed at +that checkpoint with two total calls instead of one. The repair prevents a +direct-cache notification from scheduling the deferred refetch. The same +barrier history now passes. The adjacent replacement-Query case still checks +the cache-clear path through an explicit refetch. + +For ORC-001 through ORC-006, the added laws are the existing no-refetch promise +at the barrier checkpoint and the documented read-only nature of derived +`select` projections. The authored envelope supplies the independent expected +shape. The production driver uses a real QueryClient and public direct write. +The assertions observe cache shape, public Collection row, and provider calls. +Both original faults failed at their intended assertions; the cache fault also +caused a logged application error. ORC-007 and ORC-008 remain inapplicable +because these are fixed histories without model-state changes. ORC-009 and +ORC-010 follow the existing vocabulary and cleanup harness. For ORC-011 +through ORC-013, the directly selected wrapper control distinguishes safe +patching from the derived projection, and the held barrier distinguishes a +post-release request from the ordinary no-barrier case. The claim remains +bounded by these histories. Under ORC-014, `OfflineExecutor` supplies a real +`deferDataRefresh` barrier during reconnect replay, but no full +OfflineExecutor receiving witness ran. The offline-transactions owner retains +that integration check in the coverage map. + +## CodeRabbit review follow-up + +CodeRabbit review `5444119327` examined commit `c173d4bdc8e5fc2af360f65549ab362ddffd0db9`. +Its three comments are recorded in source order: + +| ID | Claim | Verdict and action | +| --- | --- | --- | +| CR1 | The changeset implies every active on-demand cache is patched. | Confirmed; fixed the release wording to exclude derived `select` projections. | +| CR2 | Direct inserts have no Query row owner and can remain after subset unload; add ownership or retention. | The observation is true. It is the accepted direct-write contract: a caller may surgically insert a Collection row without establishing Query subset membership. The new ownership-oracle witness keeps that row after its fetched peer retires and removes it by explicit `writeDelete`. Documentation now names the caller's retention responsibility. | +| CR3 | A direct write evicts an unobserved cache entry under the base prefix even if this Collection has not yet observed it; gate eviction on `ownedCacheQueries`. | The observation is true, but the proposed gate contradicts the documented query-key prefix convention and the existing inactive-sibling oracle. A matching-prefix cache entry can seed a later subset; keeping stale data would let it revive a deleted row. The documentation and PR description now say matching-prefix entries rather than collection-owned entries. | + +For CR2, the legal history loads one subset, accepts a direct insert, unloads +the subset, then explicitly deletes the inserted row. A standalone expected-key +set keeps the direct row and retires the fetched row. The real QueryClient and +public `writeInsert`/`writeDelete` path reaches all three public-row +checkpoints without a new provider call. A hostile mutant that assigned every +accepted direct key to the active Query owner failed at unload: it returned no +rows where the reference required the direct row. The restored implementation +passed. This bounded witness does not settle the lifetime of unowned direct +rows across persisted restore or every later Query result. + +For CR3, three existing ownership-oracle cases establish that direct writes +evict inactive matching-prefix entries, including pre-sync and restart seeds. +They passed on the reviewed commit. CodeRabbit's proposed `ownedCacheQueries` +gate was run as a hostile mutant; all three failed at their cache observation +because the stale sibling remained. Restoring the implementation made them +pass. The check concerns Query cache entries under the Collection's declared +prefix, not arbitrary unrelated keys elsewhere in the QueryClient. A later +remount that proves the stale row would become public is a useful further +witness for the ownership oracle; the current tests assert the cache boundary. + +ORC-001 rests on the prior direct-write promise and the documented query-key +prefix convention. ORC-002 uses an independent key set for CR2, while CR3's +expected cache absence follows from the prefix contract and the authored +stale response. ORC-003 places the direct-insert law, history, path, and +checkpoints beside its executable witness. ORC-004, ORC-007, and ORC-008 do +not apply: these are fixed histories and no reference-model state changed. +ORC-005 reaches real QueryClient and public Collection observations. ORC-006 +has the two assertion-failing hostile mutants described above. ORC-009 uses +the glossary's Collection, row, and subset-owner terms. ORC-010 uses the +existing aggregate cleanup harness. ORC-011 adds no second formulation: +neither review claim named a fault the independent key set or authored cache +response could share with production. ORC-012 limits the claims to the named +histories and records the remount gap in the coverage map. ORC-013's nearby +distinction is fetched versus directly inserted row for CR2, and active versus +inactive matching-prefix entries for CR3. ORC-014 makes no cross-provider +claim; the boundary is the real QueryClient with an authored query function. + +## Retained in-flight Query follow-up + +CodeRabbit review `5445111669` examined `afb8ac348c29983dc20e38375f4bb0ebe0f3c54f`. +Its one inline finding was correct: persisted retention removed a fetch-start +position while Query Core kept an empty-cache request in flight. A later subset +lease reused that Query, so a direct delete followed by explicit refetch also +reused the request that began before the delete. The new ownership-oracle +witness failed on the reviewed implementation at the refetch-settlement +checkpoint: expected two provider requests, observed one. The public row +assertion also requires the accepted delete to survive settlement. + +The repair keeps the fetch-start position while the detached Query still +fetches. Cache settlement or removal retires it when no observer owns that +subset. The same retained-refetch witness passes after repair. Its nearby +no-later-write history expects only one request, rejecting an implementation +that cancels every retained fetch. A second witness observes the position at +unload and after detached settlement or cache removal. Removing the terminal +cleanup was a hostile mutant: the settlement branch failed by assertion with +one position remaining, not by timeout or setup failure. The original deletion +is the hostile control for the post-write request comparison. + +The law is the documented explicit-refetch contract: after an accepted direct +write, an older in-flight request cannot satisfy a refetch that asks the server +to reestablish exact results. The independent reference uses request order, +not Query Core's cache-state or cancellation branches. Legal controlled +histories here retain an empty-cache fetch across subset unload and +reacquisition, with and without a later direct delete. The production driver +uses the real QueryClient, on-demand Collection, persisted-retention metadata, +and public `writeDelete` and `utils.refetch`. It compares provider calls and +the public row at refetch settlement. The retirement witness observes the +internal position only for the separate space law. These finite histories do +not cover arbitrary custom hashes, every cache-removal schedule, or native +SQLite persistence timing; the ownership oracle retains those Query-boundary +cases in the coverage map. + +| Guide requirement | Review outcome | +| --- | --- | +| ORC-001 | The Query Collection guide promises a post-write explicit refetch despite an older in-flight request; the controlled history and external-provider limit are stated above. | +| ORC-002 | Request order and the accepted direct delete determine the expected requests and public row independently of Query Core's fetch-state logic. The retirement count is a separate resource law. | +| ORC-003 | The witness comments state law, limits, reference, legal history, real driver, observations, and settlement checkpoints beside the code. | +| ORC-004 | Not triggered: the new histories are fixed controls, not a generated grammar coverage claim. | +| ORC-005 | The tests call the real on-demand Collection and QueryClient; assertions compare provider requests and public rows at refetch settlement, plus position count at unload and terminal cuts. | +| ORC-006 | The reviewed implementation failed the two-request assertion. A terminal-cleanup mutant failed the zero-position assertion. Both reached the intended checkpoint. | +| ORC-007 | Not triggered: no important generated property changed. | +| ORC-008 | Not triggered: the independent request-order rule introduces no stateful reference model. | +| ORC-009 | The witness uses the glossary's Collection, observer lease, request, settlement, and generation terms; request order is the reference abstraction for the production fetch-start position. | +| ORC-010 | Controlled promises are resolved in the test, and the existing harness cleans up the Collection and QueryClient. No shrinking or capture applies. | +| ORC-011 | No shared semantic classifier is used to compute the request-order reference. A second formulation is not required for this bounded Query Core boundary. | +| ORC-012 | This versioned record ties the repair to the reviewed commit and states both enforced and open boundaries. | +| ORC-013 | A later direct delete requires two requests; no later write permits one. The original implementation fails the triggering case. | +| ORC-014 | The claim is limited to real Query Core with controlled persisted metadata; it does not claim native SQLite timing. | diff --git a/docs/contributing/oracle-reviews/issue-1939-offline-admission.md b/docs/contributing/oracle-reviews/issue-1939-offline-admission.md new file mode 100644 index 0000000000..2b98c7aba9 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-1939-offline-admission.md @@ -0,0 +1,128 @@ +# Issue #1939 offline admission review + +## Reviewed state and contract + +- Source review: [issue #1939](https://github.com/TanStack/db/issues/1939). +- Base: `c0d123b86aeb2942d0a2a70a050bf10a3b3dda48` (`origin/main`). +- Reviewed executable head: `f1ae370853b3d3e0f5e0e770ef4dbb5866239b81`. +- Owner: `packages/offline-transactions/tests/leadership-replay.property.test.ts`. + +An action selected for offline execution must not fulfill without either an +admitted outbox record or a provider call. This repair chooses rejection and +rollback when leadership is lost before admission. A later action invocation +while offline execution is disabled selects the online-only path instead. +The manual offline transaction path uses the same admission guard. + +## Lossless issue review + +| ID | Source claim | Verdict and action | Durable value | +| --- | --- | --- | --- | +| R1 | Leadership loss before admission can fulfill with no outbox record or provider call and an absent Collection row. | Confirmed on base; fixed now. | Admission safety relation in the leadership oracle. | +| R2 | `onMutate` can revoke leadership after offline selection; the old guard calls `resolveTransaction()` after an `await`. | Confirmed by controlled path and source; fixed now. | Synchronous and microtask loss schedules retained in the oracle. | +| R3 | Choose rejection/rollback or durable handoff, checking public settlement. | Rejection/rollback chosen and verified; fixed now. | `NonRetriableError` contract and offline guide. | +| R4 | Already durable replay coverage misses selection-to-admission loss; extend the primary oracle, prove RED/GREEN, and record scope. | Confirmed gap; fixed now. | Bounded action matrix, manual path, and coverage map. | +| R5 | The guide's online-only statement applies to actions starting disabled, not an action already selected for offline execution. | Confirmed; fixed now. | Corrected path-selection and pre-admission failure wording. | +| R6 | PR #1938 changed documentation only; RFC #1659 owns broader admission policy. | Confirmed provenance; deferred context. | Coverage map retains the broader boundary and needed witnesses. | + +The raw issue contains these six claims and no separate comments at review. +`git show --stat c0d123b8` confirms that PR #1938 did not change production +code. The issue's proposed durable handoff remains a valid alternative design, +but the selected rejection policy needs no new queue or ownership state. + +## RED, GREEN, and scope + +The issue's original action history failed before the fix with +`['fulfilled', 0, 0]`: settled outcome, durable outbox count, provider-call +count. The retained-leader and before-invocation controls passed. + +After adding the microtask and manual paths, a temporary restoration of the +original guard caused three assertion failures at the intended checkpoints: +loss during `onMutate`, loss just after `onMutate`, and manual loss before +`commit()`. Two adjacent action controls passed. These are assertion kills, +not timeouts, setup failures, or unreached paths. The original guard was +removed again. All five admission cases then passed, as did the full package +suite: 207 tests in 17 files. Package typecheck, changed-file Prettier, and +`git diff --check` passed against the reviewed executable head. The commit +hook ran ESLint on the changed TypeScript files. + +The bounded action grammar has four schedules: retained leadership, loss +before invocation, loss during `onMutate`, and microtask loss just afterward. +It uses one Collection row and an immediate fake store. The direct manual +history loses leadership after optimistic mutation and before `commit()`. +The driver waits for public settlement or a completed outbox write, then +compares promise outcome, durable records, provider calls, and public row. +The loss cases require a `NonRetriableError` rejection, no durable record or +provider call, and rollback. The controls require provider execution and a +fulfilled transaction. Removing either loss schedule would let a check placed +only at the other boundary survive. An asynchronous `onMutate` return is +excluded by the public API; neither this fixture nor the fake store models +native storage, leader regain, or a write held across leadership loss. + +## Oracle guide audit (ORC-001 through ORC-012) + +| Requirement | Outcome | +| --- | --- | +| ORC-001 authority and limits | Pass. Issue #1939 states the no-false-success law; the offline guide and transaction settlement owner state promise and rollback behavior. The bounded limits are above and in the coverage map. | +| ORC-002 independent judgment | Pass. Expected results come from settlement, durability, and leadership laws. The test does not import the admission classifier to compute them. | +| ORC-003 visible responsibilities | Pass. The owner header states contract and limits; the local relation, four action histories plus manual history, real entry points, and public comparisons are together. | +| ORC-004 generated grammar controls | Not triggered: this addition is bounded enumeration, not a generated-history property. Reconstruction, ablation, range, and exclusion for its finite grammar are stated above. Existing generated properties are unchanged. | +| ORC-005 path and observation | Pass. `createOfflineAction()` and `createOfflineTransaction()` run through `persistTransaction()`. The original mutant reached the public settlement and outbox/provider comparisons. | +| ORC-006 checker calibration | Pass. The original guard was a plausible wrong design and produced three assertion failures while controls passed. | +| ORC-007 fixed/random replay | Not triggered: the added fixed matrix is not an important generated property. Existing leadership properties retain their fixed/random campaigns and replay interface. | +| ORC-008 stateful-model minimality | Not triggered: the new relation adds no reference-model state. | +| ORC-009 vocabulary mapping | Pass. The test-only `loss` axis names callback timing. `onMutate`, admission, outbox, leadership, and settlement retain production meanings. | +| ORC-010 failure and cleanup | Pass. `atOracleCheckpoint()` bounds hangs. `cleanupOfflineOracle()` retains primary failures and reports cleanup separately; the mutant failed at assertions without cleanup replacing them. | +| ORC-011 second formulation | Not triggered: no shared semantic fault requiring a second reference formulation was identified. The manual API is an adjacent production path, not a copied reference model. | +| ORC-012 review evidence | This record ties the audit and RED/GREEN evidence to the exact executable commit above. It was added after that code commit; no executable file changed afterward. | + +The repair covers these action and manual selection-to-admission histories. +The leadership oracle still needs held outbox writes crossed with leadership +loss and regain, with settlement checked after the write. The IndexedDB +write-settlement owner needs a browser-host composition of that timing with +the adapter's transaction-completion boundary. The coverage map names both +owners. + +## PR #1966 follow-up: admission and provider checkpoints + +- External review: two test-assertion findings supplied for PR #1966. +- Starting head: `bf742b1b7`. +- Reviewed executable head after repair: `2579ac0acf70465a3902a8317ff241129b9b550e`. +- Owner: `packages/offline-transactions/tests/leadership-replay.property.test.ts`. + +| ID | Review claim | Verdict and action | Durable value | +| --- | --- | --- | --- | +| F0 | The production rejection and rollback path is sound. | Confirmed; already fixed before this review. The original-guard mutant and five admission cases are recorded above. | Keep the public settlement, outbox, provider-call, and optimistic-row checks in the leadership oracle. | +| F1 | The retained-leader provider-call count can run after storage becomes visible but before `executor.execute()` starts. | Confirmed; fixed in the reviewed executable head. | The oracle now separates the stored-record checkpoint from transaction settlement. | +| F2 | Comparing the manual `commit()` and `isPersisted.promise` errors by identity overconstrains the contract; check only their class. | Refuted as a test flaw; retain the identity assertion. | `packages/db/tests/transactions.test.ts` explicitly owns the same-instance error law for the underlying transaction. The offline manual path checks that its wrapper preserves it. | + +A controlled storage wrapper wrote the retained-leader record, signaled that it +was visible, and held its `set()` completion. On the starting code, the old +provider-count assertion failed at its original checkpoint: expected one call, +observed zero. This was an assertion failure, not a timeout or setup failure. +After the repair, the same held-write case observed a pending caller, one stored +record, and zero provider calls. It then released the write, awaited public +settlement, and observed one provider call and fulfillment. Clearing the +provider-call recorder after settlement made the moved assertion fail, proving +that the later check still rejects a missing call. The recorder corruption +was removed; the controlled hold remains in the oracle. + +The `commit()`/`isPersisted.promise` identity law is explicit in +`packages/db/tests/transactions.test.ts` and in the core transaction's original +error rethrow. The offline wrapper rethrows the same error. A temporary wrapper +that threw a fresh `NonRetriableError` with the same message made the manual +oracle's identity assertion fail at the intended checkpoint. Replacing that +assertion with the proposed class-only check let the mutant pass. Both temporary +changes were removed; the core identity test passed on this branch. The review +correctly spotted a dependence on identity, but that dependence protects an +established behavior. + +The changed executable owner passed its focused held-write case and the full +offline package suite: 207 tests in 17 files. Package typecheck, changed-file +Prettier, `git diff --check`, and the commit hook's ESLint task passed. The +addition retains the admission law and bounded leadership-loss scope. In particular, +ORC-005 now names the stored-record and settled-call checkpoints separately; +ORC-006 has the controlled old-order failure and missing-call control; +ORC-010 releases the held write during cleanup; ORC-012 records the exact +executable head. The new hold keeps leadership throughout the write. Leadership +loss or regain across a held write and the IndexedDB browser composition remain +with the owners in the coverage map; this review does not close those cells. diff --git a/docs/contributing/oracle-reviews/issue-1945-invalid-comparison.md b/docs/contributing/oracle-reviews/issue-1945-invalid-comparison.md new file mode 100644 index 0000000000..745bebd275 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-1945-invalid-comparison.md @@ -0,0 +1,107 @@ +# Invalid index comparison review + +Production/test head: the commit that adds this record. +This record is a documentation-only follow-up to that head. + +## Law and boundary + +A custom index comparator must return a number that is not `NaN`. A comparator +that returns `NaN`, a boolean, or any non-number cannot order the index's +storage. Per `AGENTS.md` ("Separate Valid Edge Cases from Contract +Contradictions"), a broken comparator is a caller programming error, so the +index crashes: the operation that receives an invalid result throws. `NaN` +values ordered by the default comparator remain valid and supported. + +`makeCheckedComparator` in `packages/db/src/utils/comparison.ts` wraps a +user-supplied `compareFn` and throws a `TypeError` on any result that is not a +non-`NaN` number. `BaseIndex` builds every index's comparator through it, so +both `BasicIndex` and `BTreeIndex` share one enforcement point. Because the +comparator now guarantees a valid order, the vendored B+ tree no longer needs +its own `NaN`/invalid-result branch, and the post-split child placement follows +the already-known insertion index instead of a redundant comparison. Net +production code shrinks. + +The index owner (`packages/db/tests/index-update.property.test.ts`) states the +refinement law: for each index type, comparator, accepted numeric prefix, and +probe operation, the operation throws exactly when it receives an invalid +comparison result, and never when every comparison it makes is valid. The +model counts invalid results independently of the production check. An empty +prefix is included: the first write compares nothing and must succeed, and the +next comparing operation must throw. The BTree Map owner +(`packages/db/tests/btree-map-oracle.test.ts`) keeps two split histories that +prove split placement does not consult a post-mutation comparison. + +A rejected index `add` or `remove` throws before it changes the index, so the +index still refines its accepted rows. Index `update` and `build` are not +atomic. `createIndex` builds before it registers the index, so a failed build +never reaches a collection. + +### Collection boundary + +An external review showed that the first version of this change was worse than +`main` at the Collection boundary. The Collection writes rows before it +updates indexes and publishes change events. An index throw between those +steps left rows that subscribers were never told about, while the collection +stayed usable and reported the write as failed. That is the partial success +`AGENTS.md` forbids. `main` stored and published those rows; only its index +order was wrong. + +The repair crashes the collection. `CollectionIndexesManager.updateIndexes` +calls the existing `lifecycle.markError` and rethrows. The collection status +becomes `error`, and the next mutation throws `CollectionInErrorStateError`. +No recovery path, generation, or rollback is added. The index owner states +this law for both index types and both write paths (optimistic insert and sync +commit). + +Remaining limits: + +- A sync source can still begin and commit writes on a collection in `error` + state. Those rows are stored but not published. This is existing + `markError` behavior for any crashed collection, not specific to comparators. +- A user who relied on `main`'s silently misordered index now gets a crashed + collection at the first invalid comparison. The changeset states this. +- Comparator transitivity is not validated. + +## Conformance evidence + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | The index comparator contract and `AGENTS.md` fail-fast policy supply the law; the limits above bound it. | +| ORC-002 | The refinement model counts invalid comparator results with its own recording wrapper. It does not call `makeCheckedComparator` or any production classifier. | +| ORC-003 | The opening comment states the law and limits; the probe/comparator/prefix tables supply inputs; public `add`/`update`/`lookup`/`take`/`build` drive production; the recording step judges throw-vs-success. | +| ORC-004 | Bounded enumeration crosses two index types, three comparators, seven probe operations, and seven prefixes including the empty prefix. The empty prefix reconstructs the first-write-never-compares case. The `signed infinity` comparator is the marginal valid case; it must never throw. | +| ORC-005 | Public index entry points execute, and the Collection cells drive `collection.insert` and a sync `commit`. Observations: throw vs. success, accepted-row refinement after a rejected add/remove, and collection status plus the next mutation after a rejected write. | +| ORC-006 | On unmodified `origin/main`, the BasicIndex law and the reported-string case fail (85 cases). Mutants that leave either index's comparator unchecked, that reject only `NaN` (accepting booleans), or that also reject infinite results each fail at the intended checkpoint. All were classified as assertion failures. | +| ORC-007 | These are bounded enumerations, not an important generated property; no fixed/random campaign parity is required. The package's existing generated index campaign is unchanged. | +| ORC-008 | No stateful reference model is introduced; the check recomputes throw-vs-success per operation. | +| ORC-009 | Accepted rows and index operations retain existing vocabulary. | +| ORC-010 | Checks are synchronous and retain no resources. | +| ORC-011 | Both index types run through the same law, distinguishing an index-type-specific fault from a shared one. | +| ORC-012 | This record names the reviewed head and each applicable outcome. It makes no universal bug-class closure claim; the sync-writes-after-crash limit above stays open. | + +## Mutant kills (recorded at review time) + +Run: `vitest run tests/index-update.property.test.ts -t "invalid comparator results|NaN keys|reported string"`. + +| Mutant | Result | +| --- | --- | +| `origin/main` production (no check) | 85 assertion failures (all BasicIndex; BTreeIndex passed the law but not the error class). | +| BTreeIndex comparator unchecked | 84 assertion failures. | +| BasicIndex comparator unchecked | 84 assertion failures. | +| Check rejects only `NaN`, accepts booleans | 84 assertion failures. | +| Check also rejects `±Infinity` | 82 assertion failures (the `signed infinity` valid control now throws). | +| Shipped fix | 0 failures. | + +Collection and atomicity cells, run with `-t "invalid comparator|rejected remove"`: + +| Mutant | Result | +| --- | --- | +| PR head before this repair | 12 assertion failures: 4 Collection cells (status stayed `ready`), 6 BasicIndex add cells, 2 remove cells. | +| `updateIndexes` rethrows without `markError` | 4 assertion failures (Collection cells). | +| BasicIndex add writes `valueMap` before its sorted search | 6 assertion failures. | +| BTreeIndex remove changes maps before the tree delete | 1 assertion failure. | +| Shipped fix | 0 failures. | + +The full `@tanstack/db` suite passes: 223 files, 7,429 tests, no type errors. +Production code is net negative against the merge base. Tests and this record +grow separately. diff --git a/docs/contributing/oracle-reviews/issue-1948-keyed-consolidation.md b/docs/contributing/oracle-reviews/issue-1948-keyed-consolidation.md new file mode 100644 index 0000000000..7d250c1bad --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-1948-keyed-consolidation.md @@ -0,0 +1,123 @@ +# Issue #1948: keyed MultiSet identity + +Reviewed executable revision: `0641a0e2c8a0b6c7351162f667a2eca7662ca4cd`. +The unchanged production baseline was `c879ba6d855914e5c0cac49af7a007eb5cabe7e4`. +The baseline code in `packages/db-ivm/src/{multiset,utils}.ts` is also identical +to the local main checkout at `c0d123b8`, used for the isolated object probe. + +## Contract and boundary + +`MultiSet.consolidate()` promises that keyed records merge only when their +keys and values have the documented identity. String and numeric keys are +different. Primitive values compare by value; objects and functions compare +by reference, and distinct symbols remain distinct. A two-element value is a +join tuple whose elements follow that leaf rule. After `consolidate()` returns, +the complete output must retain each nonzero identity with its summed weight +and first record. The input must remain unchanged. The oracle does not assert +output order. + +This record closes the reported direct `MultiSet` collisions for the oracle's +bounded key/value grammar. It does not establish which `@tanstack/db` queries +produce primitive keyed relation values or mixed-type source keys. A live-query +production witness is still needed for those application paths; the coverage +map names that gap. + +## RED and GREEN evidence + +The old oracle excluded cross-type, delimiter, symbol, and function cases, so +it remained green on the faulty implementation. The expanded oracle uses the +same independent reference model for eight pinned collision cases and for a +generated collision branch. Before the production change: + +- Eight pinned cases failed at multiplicity or missing-identity assertions. +- Both generated campaigns failed. The fixed seed `2026929` shrank to + `path: "1:0"`, numeric and string keys with opposite weights. Direct replay + reached the same missing-identity assertion. +- An isolated process probe with a direct object and an object tuple under + a delimiter-bearing key returned zero records. The two distinct records + have weights `1` and `-1` under the contract. + +On `0641a0e2`, the oracle passes all 22 tests. The exact fixed seed and path +replay passes. The same object/tuple process probe returns two records with +weights `[1, -1]`. All 649 `@tanstack/db-ivm` tests pass, package TypeScript +typechecking passes, Prettier passes, and `git diff --check` passes. + +The original production code is the hostile wrong design for this oracle: +`str_${String(value)}` erases primitive type and reference identity, and bare +`|` joins erase component boundaries. Its failures reached the intended +public-result comparison. The repair type-tags primitive identities, uses a +per-consolidation reference map, distinguishes a leaf from a tuple, and +length-prefixes components. Positive controls keep same-symbol, +same-function, signed-zero-key, and NaN-value merging intact. + +Production code changes by +10 net lines. Test code grows to make the new +grammar, named cases, and controls visible. No elapsed-time performance claim +is made for the repaired hot path. + +## Oracle guide audit + +| Requirement | Outcome at reviewed revision | +| --- | --- | +| ORC-001 | The oracle's opening contract cites `MultiSet.consolidate()` and limits the result to its direct API and bounded grammar. | +| ORC-002 | The expected identity uses a type-tagged structured key and a local reference map. It does not call production `getStringId` or hash. | +| ORC-003 | Contract, model, history grammar, production call, and refinement check remain separate in the oracle file. | +| ORC-004 | Eight named cases reconstruct the reported regimes. The collision branch varies case and nonzero sign/magnitude; ordinary keyed histories vary record count and values. The fallback branch deliberately retains its original structural domain. A non-keyed record makes the keyed rule inapplicable. | +| ORC-005 | The driver calls `new MultiSet(records).consolidate().getInner()` and compares every output identity, weight, and retained first record after return. The fixed campaign's reach witness covers every grammar mode. | +| ORC-006 | Unchanged production failed at the intended assertions; the object/tuple process probe also failed before the repair and passed after it. | +| ORC-007 | The same property, grammar, check, and 300-run budget execute with fixed seed `2026929` and a seedless random campaign. Seed-plus-path replay was checked RED and GREEN. No `fc.commands` are used. | +| ORC-008 | Not applicable: the reference is stateless recomputation. | +| ORC-009 | `keyedCollision` is documented as a grammar branch for the same keyed production path; `caseName` is only a replay label. | +| ORC-010 | No external resources are acquired. Shrinking and direct replay preserve the missing-identity failure at the result checkpoint. | +| ORC-011 | No separate shared semantic fault was named. The reference's structured key differs from production's length-prefixed composition, and the positive controls challenge adjacent identity equivalences. | +| ORC-012 | This versioned record identifies the exact executable revision, RED/GREEN receipts, limits, and the outcomes above. | + +## PR #1967 external-review follow-up + +Reviewed executable revision: `b39e162f82bfc8b228841970b0c31bd5624b508f`. +The review targeted `bd0a95eaa39c5b99ae420752de3b5f100b4cd183`. +The production encoder did not change in this follow-up. + +A new pinned witness uses `NaN`, `Infinity`, and `-Infinity` as numeric keys. +Before the model repair, the witness failed at the multiplicity assertion. +`JSON.stringify` converted all three raw numeric keys to `null`, so the model +expected one group with weight 6. Production returned three distinct groups. +The repaired model compares keys and leaves directly with SameValueZero, then +compares the two elements of join tuples. It does not serialize keyed identity. +The keyed generator now includes all three non-finite keys. The unkeyed +structural fallback retains its former key and value domains. + +The same focused witness is GREEN after the model repair. All 23 consolidation +oracle tests, all 650 `@tanstack/db-ivm` tests, package typechecking, Prettier, +and `git diff --check` pass. The full package run disabled coverage output. +The old production files from `c879ba6d` were run with the new oracle in an +isolated directory. The numeric/string-key witness failed at the public +multiplicity comparison: production returned weight 2 where the model required +weight 1. A bigint/number value witness failed at the missing-identity +comparison. An initial isolated run stopped during TypeScript configuration +setup; that run is not counted as a mutant kill. The reruns reached the +assertions. + +The external review also questioned the baseline label. `c879ba6d` was the +recorded unchanged production baseline. `c0d123b8` was its child and supplied +the isolated object probe. Both commits have identical `multiset.ts` and +`utils.ts` blobs. The coverage map's RED baseline is accurate. + +| Requirement | Follow-up outcome at reviewed revision | +| --- | --- | +| ORC-001 | The keyed law and direct-API limit remain in the opening contract. It now names non-finite keys in the grammar. | +| ORC-002 | Direct SameValueZero/reference equality supplies expected keyed groups. It does not use production's string encoding or `getStringId`. | +| ORC-003 | Contract, model, grammar, production call, and result comparison remain visible in one file. | +| ORC-004 | The pinned non-finite-key case reconstructs the false RED. The keyed generator adds those keys; the fallback generator keeps its prior domains. The earlier collision reconstruction and exclusions remain. | +| ORC-005 | The driver still calls `MultiSet.consolidate()` and compares every output group, weight, and retained first record after return. | +| ORC-006 | The unchanged pre-fix encoder fails the revised model at the intended public-result assertion. Setup failure was not credited. | +| ORC-007 | Fixed and seedless 300-run campaigns retain the same grammar and comparison within each revision. Seed-plus-path replay support remains. | +| ORC-008 | Not applicable: the model is stateless recomputation. | +| ORC-009 | The `keyedCollision` grammar branch and replay label retain their declared mapping. | +| ORC-010 | No external resources are acquired. The isolated baseline rerun reached the same result checkpoint. | +| ORC-011 | A shared type/text encoding concern now has a second formulation: direct SameValueZero/reference equality. The old encoder fails it. | +| ORC-012 | This append-only entry records the revised executable commit, false-RED repair, hostile baseline, GREEN checks, and limits. | + +Direct replay also used fixed seed `2026929` and shrink path `1:0` with the +revised model. The old encoder failed after one replay case at `missing +identities`, with numeric and string keys carrying opposite weights. The same +seed and path passed on `b39e162f`. diff --git a/docs/contributing/oracle-reviews/issue-1950-ssr-dehydration.md b/docs/contributing/oracle-reviews/issue-1950-ssr-dehydration.md new file mode 100644 index 0000000000..d08516bcc6 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-1950-ssr-dehydration.md @@ -0,0 +1,93 @@ +# Query DB SSR dehydration oracle review + +Reviewed implementation: `84c9557053f3d477b89492679da47c2dae190e5f`. +Original implementation: `c0d123b86aeb2942d0a2a70a050bf10a3b3dda48`. +The oracle was added to the original implementation before the production fix. +This record follows the reviewed implementation commit. + +## Claim and calibration + +A successful on-demand Query collection can put a plain cached row into a +TanStack Start SSR payload. Its request options may contain IR classes, an +`AbortSignal`, and a custom comparator. Query functions must still receive +those original request values. The serialized Query state must carry the row +and enumerable user metadata without carrying the request options. + +On the original implementation, the plain Query cache control passed. The +live query with two `where` clauses loaded and published row `1`, then failed +at `serializeRouterPayload(initial)` because Seroval rejected `Func and`. +The signal-only request loaded its row, then failed at the same stream +checkpoint because Seroval rejected `AbortSignal`. These were assertion +failures at the intended serialization boundary, not setup failures or +timeouts. Type checking passed. The original `query.test.ts` structured-clone +case also passed, which showed why it could not protect this boundary. + +After the fix, all four SSR oracle cases passed. The added ordered cursor case +checks that a function-valued comparator and IR cursor still reach the query +function while the stream completes. A separate Seroval serialize/deserialize +round trip followed by Query Core `hydrate` restores the row and enumerable +user metadata, without request options. The surrounding `query.test.ts`, +`load-subset-lifecycle-oracle.test.ts`, and SSR oracle run passed 421 runtime +tests with no type errors. The run used +`pnpm exec vitest run tests/query.test.ts tests/load-subset-lifecycle-oracle.test.ts tests/ssr-dehydration-oracle.test.ts --coverage.enabled=false --pool-options.threads.maxThreads=2` +from `packages/query-db-collection`. TypeScript and Prettier checks passed. + +## ORC-001 through ORC-011 + +| Requirement | Outcome | +| --- | --- | +| ORC-001 Contract authority and limits | The issue's expected SSR behavior and the Query collection documentation require successful cached rows to survive dehydration. The owner stops at QueryClient, Seroval, and Query Core hydration; it does not run the TanStack Start host or resume a browser Collection. | +| ORC-002 Independent judgment | The expected plain row and user metadata come from the fixture and public cache behavior. No production request classifier or serialization helper computes the expected result. | +| ORC-003 Distinguishable responsibilities | The oracle opening states the contract and fixed request grammar. The row and metadata expectations are the model. Real Collection, QueryClient, and Seroval calls are the driver. Stream completion, row equality, runtime request identity, and hydration checks are the refinement observations. | +| ORC-004 Generated-history controls | Not applicable. The owner has fixed cases and makes no generated-history coverage claim. | +| ORC-005 Production path and observation | The on-demand live query and direct `loadSubset` requests use real Query collections. `dehydrate` and `crossSerializeStream` reach the reported boundary. The check observes the row before serialization and completion or failure of the stream; the separate hydration check observes the restored row and user metadata. | +| ORC-006 Checker calibration | The original implementation is the wrong-design control. Its compound predicate and signal-only cases fail at the stream assertion, while the plain cache control passes. The outcome is an assertion kill at the intended checkpoint. | +| ORC-007 Fixed/random replay | Not applicable. There is no important generated property. | +| ORC-008 Stateful-model minimality | Not applicable. The model is a fixed successful cache entry, with no reference state machine. | +| ORC-009 Vocabulary mapping | The oracle introduces no model-only state or action names. Query, request options, dehydration, and hydration match the production and documentation terms. | +| ORC-010 Failure fidelity and cleanup | `checkWithCleanup` retains the primary stream failure and reports each cleanup failure separately through `AggregateError` when needed. Each case releases its Collection and QueryClient. | +| ORC-011 Independent second formulation | No shared semantic classifier between the expected plain row and the serializer was identified. The ordinary Query cache control follows a second loading path and separates a general Seroval failure from request-metadata failure. It does not claim an alternate full Start formulation. | + +The established boundary is: successful plain-row Query entries × the four +fixed request forms × Query collection → QueryClient dehydration → Seroval +stream, with Query Core hydration checked separately × row, runtime request +identity, stream completion, and restored row/user metadata. The original +compound and signal witnesses distinguish the repair from the original +design. The ordered cursor with a custom comparator is an adjacent witness +against removing only `where` and `signal`; that partial mutant was not run. + +The coverage map assigns exact Router/Start versions, hydration of the emitted +stream in a browser, Collection resume/refetch, and cancellation across SSR to +a future Start integration owner. Subset and pagination oracles own predicate, +order, and cursor meaning. These remaining paths are outside this bounded +serialization claim. + +## External review follow-up (pre-change HEAD `f7272189f3f4213ef063616f877752bfcc6c48ae`) + +The original oracle checked only that `crossSerializeStream` emitted a chunk. +It then used Seroval's synchronous `serialize`/`deserialize` for the exclusion +and hydration assertions. A temporary stream-only mutant added a serializable +`meta.loadSubsetOptions` marker to the streamed Query state while leaving the +state used for the sync round trip untouched. All four original oracle cases +passed. The revised oracle evaluates the emitted JavaScript chunks with the +Router's scoped reference header, checks that request options are absent from +the reconstructed payload, and hydrates Query Core from that payload. The same +mutant failed all four cases at the request-options exclusion assertion. The +unmutated implementation passed all four cases with no TypeScript errors. + +A second temporary mutant made the actual ordered-cursor request options +enumerable in the streamed state. Seroval 1.5.0 rejected an unsupported IR +object before emitting a chunk. The external review's assertion that this +specific enumerable comparator shape could still complete the stream was +incorrect. The stream-only serializable mutant still establishes the broader +checker gap independently of that example. + +The test package pins Seroval 1.5.0, matching the installed Router Core +1.159.4 in this lockfile. Issue #1950 reports Router Core 1.171.33 and Seroval +1.6.8. A temporary Seroval 1.6.7 probe reconstructed the same plain Query +payload and confirmed that its stream excludes non-enumerable request options +while retaining enumerable user metadata; Seroval 1.5.0 did likewise. Version +1.6.8 was unavailable from the configured npm registry during this review, so +neither that version nor the full reported TanStack Start host has been +certified. The coverage map assigns that witness to a future Start integration +owner. diff --git a/docs/contributing/oracle-reviews/issue-1975-scope-identity.md b/docs/contributing/oracle-reviews/issue-1975-scope-identity.md new file mode 100644 index 0000000000..f14e103432 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-1975-scope-identity.md @@ -0,0 +1,132 @@ +# Issue #1975: alias scope identity + +Reviewed head: `18abceee4` (`origin/main`) plus the working-tree repair on +branch `red/issue-1975-materialize-alias`. Owner: +`packages/db/tests/query/includes-oracle.property.test.ts` +(`includes alpha-renaming across sibling scopes`), with grammar, model, +driver, and observation in +`packages/db/tests/query/includes-scope-identity-oracle.ts`. Alias +validation is owned by `packages/db/tests/query/validate-aliases.test.ts`. + +## Defect and cause + +A live query returned no rows when an include reused an alias from a joined +`from()` subquery that received a pushed-down predicate. The optimizer copied, +wrapped, or collapsed `CollectionRef` objects with `new CollectionRef(...)`, +which mints a fresh `SourceId`. Before #1877 the compiler compiled the user's +original subquery and discarded the optimized copy, so the orphaned IDs were +never read. #1877 compiles the optimized copy when it differs. The compiler +then missed every orphaned `SourceId` and fell back to alias text. +`bindSourceInputs` had written every scope's input under its alias, so the +lookup returned a sibling scope's source with the same name. + +## Repair + +- `optimizer.ts`: the four copy, wrap, and collapse sites reuse the existing + `CollectionRef` (lines near 500, 848, 935, and 945). +- `compiler/index.ts` and `compiler/joins.ts`: `bindSourceInputs` maps + caller-supplied alias keys to `SourceId`s once and no longer publishes + alias keys; both source lookups read `allInputs[sourceId]` only. A lost + identity now raises `CollectionInputNotFoundError`. +- `compiler/index.ts` `validateQueryStructure`: no scope inside an include + can reuse an alias its ancestors can see, including a subquery alias. This + covers the include itself, its `unionAll()` branches, and its `from()` and + join subqueries, which can read the parent row through their callbacks. + The builder previously accepted these namings and returned wrong rows. + Top-level `from()` subqueries see no ancestor, so they may still reuse names. +- `compiler/index.ts` `validateQueryStructure`: one query cannot give two of + its sources the same alias. Two joins with one alias were accepted before. +- An include sees its ancestors' from and join aliases, but not the aliases + inside a parent's `unionAll()` branches. A union row holds the branches' + projected fields, so those names belong to sibling scopes. An earlier + revision of this change rejected an include that reused a branch alias, which + base `18abceee4` accepted with correct rows. The `unionParent` topology now + guards that legal naming. + +## Bug-class boundary + +- **Contract:** ARCHITECTURE.md normative law 1 and §Identity: renaming an + accepted alias cannot change an explicitly projected result, and a plan + rewrite preserves `SourceId`. +- **Histories:** the owner grammar (three topologies; zero to two joins; + LEFT or INNER; zero to two predicates in separate or combined form; plain, + ordered-and-limited, or DISTINCT bodies; include form, source, and filter; + outer spread or fields; eager or on-demand finite providers; up to four + source writes). +- **Production path:** `createLiveQueryCollection` through optimizer, + compiler, and live builder. +- **Observations:** complete public rows after preload and after each write, + compared with recomputation and across namings; per-Collection + `loadSubset` WHERE clauses for on-demand sources. + +## Evidence + +| Check | Result | +| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Original reproduction | Issue query returns `[]` on `18abceee4`, `[1]` with the repair. Bisected first bad commit: `40a5aea56` (#1877). | +| Pinned witnesses | All 13 behavioral witnesses (issue rows plus six topologies in eager and on-demand modes) fail on `18abceee4` and pass with the repair. | +| Campaigns | Fixed seed `1715` and the random campaign (80 runs each) fail on `18abceee4` and pass with the repair. | +| Optimizer site mutants | Reverting site 500, 848, 935, or 945 alone fails the owner (4, 13, 13, and 13 tests). The pure-wrapper witness is the only witness that reaches site 500. | +| Identity-only binding | With it, each optimizer site mutant also fails the existing suite with `CollectionInputNotFoundError` (3, 53, 146, and 59 tests) instead of returning wrong rows. With the optimizer repair in place it is behavior-equivalent; its value is converting a future identity loss into an error. | +| Include shadowing | Four rejection witnesses (include, nested include, include `unionAll()` branch, include `from()` subquery) fail on `18abceee4` and pass with the repair; a sibling-reuse control passes on both. | +| Generated rejection | One in four scenarios draws any naming; illegal ones must be rejected, and shadowing ones with `DuplicateAliasInSubqueryError`. At a 10x budget this check found the duplicate-join acceptance, and it fails when include `from()` visibility or the same-scope check is reverted. Reverting union-branch visibility is caught only by its pinned `validate-aliases.test.ts` witness, because the include level already checks branch aliases. | +| Union scopes | Reintroducing branch aliases into an include's visible set fails both `unionParent` witnesses and both campaigns at the default budget. A derived branch alias and a same-named union join return the same rows as a renamed join; `validate-aliases.test.ts` pins both legal namings. | +| Model calibration | Planted model faults (ignore child filter, LEFT as INNER, drop join duplicates) fail only at "canonical naming against recomputation". | +| Request calibration | An alias-dependent WHERE routing mutant fails only at "loadSubset requests per Collection". | +| Ablation | Restricting the earlier grammar to all-distinct names made both campaigns pass on the unrepaired code; the failure needs cross-scope reuse. | +| Enumeration (earlier grammar) | Failures needed a joined subquery with a pushed source predicate and an include reusing the subquery source or join alias. Outer-alias collisions alone never failed. | +| Full suite | `pnpm --filter @tanstack/db test` passes. | + +## Guide conformance (ORC-001 to ORC-014) + +- **ORC-001:** Law, authority, and limits are in the owner's opening comment. +- **ORC-002:** The recomputation model reads plain arrays and never consults + aliases, plans, or `SourceId`s. +- **ORC-003:** The contract and campaigns are in the owner. The grammar, + model, driver, and observation are in named sections of the companion. +- **ORC-004:** Reconstruction: every pinned witness is a grammar member. + Ablation and enumeration are recorded above. Range: IDs 1–4, three parts, + four refs and notes, and a three-name pool chosen for frequent collisions. + Exclusion: the legality test rejects shadowing, same-scope reuse, and + repeated `unionAll()` branch names. +- **ORC-005:** Real live-query Collections are compared at named checkpoints. + Failure messages carry the checkpoint, the write, and the naming. +- **ORC-006:** Site mutants, a hostile routing mutant, and planted model + faults are each rejected at the intended checkpoint. +- **ORC-007:** `generatedCampaigns` runs the same property with a fixed seed + and without one. Replay with `TANSTACK_DB_ORACLE_PROPERTY=includes.scoped-alpha-renaming` + plus `TANSTACK_DB_ORACLE_SEED` and `TANSTACK_DB_ORACLE_PATH`. The property + is registered in `oracle-config.ts` and `oracle-replay-manifest.ts`. +- **ORC-008:** Not applicable: the model is a stateless recomputation. +- **ORC-009:** The companion's opening comment maps slots, roles, aliases, and + `SourceId`. +- **ORC-010:** Cleanup failures join the primary failure in an + `AggregateError` whose `cause` is the violated law. +- **ORC-011:** Shared-fault hypothesis: every naming of one shape could be + wrong in the same way. The recomputation model is the second formulation + that distinguishes it. +- **ORC-012:** This record. +- **ORC-013:** The pinned wrapper witness distinguishes "reuse references + where the optimizer copies" from "reuse references except during + collapse". +- **ORC-014:** The on-demand provider is controlled. Its premise is a + `loadSubset` WHERE clause naming Collection fields. Real-provider + reception of that premise belongs to the load-subset owners. + +## Open in-scope cells + +These legal forms are in the bug class but outside the owner's grammar. Probes +during review passed on both revisions, so none is a known counterexample, but +none is protected by this owner: + +- nested includes, and includes inside a `from()` subquery; +- join subqueries outside the wrapper topology, and joined subqueries with + `groupBy` or `having`; +- outer RIGHT and FULL joins; +- publication events, observer timing, and unload under reused names; +- ordered windows and lazy join loading, whose lazy-target lookup reads + `aliasRemapping`, a map merged by alias across include scopes; +- temporal demand, cancellation, and progressive loading. + +The identity-only binding makes a lost `SourceId` raise an error in any of +these forms. That is the current protection against silent wrong rows there. diff --git a/docs/contributing/oracle-reviews/issue-1991-react-source-id.md b/docs/contributing/oracle-reviews/issue-1991-react-source-id.md new file mode 100644 index 0000000000..66e97204f0 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-1991-react-source-id.md @@ -0,0 +1,137 @@ +# React source Collection ID reuse oracle review + +Reviewed semantic head: `27560e3a4d1a0f76b12cf5bed720e4ab03485bb2` + +## Claim and limit + +The reported bug used a mounted `useLiveQuery({ query })` hook. A new source Collection reused the old source's ID. The hook returned rows from the old source because derived query identity used the ID. The user selected a fail-fast contract: one mounted derived-identity hook rejects a different source Collection object with a previously used ID in the same `DbClient` scope. The old hook must unmount before the application reuses that ID in a new hook. Separate hooks may use same-ID sources. Their Suspense cache entries must remain distinct. + +The public hook and Suspense wrappers are the production paths. The observation cuts are a rerender error for a mounted-hook collision and the public row for separate Suspense hooks. The controlled sources do not prove real SQLite persistence, DbClient SSR preload or streaming, explicit `queryKey`, legacy dependency arrays, or direct Collection input. The coverage map assigns those limits to this owner or the existing integration owners. + +The oracle keeps five responsibilities close together. Its opening states the contract, model, limits, and observation cuts. Its `Binding` histories and `expectedCollision` relation define the reference judgment. Each test constructs a bounded legal history. The driver renders the public React hooks. Assertions compare the thrown error or public row at the rerender or settled-render boundary. + +## Guide audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 authority and limits | Pass. The issue reports the ID-based stale query. The user selected fail-fast replacement in a mounted hook. The oracle and coverage map state the boundary and excluded paths. | +| ORC-002 independent judgment | Pass. `expectedCollision` compares prior and next source object references for each client scope. It does not import the production token map, IR hash, or cache-key logic. Public-row checks also distinguish separate Suspense sources. | +| ORC-003 visible responsibilities | Pass. The opening contract and model, bounded test histories, public hook driver, and error or row comparisons are visible in the oracle file. | +| ORC-004 generated grammar | Not applicable. These are fixed legal histories, not a generated-history coverage claim. | +| ORC-005 production path and observation | Pass. Tests render `useLiveQuery` or `useLiveSuspenseQuery`. They assert a thrown collision before stale rows or exact public row values after React renders. | +| ORC-006 calibration | Pass. The original production file fails the mounted A-to-B collision test at its error assertion. A temporary cache-key mutant that restores the ID-only client cache fails both client-scoped Suspense row tests: the second render shows `first` instead of `second`. Both results are assertion failures at the intended checkpoints. The production file was restored after each run. | +| ORC-007 fixed and random campaigns | Not applicable. No important generated property is claimed. | +| ORC-008 model minimality | Pass. The pairwise model retains source object, ID, and client scope. A → other ID → B with A's ID distinguishes full mounted-hook history from a model that remembers only the current source. A client switch and return distinguish retained scope bindings from one global binding. | +| ORC-009 vocabulary mapping | Pass. `Binding` is a model-only pair of a production source Collection and an optional `DbClient` scope. The model's history array represents mounted-hook source observations; it does not copy production refs or caches. | +| ORC-010 failure fidelity and cleanup | Not triggered by shrinking or normalized capture. Tests compare the synchronous error or exact row before test cleanup. One controlled source cleanup intentionally logs the dependent live-query error; its following collision assertion remains the primary check. | +| ORC-011 second formulation | Pass for the named cache fault. The mounted-hook relation judges object reuse through an error. Independent Suspense histories judge separate hook ownership through public rows, including an initial suspended retry. | +| ORC-012 review evidence | Pass. This record names the reviewed semantic head, all guide outcomes, original and adjacent witnesses, mutant results, and limits. | +| ORC-013 boundary witness | Pass. Same object and new ID rerenders are accepted. A different object with a prior ID rejects directly, after another ID, after cleanup, and when two same-ID sources occur within one query. Separate hooks remain legal and show their own rows. | +| ORC-014 controlled premise handoff | Not triggered for a real-provider claim. The oracle claims controlled React identity behavior only. Real persisted SQLite remains outside its evidence. | + +## Loss audit and closeout + +The loss audit found three missing distinctions. The final oracle adds two same-ID sources in one query, mounted-hook reuse after source cleanup, and a `DbProvider` A → B → A scope history. A code review found that the client-scoped Suspense cache reused an ID-only key across separate hooks. The final oracle checks both a committed sibling hook and an initial suspended retry. A temporary ID-only cache-key mutant failed both checks. + +The original witness is the mounted A(id=x) → B(id=x) rerender. Adjacent histories include A(id=x) → C(id=y) → B(id=x), a changed predicate, source cleanup while mounted, same-query duplicate IDs, client scope changes, and separate Suspense hooks. The claim covers these legal React histories and their public error or row observations. It does not assert that an ID is globally unique across hooks or clients. No reachable in-scope counterexample was found in this bounded audit. + +The focused oracle passed 12 tests. The related React hook and Suspense suites passed 120 tests in five files. Lint and the React DB package build passed. This is sampled and bounded evidence, not a universal proof of every React schedule. + +## 2026-10-02 collision-path addendum + +Reviewed semantic head: `ebf7c785bc42f6dd1d652b296a3de7e15ec310a5`. +The earlier review above remains evidence for its own head. This addendum +evaluates the new sync-start deferral witness and fix. + +**Law and boundary.** A mounted derived-identity hook rejects a source object +that reuses a remembered ID. If the rejected render also materialized a +different shared `DbClient` descriptor, it must release the descriptor's +sync-start deferral. A later direct Collection reader must be able to start +sync and observe the descriptor's row. This is a controlled React render and +ErrorBoundary history, observed through the public hook error, Collection +status, and row after the later reader subscribes. It does not claim that all +possible render exceptions release deferrals. + +**RED/GREEN and distinguishing histories.** At prior head `c5a1bd9f4`, the +ErrorBoundary caught the expected collision, but the later direct reader stayed +`idle` and the descriptor sync callback had run zero times. The same test at +`ebf7c785b` reached `ready`, returned the exact shared row, and counted one +sync start. The existing same-source, new-ID, same-ID collision, client-scope, +and Suspense controls remained green. The old implementation is the relevant +hostile design: it passes the original collision assertion but fails at the +later reader's public status checkpoint. + +| Requirement | Addendum outcome | +| --- | --- | +| ORC-001 | Pass. The reported deferred-sync leak and the existing mounted-hook contract supply authority; the coverage map names the React owner and its limits. | +| ORC-002 | Pass. The expected later `ready` status and row follow from a distinct reader's public Collection contract, not the production deferral flag or token map. | +| ORC-003 | Pass. The oracle prologue states this law and its limit; the fixed history, React driver, and public assertions are in one file. | +| ORC-004 | Not applicable. The new history is fixed, with no generated coverage claim. | +| ORC-005 | Pass. The driver uses `useLiveQuery`, a real `DbClient` descriptor, a real ErrorBoundary, and a later direct reader. | +| ORC-006 | Pass. The prior production implementation fails at `idle` versus `ready`; the same case passes after the fix. | +| ORC-007 | Not applicable. No important generated property was added. | +| ORC-008 | Pass. The model needs only a remembered same-ID binding, a distinct shared descriptor, a rejected render, and a later reader; it does not copy sync-manager state. | +| ORC-009 | Pass. The new prose uses source Collection, sync-start deferral, and Collection status with the glossary meanings. | +| ORC-010 | Pass. The test checks the caught error and later reader before test cleanup, with no error normalization or shrinking. | +| ORC-011 | Not triggered by a shared-fault hypothesis for this new boundary; the later direct reader is a separate path from the rejected derived hook. | +| ORC-012 | Pass. This versioned addendum identifies the exact reviewed semantic head, all requirement outcomes, original wrong design, and remaining limits. | +| ORC-013 | Pass for the named collision condition. A same-source or fresh-ID rerender stays legal; a same-ID replacement rejects, while its unrelated descriptor remains usable. | +| ORC-014 | Not triggered for a real-provider claim. The source sync is controlled; persisted SQLite and streaming remain outside this owner. | + +The updated oracle passed 13 cases. The oracle plus the surrounding React hook +and Suspense suites passed 100 cases; ESLint and Prettier passed. A separate +TypeScript project check was blocked by missing `@tanstack/query-core` and +`@standard-schema/spec` dependencies in this checkout, while Vitest reported +no type errors for the tested files. A bounded 32-ID diagnostic showed linear +retention in one mounted hook; the coverage map records that unresolved space +policy. No claim of space-bounded ID history is made. + +## 2026-10-02 multiple startup failures and main integration + +Reviewed semantic head: `639bcd16fdb5db83fc11c3d53a94aa2d7e4c19fa`. +This entry supplements the earlier heads. It covers the merge with main +`06cab6fc7b808acfbfb3af1eb2fc1fdc3c9f0fa8` and the deferred-startup +failure that CodeRabbit retained after the collision-path fix. + +**Law and boundary.** If a rejected derived-identity render defers several +shared source Collections, one failing sync start must not prevent later +Collections from starting. The hook reports the first startup error after it +attempts each pending Collection once. A later direct reader observes the +healthy source's `ready` status and row. This is a controlled React render, +ErrorBoundary, and direct-reader history. It does not establish behavior for +every render exception or a real persisted source. + +**RED/GREEN.** At prior head `0da7eae89`, two deferred sync starts threw +across React's retry. The later healthy Collection stayed `idle`; its sync +callback had not run. The new case failed at the public `ready` assertion. +At `639bcd16f`, the same case reaches `ready`, returns the exact healthy row, +and counts one healthy sync start. The two failing starts each run once. +This rejects the plausible wrong design that stops draining at the first +startup error. The existing one-failure collision case and source-binding, +client-scope, and Suspense cases also pass. The merged production path keeps +main's source-object-keyed pooled partitions and the PR's source-qualified +Suspense keys. + +| Requirement | Addendum outcome | +| --- | --- | +| ORC-001 | Pass. The mounted-hook collision contract and the public direct-reader startup obligation authorize the expected result; the coverage map bounds the claim. | +| ORC-002 | Pass. The expected `ready` status and row come from the public Collection behavior, independent of the hook's deferral set and drain algorithm. | +| ORC-003 | Pass. The oracle prologue, fixed history, real React driver, and public assertions keep the five responsibilities visible together. | +| ORC-004 | Not applicable. No generated-history coverage is claimed. | +| ORC-005 | Pass. The driver uses `useLiveQuery`, a `DbClient` descriptor, an ErrorBoundary, and a later direct reader; it checks status and row after subscription. | +| ORC-006 | Pass. The old sequential drain fails at `idle` versus `ready`; the same history passes with the new drain. | +| ORC-007 | Not applicable. No important generated property was added. | +| ORC-008 | Pass. The fixed history distinguishes failed and healthy source roles without modeling production's deferral set. | +| ORC-009 | Pass. The prose and assertions use source Collection, sync start, and Collection status with the glossary meanings. | +| ORC-010 | Pass. The test observes the caught error and later reader before cleanup; it has no shrinking or error normalization. | +| ORC-011 | No shared-fault hypothesis requires a second formulation; the later direct reader is a separate public path. | +| ORC-012 | Pass for this bounded repair. This record identifies the reviewed head, prior wrong design, observation cut, and limits. | +| ORC-013 | Pass for the named startup boundary. The prior one-failure case reaches a later reader; this two-failure case distinguishes full draining from stopping after React's retry. | +| ORC-014 | Not triggered for a real-provider claim. The controlled source sync callbacks are the declared premise. | + +The source-ID oracle passed 14 cases, six surrounding React hook and Suspense +files passed 110 cases, and the React TypeScript project check passed after +restoring workspace dependency links. The pooled-query oracle, GC, and identity +suites passed on the merged main. The scope remains the controlled React paths +listed in the coverage map; the bounded-navigation space policy, persisted +SQLite sources, and DbClient SSR preload or streaming remain open there. diff --git a/docs/contributing/oracle-reviews/issue-1992-ordinary-sqlite-work.md b/docs/contributing/oracle-reviews/issue-1992-ordinary-sqlite-work.md new file mode 100644 index 0000000000..a1c557ce39 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-1992-ordinary-sqlite-work.md @@ -0,0 +1,49 @@ +# Ordinary SQLite work oracle review + +Baseline production: `origin/main` at `ef1e6a4aa25fcfa988b1a14c7aa91406885814fe`. +Repair: `codex/issue-1992-sqlite-writes`, starting from that baseline. Oracle: `packages/db-sqlite-persistence-core/tests/ordinary-transaction-work-oracle.ts`, invoked by `sqlite-resume-snapshot.test.ts`. The ordinary write work law is RED on the baseline and GREEN with the repair. This record does not claim untested host behavior. + +| Guide item | Outcome and evidence | +| --- | --- | +| ORC-001 | The file's opening contract derives durable row/metadata/position semantics from `PersistedTx` and the existing SQLite owner. Chunk-bounded work is explicitly a proposed acceptance law from #1992, not an existing promise. Browser and other host limits are named. | +| ORC-002 | `modelAfter` is a Map reducer over cloned public actions. It imports no SQL scheduler, batching classifier, or production serializer. | +| ORC-003 | The opening prose, `modelAfter`, fixed and generated semantic/work histories, node:sqlite fixture, and complete-observation/work comparisons expose the five responsibilities. | +| ORC-004 | The semantic generator is seed -> ordinary transaction -> optional dependent followup, with a late-bookkeeping rollback branch. Seven mandatory suffixes reconstruct repeated-key, delete/reinsert, partial-update, and present/absent row-metadata set/delete cases; random prefixes vary nearby action order. Its 120-sample fixed calibration reaches all seven scenarios on both rollback outcomes, both target keys, both followup outcomes, and a nonempty action/metadata prefix overlap. Removing a suffix loses its witness; removing prefixes loses adjacent orders. Bounds: 0–4 prefix mutations, 0–3 row-metadata actions, 0–2 collection-metadata actions, keys `a`/`b` plus absent `c`, values 0–9, one term with seq 1–3. The separate independent-key work generator has 0–60 unique keys, six insert/update/delete/row and collection metadata shapes, and 100/999 parameter caps; its 120-sample calibration reaches all shapes and caps. Truncate, concurrent owners, other key types, arbitrary-long histories, invalid positions, and interleaved row metadata are excluded. Fixed 25/26 and 10k work cases remain separate. | +| ORC-005 | The driver calls real `SQLiteCorePersistenceAdapter.applyCommittedTx`. Every generated seed/candidate/followup and late rollback resets counters around that apply and asserts exactly one `SQLiteDriver.transaction` entry before snapshot queries. The independent-key generator also captures query/run counts before comparing durable state, then applies `12 + 6 × ceil(n / min(100, floor(cap / 4)))`. Semantic/conflicting histories have no call bound. Durable observations come from `loadResumeSnapshot` plus diagnostic SQL tables. Public `pullSince` distinguishes 128 replay actions from 130 full-reload actions. | +| ORC-006 | An adapter-path driver corruption drops metadata and the complete observation rejects it. A test-only two-statement replacement of 26 rows passes the 100-parameter cap and value comparison but loses metadata, which the row comparison rejects. A temporary metadata-dropping driver mutation in the generated campaign failed at `candidate durable checkpoint` and shrank to repeated-key case `{seed:1992, path:"0:0:0:0:0"}`; the mutation was restored. Count perturbation controls add a phantom second transaction at each seed/candidate/followup and rollback checkpoint, and 20 phantom row calls to a normally green one-row case. Each is rejected by the corresponding transaction or work assertion. The baseline fails fixed and generated work assertions at the driver-call checkpoint. With the repair, a temporary row classifier mutation that admits repeated keys to a batch fails the generated oracle at the candidate durable checkpoint, seed 1992, path `2:0:0:2:2:2:2`. A separate temporary chunk-limit mutation passes the Cloudflare 25-row case but fails its 26-row case with `host parameter limit exceeded`. A collection-metadata classifier mutation that treats duplicate keys as distinct fails the fixed `repeated-collection-metadata-key-keeps-order` case at the complete durable checkpoint: it leaves `cursor` set after the model deletes it. All were restored. | +| ORC-007 | Semantic fixed/random campaigns share `generatedHistoryArbitrary`, `assertGeneratedHistory`, and `oracleRuns(24)`, fixed seed 1992, property `sqlite-ordinary.write-history`; the temporary metadata mutant failure directly replayed at path `0:0:0:0:0`. Independent work campaigns share `independentWorkArbitrary`, `assertIndependentWork`, and `oracleRuns(24)`, fixed seed 31992, property `sqlite-ordinary.independent-work`. The baseline shrinks to `{shape:"insert-later-metadata",size:3,cap:100}` at path `0:0:1:0:0:0`; direct random-campaign replay reproduces 20 calls against bound 18 and records `numRuns:1, failed:true`. The collection-metadata work gap on the first repair shrinks to `{shape:"collection-metadata-only",size:15,cap:100}` at path `0:2:2`, with 19 calls against bound 18. The repaired focused SQLite owner passes 36/36, the Cloudflare driver tests pass 6/6, and standalone package TypeScript passes. Two earlier broad package runs had resource-sensitive timeouts; the two affected CLI tests and 15-second lifecycle test pass when run alone. Neither broad run is a clean suite pass. | +| ORC-008 | The reducer retains rows, row metadata, tombstones, collection metadata, applied positions and latest position. A later partial update, delete/reinsert, metadata action, or replay distinguishes these states. It omits schema/reset/leader state because this bounded single-adapter history does not act on them. | +| ORC-009 | The opening prose maps diagnostic `expectedKeys`, `tombstones`, and `applied` to the persisted SQLite tables and uses glossary terms for committed transaction, metadata, position, and full reload. No model-only lifecycle is presented as production state. | +| ORC-010 | Every fixture is closed even after assertion failure. An `AggregateError` retains the primary error as `cause` and cleanup as a separate error. Fast-check shrink paths are reported and replayed directly; the generated work failure remains a work-checkpoint failure after shrinking to three rows. | +| ORC-011 | The generated semantic model uses a Map reducer, independent of the production classifier and SQL. The temporary repeated-key classifier mutation fails that model at the durable checkpoint. Existing owner tests separately exercise full replacement and resume; the Cloudflare driver provides a receiving seam. No additional shared-fault hypothesis has been named. | +| ORC-012 | This record covers every numbered guide item for the baseline and repair. The exact tested code HEAD is `1c521597f289226050285c27669907a300e9b2b6`; the record-only follow-up does not change production or oracle code. The generated campaigns sample nearby semantics and independent-key work within their stated bounds, including one-transaction checks for each generated apply. The bounded ordinary-write class is closed for these owner and Cloudflare storage-seam histories and observations; the host limits below remain outside that claim. | +| ORC-013 | Numeric 100/101 parameter guard and 25/26 rows are executable, and 64/65 mutations straddle the public 128-action replay threshold. A repeated-key classifier mutant fails at the generated candidate durable checkpoint, and a chunk-limit mutant crosses the 25/26 Cloudflare receiving boundary. Both were restored. | +| ORC-014 | The synthetic node:sqlite cap is a controlled host premise. The Cloudflare driver now covers the 100-parameter ordinary-write seam at 25, 26, and 205 rows with row and collection metadata as well as full replacement. The storage seam does not execute in Workers. OPFS scheduling and browser latency remain unverified receiving boundaries. | + +The violated proposed law is bounded query/run calls per parameter-limited chunk for ordinary independent-key writes, while preserving atomic durable observations. The original witness is 10,000 distinct inserts plus metadata; adjacent cases include 25/26 and 100/101 keys, partial updates, deletes, row and collection metadata-only writes, repeated keys, and two rollback cuts. On the baseline, the owner has 33 passing tests and three RED work tests. With the repair, the owner has 36 passing tests. The 10,000-row insert plus metadata takes 505 query/run calls, down from 50,005; partial update, delete, and row metadata-only at 205 rows take 49, 31, and 13 calls. Collection metadata-only at 205 keys takes 22 calls, down from 209 before the extra batching pass. The tested component path is `applyCommittedTx` on node:sqlite after schema setup, with durable observations after settlement. Production source weight is 136 lines added and 153 removed, net -17 against the baseline. + +The pre-repair package run reported 695 passing tests, one intended RED work test, two todo, and 153 `rootDir` source errors from cross-package imports in its typecheck worker; that run preceded the independent-key work generator. After the repair, two broad runs had resource-sensitive timeouts. The two affected CLI tests passed alone, and the 15-second lifecycle test passed alone in 14.76 seconds. Standalone `tsc -p packages/db-sqlite-persistence-core/tsconfig.json --noEmit` passes. A clean broad package run is still outstanding. + +Replay command for the captured mutant path, from `packages/db-sqlite-persistence-core` (the temporary mutation has been restored): + +```sh +TANSTACK_DB_ORACLE_SEED=1992 TANSTACK_DB_ORACLE_PATH='0:0:0:0:0' TANSTACK_DB_ORACLE_PROPERTY=sqlite-ordinary.write-history ../../node_modules/.bin/vitest run tests/sqlite-resume-snapshot.test.ts --testNamePattern 'preserves bounded ordinary write histories \(random or replayed\)' --coverage.enabled false --typecheck.enabled false +``` + +The baseline work failure replays directly against `ef1e6a4` with: + +```sh +TANSTACK_DB_ORACLE_SEED=31992 TANSTACK_DB_ORACLE_PATH='0:0:1:0:0:0' TANSTACK_DB_ORACLE_PROPERTY=sqlite-ordinary.independent-work ../../node_modules/.bin/vitest run tests/sqlite-resume-snapshot.test.ts --testNamePattern 'bounds independent-key write work \(random or replayed\)' --coverage.enabled false --typecheck.enabled false +``` + +## Repeated-key work follow-up (2026-10-02) + +The tested implementation commit is `326b1da83ea2978a52b2a07daa9347ee10492391` on `codex/issue-1992-sqlite-writes`. This section adds evidence for the repeated-key law. The earlier independent-key review above remains a record of its own tested commit. + +The proposed work law now counts distinct keys in each row, row-metadata, and collection-metadata action family. A transaction with many actions on one key must use bounded query/run calls. The durable law still applies actions in their original order. A 205-update transaction on one key, with partial values and periodic inline metadata overrides, was RED on the published pre-follow-up commit `9d43ec43b81a24f6f8ef26e04f81fd7358676df6`: the independent Map model matched complete durable state, but the driver made 824 query/run calls against a bound of 18. The same witness is GREEN at 8 calls on `326b1da83`. + +The existing generated-history grammar now samples 0–4, 5–24, and 25–60 action prefixes before its required scenario suffix. It checks complete durable state and the distinct-key work bound after committed candidates and late-bookkeeping rollback. Its scope remains two writable string keys, one absent metadata key, at most three row-metadata prefix actions, at most two collection-metadata prefix actions, and up to three committed positions. Fixed 25/26 and 10,000-key cases keep the parameter-boundary and large independent-key checks. This bounded grammar does not establish arbitrary-length histories, concurrent owners, or other host schedulers. + +A separate truncate witness requires invalid intermediate values to reject atomically, even when a later same-key action overwrites them. It covers row values, inline row metadata, row metadata actions, and collection metadata actions. The first implementation was RED when a collection-metadata `set(undefined)` preceded a delete: the promise resolved and committed because the fold removed the invalid set. Before the fold, SQLite rejected the unbindable value. The final helper validates superseded sets under the same bound-value rule while retaining row metadata's `undefined`-to-NULL rule. The witness is GREEN on `326b1da83`, and the durable position remains unchanged after rejection. The pre-follow-up implementation is a hostile work-law case; the intermediate unbindable-value implementation is a hostile atomic-rejection case. Both fail at the intended checkpoint. + +Final local verification on the implementation commit: the full SQLite core package run passed 394 tests with one todo across nine files, and the full Cloudflare package run passed 54 tests across four files. Both packages passed standalone TypeScript checks. Changed-file ESLint reported zero errors and three existing fixture warnings. Prettier and `git diff --check` passed. The repeated-key follow-up adds 82 and removes 20 production lines relative to `9d43ec43`; the complete PR adapter diff adds 199 and removes 148 lines against the fetched `origin/main`. The extra fold is the cost of the stronger work law. Browser latency, OPFS scheduling, Cloudflare Workers execution, Electric, Tauri, and native mobile remain outside this evidence. diff --git a/docs/contributing/oracle-reviews/issue-1993-sqlite-binding-capacity.md b/docs/contributing/oracle-reviews/issue-1993-sqlite-binding-capacity.md new file mode 100644 index 0000000000..1a745eaed0 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-1993-sqlite-binding-capacity.md @@ -0,0 +1,188 @@ +# SQLite subset binding capacity, issue #1993 + +Reviewed executable revisions: `51c060aa5` (the initial core and Cloudflare +repairs) and `14c0e4efc` (the CI integration follow-up, including the Node +expression-index oracle repair at `398725d45`). The pre-repair core source was +`ef1e6a4aa`; the Cloudflare transaction driver before its one-line repair was +`cbd9d24de`. This record was updated after the follow-up executable revision. +The subsequent test-file edit only clarifies the oracle's documented scope. + +## Contract, model, and boundary + +The established 1,200-ID `loadSubset` case promises support for large `IN` +predicates. Issue #1993 adds the statement-wide law: a supported subset request +must return its exact persisted rows without asking its SQLite driver to bind +more parameters than the driver's host allows. The core owner is +`packages/db-sqlite-persistence-core/tests/sqlite-core-adapter.test.ts`; the +Cloudflare receiving owner is +`packages/cloudflare-durable-objects-db-sqlite-persistence/tests/do-replacement-batching.test.ts`. + +The core oracle filters two or three declared rows with typed equality and +Boolean clauses. Its result does not come from SQL compilation or SQLite. It +builds corresponding IR predicates, seeds the real core adapter, and records +each attempted prepared statement before the driver calls SQLite. At +`loadSubset` settlement it compares exact keys and decoded values, ordered keys +where requested, the reached predicate SELECT, errors, and each statement's +binding count against a real Node SQLite connection limited to 999 variables. +The Cloudflare witness independently expects only the seeded `target` row from +the named IDs. Its Node SQLite storage seam rejects more than Cloudflare's +documented 100 parameters before execution. It reaches the actual Cloudflare +driver and core adapter through both savepoint and native transaction modes. + +The bounded history grammar crosses one/two lists, an optional scalar, nested +`and`/`or`, five value kinds, 998/999/1000-value and 499+500/500+500 +edges, direct and `transactionWithDriver` routes, cursor SELECTs, and both +index-definition contexts. An empty `IN` is valid; a scalar equality clause +with no value is excluded by the model-input guard. Cloudflare crosses 100/101 +equality and one-item `IN` clauses in both transaction modes. The core's +1,000-scalar control also reaches the final statement-total guard. + +## RED, GREEN, and hostile controls + +On unchanged core production at `ef1e6a4aa`, the normal targeted package run +had five expected failures and one passing index-definition control. One +1,000-value list attempted 1,000 bindings; two 500-value lists attempted +1,000; the reported two 1,000-value lists attempted 2,000; a 999-value list +plus one scalar attempted 1,000; the nested case attempted 1,001. Both cursor +SELECTs attempted 1,000. The 999 and 499+500 controls passed. SQLite rejected +the over-cap statements with `too many SQL variables` at the predicate SELECT. +The original 900-item OR-chunk compiler is the executed hostile design. + +The fixed-seed campaign shrank with `seed=1993999 path=1:0`: both the first and +reduced failures were `capacity@predicate-select`. Direct replay reproduced the +same class and checkpoint before the fix. The seedless campaign independently +shrunk to the same class. The post-repair direct replay passes using: + +```sh +TANSTACK_DB_SQLITE_BINDING_SEED=1993999 TANSTACK_DB_SQLITE_BINDING_PATH=1:0 \ +pnpm --filter @tanstack/db-sqlite-persistence-core test \ + tests/sqlite-core-adapter-cli-runtime.test.ts \ + -t 'replays the requested binding history directly' +``` + +The repaired core binds each runtime `IN` list as one JSON table value, keeps +literal `IN` values in both index DDL contexts, and uses the existing in-memory +row evaluator when the final SELECT would exceed the advertised cap. Its +primary targeted run passes all seven oracle checks with no type errors. + +With the new Cloudflare receiving witness and the old transaction driver, +both 101-clause kinds rejected with `host parameter limit exceeded` at +`loadSubsetInternal`; the transaction driver had omitted the root driver's +100-binding cap. The one-line propagation repair passes all 100/101 cases in +both transaction modes. The host-limit test file passes all seven runtime cases +with no type errors. +The SQLite CLI contract file passes 40/40. Both packages build and their +changed files pass ESLint. A full core package run under two concurrent test +threads passed 684 tests but timed out on three existing long-running cases; +the affected CLI and lifecycle files passed in isolation (40/40 and 4/4). + +## Oracle guide audit + +| Requirement | Outcome for this executable revision | +| --- | --- | +| ORC-001 authority and limits | Pass for the bounded subset law above: the prior large-list contract and #1993 authorize exact rows and a statement-wide cap. Native host and broader value limits are below. | +| ORC-002 independent judgment | Pass. The model computes typed row matches without production SQL, the compiler's classifier, or SQLite results. The Cloudflare expected key comes from named IDs, independent of the adapter. | +| ORC-003 distinguishable responsibilities | Pass. The core owner's opening names contract, model, grammar, driver, and refinement; its fixed/generated cases implement them. The Cloudflare file names its controlled host premise and public check. | +| ORC-004 grammar controls | Pass within the stated domain. Fixed cases reconstruct one large list, two small lists whose total crosses the cap, mixed scalar/list, nested, empty, typed, ordered two-hit, cursor, and index contexts. Removing list count or length loses a capacity shape; removing kind/connective loses a semantic-compatibility challenge. 998/999/1000 and 499+500/500+500 are margins. A no-value scalar clause is rejected; null, nonfinite, containers, and out-of-range BigInt are excluded. | +| ORC-005 production path and observation | Pass. Real core adapter plus prepared Node SQLite reaches the SELECT and checks exact public rows at `loadSubset` settlement, with attempted binding counts recorded before failure. The Cloudflare witness reaches the real Cloudflare driver through both transaction APIs and checks returned rows and host counts. | +| ORC-006 checker calibration | Pass. The unchanged 900-item OR-chunk compiler and the Cloudflare transaction driver without its cap both fail at the intended query checkpoint. These are assertion failures from real over-cap attempts, not timeouts or setup failures. | +| ORC-007 campaigns and replay | Pass. The normal core package test registers separate matching 12-run fixed-seed and seedless-random campaigns. The replay variables select only the requested property/seed/shrink path; the captured RED path failed directly and passes after repair. | +| ORC-008 state minimality | Not applicable. The reference recomputes a result from one immutable specification and has no transition state to combine or split. | +| ORC-009 vocabulary mapping | Pass. A `Spec` is model-only input; each clause maps to one IR predicate. Binding attempts map to driver query/run calls, and the observation checkpoint is `loadSubset` settlement. Shared subset and transaction terms retain production meanings. | +| ORC-010 failure fidelity and cleanup | Bounded pass. Generated failures preserve first and reduced kind/checkpoint; the core observation closes its Node database and records a secondary close error separately from the query error. No close failure occurred in the recorded campaigns. A setup failure with a simultaneous close failure, or a close failure during the cursor/index fixed controls, has no demonstrated separate diagnostic; this harness gap remains with the core owner. | +| ORC-011 independent second formulation | No second product-level formulation is claimed. The plausible shared fault is typed JSON conversion; the independent typed row model plus string, boolean, Date, and signed-64-bit BigInt controls distinguish the implemented lowering from a blanket numeric cast. Null, nonfinite, and structural values still need a separate equivalence law. | +| ORC-012 review evidence | This versioned record gives the outcome of ORC-001–014, exact executable revision, hostile RED and repaired GREEN, limits, and unresolved owners. It claims only the bounded contract × history × path × observation cells above. | +| ORC-013 boundary witness | Pass. 999 succeeds and 1,000 failed on the original core; 499+500 succeeds while 500+500 failed. Cloudflare's 100/101 pair reaches the nested-driver cap. A conservative early fallback is allowed, so the claim is no over-cap statement plus exact rows, not a precise fallback threshold. | +| ORC-014 controlled-premise handoff | Pass for Node's real configured 999-variable SQLite connection. Cloudflare's 100 cap is a documented limit enforced by controlled storage; actual Worker delivery of that premise remains open under the Cloudflare runtime owner. | + +## Remaining scope and code weight + +This closes the bounded prepared-Node and controlled Cloudflare transaction +paths, not every SQLite host. The core coverage map owns null, nonfinite, +container, and structured-value equivalence, 50,582-ID stress, JSON-function +availability, arbitrary predicate depth, concurrency, and row-read work during +fallback. The Cloudflare runtime bridge owns a Worker receiving witness for +JSON support and the real 100-binding premise. Browser, mobile, Tauri, and +native-device drivers each need their own receiving execution. A reachable +in-scope counterexample would keep the broader class open. + +Production diff against `origin/main` at `14c0e4efc`: SQLite core `+18/-37` +lines; Cloudflare driver `+1/-0`; combined `+19/-37`, net **18 fewer** + +## Node 24.8 CI follow-up (`f9161a617`) + +The original native-limit claim above applies to the earlier local Node run, +not to every Node version. The repository pins Node 24.8.0. On that version, +`DatabaseSync` does not expose `limits.variableNumber`, and the new fail-fast +assertion at `20a1d77d0` rejected eight oracle cases before `loadSubset` ran. +That RED was a setup failure, not a product counterexample. + +Revision `f9161a617` keeps real Node prepared SQLite execution for accepted +statements. When Node exposes the configured limit, the fixture checks it and +lets SQLite reject excess bindings. When Node does not expose it, the controlled +driver rejects a statement above its declared cap before preparation. A fixed +control forces that branch and checks rejection at 101 bindings with cap 100. +The existing observation recorder still records attempted bind counts before +either host rejects them, so an over-cap production statement fails the oracle +at `capacity@predicate-select` rather than passing because the driver rejects. + +The contract, independent row model, input grammar, replay campaigns, and +public-result checks remain unchanged (ORC-001, 002, 004, 007, 008, 009, 010, +and 011). The driver and prose now distinguish native and controlled limits +(ORC-003, 005). The original OR-chunk RED remains a reached capacity failure +under either driver (ORC-006). The 100/101 and 999/1000 margins still challenge +the bounded threshold (ORC-013). This section records the revised claim for +ORC-012. On Node 24.8, the core oracle establishes the declared driver boundary, +not a native SQLite limit. The Cloudflare and other host receiving witnesses +remain separately owned as stated above (ORC-014). + +Local Node 24.19 probes verified native caps 100 and 999 and rejection of +cap-plus-one statements. The local package suite and commit hook could not run: +the configured package proxy returned HTTP 403 for Rollup. Current-head CI is +the verification gate for the controlled Node 24.8 path. +production lines. Test and documentation growth is reported separately in the +branch diff. + +## CI integration follow-up at `14c0e4efc` + +The first PR CI run found four stale observations in the Node expression-index +oracle. Two fixed BigInt cases still expected two or 901 scalar bindings; two +hostile controls checked those old counts and stopped before changing the SQL. +The runtime query now binds one JSON numeric array through `json_each(?)`. +At `398725d45`, the fixed cases compare the exact unquoted numeric JSON text, +and the hostile controls alter the first or last number inside that one bound +array. The unchanged production failed four of 68 checks before this oracle +repair. Afterward all 68 passed with no type errors. The oracle still compares +exact adapter and direct SQL rows and the named expression-index plan. The +controls reach the query and fail on wrong rows, not on an unreached guard. + +CodeRabbit review `5393828482` found a missing production path: a root driver +can declare a host cap while `transactionWithDriver` returns a distinct driver +without that property. The old oracle reused the root driver for its transaction +route, so it could not see this omission. A new fixed witness uses a real Node +SQLite connection capped at 100 variables, a root driver that advertises 100, +and a distinct transaction driver that omits the cap. Its 100-equality control +binds 100 and returns the exact rows. With unchanged production at `398725d45`, +the 101-equality case submitted 101 bindings at the predicate SELECT and +rejected with `too many SQL variables`: `capacity@predicate-select`, not a setup +failure. At `14c0e4efc`, the transparent scheduling wrapper forwards the root +cap, and subset queries fall back to that cap when the transaction driver omits +one. The 101-equality case now makes an unbound SELECT, returns the exact rows, +and passes the same 100-cap refinement check. + +The guide outcomes above carry forward for ORC-002, ORC-003, ORC-007 through +ORC-009, and ORC-011. ORC-001 and ORC-004 now include the omitted-cap +transaction path and the 100/101 boundary. ORC-005 observes the real prepared +Node query and exact returned rows on that path. ORC-006 has the reached RED +failure and repaired GREEN at the same predicate checkpoint. ORC-010 retains +the original primary error separately from any close error. ORC-012 is bounded +to the declared root-cap, distinct-driver history and exact-row plus binding +observations; drivers that misreport their actual host limit remain outside it. +ORC-013 distinguishes 100 from 101 and checks that 101 falls back before +prepare. ORC-014 uses an actual Node SQLite variable limit of 100; other hosts +still own their receiving witnesses. + +The focused follow-up checks passed: 41 SQLite core CLI and binding tests, 68 +Node expression-index tests, and 21 Cloudflare driver and host-limit tests, all +with no type errors. Changed production and oracle files passed ESLint and +Prettier. diff --git a/docs/contributing/oracle-reviews/issue-2029-direct-write-lock-window.md b/docs/contributing/oracle-reviews/issue-2029-direct-write-lock-window.md new file mode 100644 index 0000000000..97a5249bc0 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2029-direct-write-lock-window.md @@ -0,0 +1,129 @@ +# Issue #2029 direct-write lock window oracle review + +Reviewed source head: the commit that updates this record on +`lock-window-validate-at-apply`. The production change is unchanged from +`6cf681fb4`. The oracle file is as of `b6747be44`. + +## Contract and evidence + +In a persisted Query Collection, each source commit waits for the persistence +wrapper's apply lock. While another task holds the lock, a committed refetch +waits there, and core has not accepted it yet. A direct write +(`writeInsert`, `writeUpdate`, `writeDelete`, `writeUpsert`) in that window +must be validated against, and apply on top of, the rows of every earlier +commit. A refetch that returns while the write waits follows the merge rule +from #2030: a fetch that started before the write was called keeps the write's +rows on the write's keys, and a fetch that started after the write wins. + +The authority is #2029, the direct-write contract in +`docs/collections/query-collection.md`, and the merge rule in +`packages/query-db-collection/src/query.ts`. The reference is the same Query +Collection without persistence, which has no lock. + +The oracle is +`packages/query-db-collection/tests/persisted-direct-write-window.oracle.test.ts`. +It has five parts: + +1. A 16-case matrix: 4 write types, 2 lock holders (a held durable write of an + earlier refetch, and startup hydration), and 2 key cases (only the waiting + refetch holds `k`, or both the applied rows and the refetch hold it). It + compares the write's outcome, the visible rows and the Query cache with the + reference, and the stored rows with the visible rows. +2. A 4-case ordering matrix: a third refetch starts before or after the + write is called, and changes the same key or a different key. It returns + while the write waits. +3. A witness for a persisted `writeInsert` of an existing key, with no lock + window. +4. A real-SQLite subset: each write type with the key that only the waiting + refetch holds and an earlier refetch's durable write holding the lock, plus + the four ordering cases. It runs the same drivers over the node SQLite + adapter for sync-present collections, with the same gate on its + `applyCommittedTx`. +5. A calibration test for the harness's failure report. + +Parts 1 to 3 use an in-memory fake adapter. The fake also seeds stored rows, +which the startup holder needs. + +## RED and GREEN + +| Check | `main` 482196ec4 | Branch | +| --- | --- | --- | +| 16-case matrix | 10 fail. Example: `writeUpdate` of `k` rejects with `UpdateOperationItemNotFoundError`; the reference returns `ok`. | 16 pass | +| Ordering matrix | Not run on `main`, which has no wait. On the branch before the ordering fix, the two `after` cases failed: `k` was 9, where the reference gave 6 and 5. | 4 pass | +| Duplicate insert | Fails: `expected 'ok' to be 'CollectionOperationError'` | Passes | +| Real-SQLite subset (8 cases) | 5 fail: insert, update and delete of `k`, and both ordering cases where the third refetch started before the write. Example: `sqlite {"outcome":"UpdateOperationItemNotFoundError",…} != reference {"outcome":"ok",…}`. Upsert and the two `after` cases pass, because `main` applies the write at once. | 8 pass | + +The oracle file passes 30 tests, and its type check reports no errors. The Query Collection package passes 972 of 972 tests with type-checking on, measured before the real-SQLite subset was added. + +## Mutant results + +Each mutant was applied to the reviewed source and reverted afterwards. + +| Mutant | Outcome | +| --- | --- | +| A direct write does not wait for earlier commits | Assertion failure, 9 of 16 matrix cases | +| No duplicate check for a persisted insert | Assertion failure, 4 of 16 matrix cases | +| The write waits only for the first pending commit | Assertion failure, 3 of 16 matrix cases | +| A result that arrives while a write waits is not deferred | Assertion failure, 2 ordering cases | +| The write's own cache update runs the stale-fetch merge | Assertion failure, 2 ordering cases | +| A deferred result is not restored into the cache | Assertion failure, 3 ordering cases | +| The write takes its generation when it applies, not when it is called | Assertion failure, 2 ordering cases | +| The harness rethrows the first cleanup error instead of the AggregateError | Assertion failure in the calibration test | + +## External review of `2e1000a77` + +An external review of the published head `2e1000a77` reported four +correctness findings and two test findings. All six were confirmed. The +differential matrix missed the four correctness findings for two reasons. +Its reference collection runs the same direct-write code. Its ordering cases +observe rows only after every promise settles and 20 more event-loop turns, +and each has only one write. A new section of the oracle, the precedence +model, takes its expected rows from stated rules instead. + +| Finding | Law | RED on `2e1000a77` | Fix | +| --- | --- | --- | --- | +| Rejected writes take precedence over a refetch | Only an accepted write gains precedence over an older fetch | 8 of 8 rejected-write cases: `expected [ { id: 'k', value: 1 } ]` or `[]` `to deeply equal [ { id: 'k', value: 2 } ]` | The write reserves its position when it is called and claims its keys only after validation passes | +| Cleanup lets a waiting write fulfill | A write whose sync run cleanup retired while it waited rejects and stores nothing | 3 of 3 cases: `expected 'ok' not to be 'ok'` | The waiting write checks that its sync context is still current, and rejects with `SyncTransactionAbortedError` | +| A deferred refetch settles before its rows apply | `await refetch()` settles after its result's rows apply | `expected [ { id: 'k', value: 9 } ] to deeply equal [ { id: 'k', value: 6 } ]`; with the merged write held, `expected true to be false` | A stale or deferred result merges once no write waits, and its waiter settles after the merged rows apply | +| A deferred refetch overwrites a later write | A fetch that started before a write never overwrites it | Same key: rows, cache and storage end at `6`, not `10`. Sibling key: `j` ends at `1`, not `10` | The deferred result keeps its own fetch start, and key positions are kept while a deferred result exists | +| The duplicate-insert witness ignores timing | Validation errors reject the returned promise | A synchronous-throw mutant passed the old witness | The witness uses `rejects.toBeInstanceOf(DuplicateKeySyncError)` for both collections | +| `tx: any` in the fake adapter | The adapter matches the persistence contract | No runtime failure; the type was unchecked | The adapter uses `PersistedTx`, and stored rows and keys are narrowed | + +The non-deferred stale-merge path had the same settlement fault as the +deferred one: it wrote the cache and returned without a settlement. Both +paths are now one path. + +Mutants against the precedence model, all assertion failures: + +| Mutant | Result | +| --- | --- | +| Claim keys before validation | 8 failures (the rejected-write cases) | +| No current-context check after the wait | 3 failures (the cleanup cases) | +| Settle before the merged rows apply | 1 failure: the case whose merged write is held. The case without a held merge cannot tell this mutant apart, because the rows apply synchronously there. | +| The deferred result loses its fetch start | 6 failures, including the 4 ownership-lifecycle merge cases | +| Key positions cleared while a deferred result exists | 6 failures, including the ordering and real-SQLite cases | +| Synchronous throw for a duplicate insert | 1 failure (the duplicate-insert witness) | +| Store an unnarrowed transaction value in the fake adapter | Type error at the `rows.set` call | + +## ORC-012 requirement audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Applicable. The law and its authority are above. The full matrix uses an in-memory adapter and the two lock holders named. The real-SQLite subset covers the held-durable-write holder only. | +| ORC-002 | Applicable. The matrix's expected result comes from a Query Collection without persistence. It does not use the persistence wrapper, which is the code under judgment. It shares the Query direct-write code, so the precedence model covers that code with expected rows written from its stated rules. | +| ORC-003 | Applicable. The opening prose states the law, the reference, the lock holders, the key cases, the observations and the limits. The ordering section and the witness each have their own prose. | +| ORC-004 | Inapplicable. The oracle is a finite matrix, not a generated history. Every case in the claimed matrix runs. | +| ORC-005 | Applicable. The driver calls the public `utils.write*` and `utils.refetch` on a collection built with `persistedCollectionOptions`. It checks that it reached the window: when the write runs, storage does not hold the waiting refetch's row. It observes the write's outcome, the visible rows, the Query cache and the stored rows after every promise settles. | +| ORC-006 | Applicable. Eight mutants fail at the intended checkpoint, as listed above. | +| ORC-007 | Inapplicable. No generated property exists. | +| ORC-008 | Inapplicable. No stateful reference model exists. | +| ORC-009 | Applicable. "Window" means the time between a refetch commit and its durable write while another task holds the lock. "Lock holder" means the task that holds the apply lock. Neither is a production state. | +| ORC-010 | Applicable. Each failure message carries both observations. Every driver runs its cleanup through `checked`. When the driver fails and a cleanup step also fails, `checked` throws an AggregateError whose `cause` is the driver's failure and whose `errors` list it first, then each cleanup failure. The calibration test forces a cleanup failure after an assertion failure and checks that the report names the assertion. Comparisons in each test body run after the driver returns, so a cleanup failure cannot hide them. | +| ORC-011 | Applicable. The shared Query direct-write code was a real shared-fault risk: the external review found two faults the differential reference could not see. The precedence model is the second formulation, with expected rows from stated rules. | +| ORC-013 | Applicable to the ordering rule. The `before` and `after` cases are opposite sides of the boundary "the fetch started before or after the write was called". A mutant that takes the generation when the write applies moves that boundary, and the `after` cases reject it. | +| ORC-014 | Applicable. The fake adapter supplies the durable-write delay in parts 1 to 3. Part 4 is the receiving witness: the node SQLite adapter supplies the same premise, a held durable write of an earlier source commit, and stores the rows. It fails on `main` and passes on the branch. The startup holder has no real-SQLite witness. | +| Mutation confirmation as a lock holder | Inapplicable. `persistAndConfirmCollectionMutations` (`packages/db-sqlite-persistence-core/src/persisted.ts:2141`) is called only by the sync-absent wrappers `wrappedOnInsert`, `wrappedOnUpdate` and `wrappedOnDelete` (`persisted.ts:5025`, `5043`, `5061`) and through `acceptMutations` (`persisted.ts:2200`, exposed at `5069`). `persistedCollectionOptions` (`persisted.ts:4906`) builds those only when the options have no `sync` key (`persisted.ts:4935`). A Query Collection always has `sync`, so it is sync-present and never takes the lock this way. | + +## Unresolved + +- The startup-hydration holder runs only over the fake adapter. diff --git a/docs/contributing/oracle-reviews/issue-2034-bounded-follow-up.md b/docs/contributing/oracle-reviews/issue-2034-bounded-follow-up.md new file mode 100644 index 0000000000..547baf9942 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2034-bounded-follow-up.md @@ -0,0 +1,101 @@ +# Bounded deferred-item follow-up + +This follow-up starts at `f982b8e04017d82c24ac147c84155858421a5d0b`. The user +requested bounded work on all eight outstanding review items. The table separates +repairs, evidence for the already selected reset policy, and the remaining work. +No native wire support or automatic destructive migration is added. + +| Review item | Evidence and change | Disposition | +| --- | --- | --- | +| HE-007, repeated native encoding | EQ previously parsed twice; three-element IN parsed twelve times. Compilation now retains the order and identity from one validated encoding and does not compile an IN list as a discarded scalar. Both native kinds have a 1/3/1,025-occurrence work bound plus the existing semantic suites. | Fixed. Recorded EQ is one parse; three-element IN is three. This is a construction-work result, not a latency claim. | +| HE-008, repeated wire projection | Previous controlled loads observed two projections on success and four across a failed attempt and retry. The coordinator also creates a detached snapshot. A new same-input/later-call witness rejects a global identity-cache mutant at the second snapshot's value assertion. Early admission remains necessary across hydration and role changes. | Open. A bounded inspection found no safe guard deletion or transparent global cache. The persisted/coordinator owners retain validation-only traversal or an explicit prepared-request boundary, including lifecycle references, ownership identity, and receiver checks. Neither design was implemented in this pass. | +| HE-010, obsolete indexes | An explicit schema reset removes both old/current signature entries and physical indexes before reading the new schema, preserves another collection's indexes and rows, and retains the replacement index on same-schema reopen. Skipping registry deletion fails the new test at the registry checkpoint. | Selected reset path verified. Automatic cleanup while preserving old cache contents remains outside the accepted reset policy. | +| HISTORY-002, ambiguous old markers | The reset witness installs frozen old JSON containing an unescaped marker-shaped ordinary record. The first new-schema read is empty; re-seeding with the current writer preserves that record after reopening. | Selected reset path verified. Already-erased types and ambiguous old bytes are not reconstructed. | +| BOUNDARY-004/HISTORY-004, mutable own-tag metadata | A plain own-tag record allowed a returned metadata snapshot to mutate the internal literal. A null-prototype own-tag record failed during createIndex. Both now follow ordinary record serialization and cloning; genuine immutable native values remain native. | Fixed for plain/null-prototype records. Custom-prototype impersonators remain outside the native metadata contract; the existing SQLite owner rejects a false native brand atomically. | +| BOUNDARY-005, signature collisions | A Temporal.Instant and an ordinary `{ __type, value }` record had equal public metadata signatures. Ordinary tagged records now receive an escape envelope. Metadata encodes values once, so generated native tags are not mistaken for user records on a second pass. Twelve fixtures compare every signature pair across equivalent native values, key-order variations, native/tagged pairs, nested mixtures, and the escape envelope. A real persisted wrapper receives the native and ordinary definitions: the old source retains only one physical index, while the repair retains two. | Fixed for the declared literal domain. No claim about function identity or arbitrary custom classes. Ordinary tagged-record signatures can change; the documented reset policy applies. | +| VERIFY-NUL-NOTE, remaining NUL paths | Mixed native/string runtime membership passed. NUL-bearing expression-index creation failed SQL parsing. SQL literals now use escaped JSON extraction when NUL is present, reusing the storage representation without a new host API. Four NUL/Unicode strings cross ordinary/native-mixed lists and positive/negated membership. Tests inspect stored expression values, final keys and candidate containment. | Fixed at the persisted-expression boundary. Existing direct equality and runtime membership coverage remains. | +| CR-003, revival helper kind | The helper and its constructor lookup now accept only `Temporal.Instant` or `Temporal.PlainDate` statically. The decoder already dispatches only those cases. TypeScript verifies the narrower calls. | Static API narrowing complete. No additional runtime behavior or constructor registry is introduced. | + +## RED/GREEN and calibration + +The seven initial probes produced six failures and one passing mixed-runtime +control. The production createIndex/ensureIndex exceptions are failures at the +requested public boundary, not harness setup failures. + +The permanent checks were then run through a loader that substitutes exactly the +pre-fix adapter and Collection index source from the starting commit. The reach +record confirms both originals loaded. Nine tests fail: four NUL index witnesses, +two encoding counters, two ordinary metadata records, and the signature relation. +The genuine-native, explicit-reset and fresh-projection controls pass. No current +production file was replaced during this comparison. + +Two additional scratch mutants fail at their intended assertions: + +- Omit registry deletion during reset: two obsolete rows remain instead of none. +- Cache wire projections globally by input identity: the next projection retains + the previous value instead of observing the next call's input. + +Those mutations do not change current production. They prove the new checks +reject those particular wrong designs, not every possible migration or cache. +The final surrounding run, type check, and lint result are appended below. + +## Test gaps and boundaries + +The old expression-index fixtures covered runtime NUL bindings but omitted NUL +inside index literals. Existing native rank/text tests observed semantics but +not repeated compilation work. Metadata tests covered equivalent definitions +without ordinary records shaped like native tags or mutation through returned +own-tag snapshots. Those missing distinctions are now executable. + +The Collection index-value oracle owns an independent fixture-equivalence and +snapshot-isolation law. The existing typed-value, expression-index, resume and +persisted owners receive the other additions. This is one new oracle owner, +plus extensions to four existing owners; it is not five new models. + +ORC-001/002/003/005 use the public metadata/snapshot, literal preservation and +selected reset laws, independent fixture classes/keys and explicit checkpoints. +ORC-004/007/008 do not apply to these fixed bounded histories; no new random +campaign or stateful reference model is claimed. The new metadata owner joins +the package oracle command. ORC-006 is demonstrated by original-source RED and +the two reached mutants. ORC-009 retains existing terms. ORC-010 uses Vitest +teardown or the existing failure-preserving cleanup. ORC-011 has no new shared +semantic-model hypothesis; stored index values separately expose DDL results +before residual filtering. ORC-013 uses neighboring NUL positions, ordinary/native +pairs, key order, another collection, and a same-schema reopen. ORC-014 claims +only the tested Node SQLite and controlled wire boundaries, not native browser, +Electron, or mobile receiving behavior. This record supplies ORC-012 evidence. + +The bounded audit accounts for all eight items: five fixes, two verified uses of +the selected reset policy, one open efficiency item with a concrete design +boundary and owner. It does not convert that open item into a refutation. + +## Final verification and code weight + +Nine affected suites pass 575 tests with zero failures and one existing TODO. +A final receiving witness added afterward passes separately: both public index +requests retain distinct registry entries and physical indexes. The same witness +fails against the original Collection index source (one index instead of two). +TypeScript passes. Changed-file ESLint has zero errors and 22 pre-existing +require-await warnings in the persisted owner. The new code adds no lint warnings. +The actual Prettier CLI and `git diff --check` pass. + +Production changes add 56 lines and remove 34 (net +22), with no persistent cache, +new retry state, or automatic reset. Tests add 380 lines across five test files; +one new oracle owner is registered with the existing package oracle command. +Documentation and the changeset record the fixes and remaining projection work. + +Local receipts: `temporal-bounded-owner-final-red.json` (nine reached original +failures), `temporal-bounded-final-surrounding.json` (575 passed, one TODO), +`temporal-bounded-signature-receiver-{red,green}.json` (one index versus two), +`temporal-bounded-reset-mutant-red.json` and +`temporal-bounded-projection-mutant-red.json` (one intended assertion failure +each). All are under `/private/tmp`; the laws, bounds, observed results and limits +are preserved in this versioned record and the executable owners. + +## Subsequent HE-008 repair + +The [validation-work follow-up](issue-2034-wire-validation-work.md) supersedes +HE-008's open disposition above. Its oracle was red before the repair. Shared +validation-only traversal removes discarded snapshots while retaining admission, +receiver validation, detached output, and local lifecycle identity. The historical +bounded-pass verdict remains unchanged as evidence of what that pass established. diff --git a/docs/contributing/oracle-reviews/issue-2034-design/README.md b/docs/contributing/oracle-reviews/issue-2034-design/README.md new file mode 100644 index 0000000000..baf0135d08 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2034-design/README.md @@ -0,0 +1,89 @@ +# Temporal persistence: design grammar and stress readings + +The implemented **[v2 design grammar](v2.md)** is the current result. Its linked +model, evidence and process layers record native Instant/PlainDate support, +translations into the existing oracles, final validation and remaining limits. +The v1 discussion below is a frozen historical record, including choices that +the user subsequently settled. It does not describe the current test status. + +## Historical v1 reading + +Current direction after the user's clarification is recorded in the +[user recheck](user-recheck.md): native types must roundtrip, existing persisted +data may be replaced, and Temporal queries should extend the existing +SQL-plus-residual pipeline. The original readings below remain the frozen record. + +The worktree is current with `origin/main` fetched on 2026-10-05 at +`e21c280f3`, including #2002's persistence/refetch ordering repair. The unchanged +reproduction still reports **13 passing tests and 8 failing Temporal cases**. +The [replay receipt](../issue-2034-temporal-persistence.md) records the setup. + +The grammar treats this as a value-preservation repair across existing +boundaries. A codec can preserve an Instant in a row while a wrapper loses that +same Instant in an index expression. A query can revive the right values yet +discard the wrong rows in SQL before JavaScript sees them. + +A case is described by: + +```text +value × carrier × operation × execution boundary × environment × legal history +``` + +For example: PlainDate × row metadata × reopen × SQLite adapter × registered +polyfill × update. These are constrained test dimensions, not new production +classes or a mandate to enumerate every combination. + +The [frozen grammar](model.md) permits variations subject to four groups of +constraints: + +- Preserve supported kind/value in rows, nested data, metadata and replay. + Keep strings, existing supported types and ordinary records intact. Genuine + brands and constructor availability are separate requirements. +- Separate storage representation, equality and ordering. Prove SQL selection + safe before applying it; preserve final filtering/order/windows and alignment + with runtime literals and index expressions. Untyped fields and Boolean + composition remain unresolved. +- Preserve adapter rollback and validation of superseded actions, wrapper + publication-before-durability, receipts/FIFO, binding caps and driver ownership. + Extend existing oracle owners without adding a transaction lifecycle. +- Qualify support by constructor, host and public route. Establish old-byte and + reader policies; already-lost `{}` values cannot be reconstructed by guessing. + +The unranked adjacent forms are tagged support with residual query execution, +the same support augmented by proved SQL/index representations, and explicit +rejection as a safety-only form that does **not** deliver Temporal support. +Their costs and missing evidence remain open. + +The [owner map](evidence.md#primary-oracle-ownership-to-retain) attaches value +reopen, action folding/rollback, snapshots, SQL/index planning, binding limits, +wrapper receipts and Query ownership to their current executable owners. +Direct SQLite results do not certify Expo, OPFS, Electron or coordinator routes. + +The requested stress readings returned: + +- **[Hostile assay](hostile.md):** the independent auditor found an upstream + index serializer that erases Temporal literals and an oracle clone operation + that erases them too. Other conditions concern query rejection/Boolean + polarity, schema fences that can reset durable data, and unsupported wire + requests entering retries. Each finding has a checkpoint and repair condition; + the latter scenes are source-grounded constructions, not observed patch failures. +- **[Fracture scan](fracture.md):** recognizing a new unescaped marker could + reinterpret an existing plain record. This defeats a naive tag extension; + no unconditional fracture of the conditional candidate was established. + Executed probes also show canonical Instant text is not a safe lexical order + key, and PlainDate order-equivalence need not imply DB equality. +- **[Tension scan](tensions.md):** three provisional, unranked options remain: + exact query semantics versus selective SQL execution; native reconstruction + versus runtime/public-route portability; and format extension versus existing + data/reader compatibility. User-fit selection remains open. Rollback versus + prior publication is a boundary distinction, not an invented competing demand. + +This is an exploratory grammar with provisional preservation properties. +Source reconstruction and component-removal controls are recorded in +[process.md](process.md); they are not an implementation proof. No independent +held-out range case was supplied, and no formal model check was applicable to +this static design grammar. It excludes spoofed brands and unsupported Temporal +kinds. Decomposition can hide scheduling/host interactions; hostile and fracture +readings can overemphasize vivid corner cases; tensions can manufacture symmetry. +SQL/error admissibility, read cost, constructors, migration and route support +remain unsettled. No production fix or bug-class closure is claimed. diff --git a/docs/contributing/oracle-reviews/issue-2034-design/candidate.md b/docs/contributing/oracle-reviews/issue-2034-design/candidate.md new file mode 100644 index 0000000000..cff84100de --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2034-design/candidate.md @@ -0,0 +1,55 @@ +# One candidate for blind hostile assay — C1 + +Declared claim: add built-in lossless Temporal persistence to TanStack DB's +shared SQLite adapter, initially Instant and PlainDate, while preserving its +existing query, transaction, wrapper and index contracts. A serializer-only +change is insufficient. + +Proposed design: +1. Extend the existing tagged serializer with explicit Temporal kind and +lossless canonical representation. Cover rows, arrays/records, inline and +separate row metadata, collection metadata, replay and tombstones. Validate +real Temporal brands rather than trusting a spoofed toStringTag. Decode with +native or registered polyfill constructors, following existing offline +serializer convention. Missing required constructors reject explicitly. +2. Preserve DB's existing distinction between equality normalization and +semantic ordering; reuse Temporal-aware comparison behavior in persistence's +local sort. Keep strings and Date/BigInt behavior unchanged. +3. Preserve public query results by emitting SQL only where candidate selection +is exact or demonstrably conservative, otherwise use existing residual +execution. No SQL precision-losing Date/float conversion. Scalar/IN/cursor/ +index-literal representations and normalized index SQL must agree. How to +classify untyped field-to-field expressions and all Boolean compositions is +not yet specified; performance cost of broad fallback is an open requirement. +4. Keep current action folding, validate required intermediate serialized +values, preserve adapter atomic rollback/positions/replay, host binding caps, +transaction-driver ownership, existing physical-index rebuild and wrapper FIFO. +Do not introduce a new transaction lifecycle. Adapter rollback does not imply +undoing a value already published by the persisted wrapper. +5. First-party coordinator transports and host runtime receipt paths require +separate support evidence. SQLite's new codec cannot silently expand those +wire contracts. Whether the first implementation includes or explicitly excludes +these routes remains unresolved. Other Temporal kinds are not initially promised. +6. Extend existing oracle owners: direct file SQLite for typed values and +reopen; ordinary-work owner for folding/rollback; expression-index owner for +captured SQL before re-filtering, bindings/index use/upgrades; wrapper/Query +owners for errors/publication/FIFO; receiving host/coordinator tests where those +paths are claimed. Do not replace them with another reference scheduler. + +Success standard: supported inputs retain native kind and value across every +claimed carrier and boundary; query public results match established DB +semantics; required rejection leaves prior durable state/positions unchanged; +existing receipt/FIFO and work/cap laws remain; claims are limited to reached +production observations and current declared scope. No implementation exists +for this candidate. A lossless serialization string is not asserted to be a +valid comparison key. Stored {} corruption cannot be guessed back into dates. + +Open conditions: exact SQL admissibility mechanism; global versus explicit +constructor provider; reserved-marker collisions and old readers; coordinator +scope; expansion beyond the two reported Temporal kinds. These are known +unresolved details, not assumed solved. Find whether they defeat the structural +claim or hide additional necessary conditions, not merely restate the list. + +Source trace is in [evidence.md](evidence.md). You may inspect +only those source files and relevant existing oracle owners/limits for evidence. +No sibling candidate, preferred outcome or later stress reading is supplied. diff --git a/docs/contributing/oracle-reviews/issue-2034-design/evidence.md b/docs/contributing/oracle-reviews/issue-2034-design/evidence.md new file mode 100644 index 0000000000..8e124c415d --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2034-design/evidence.md @@ -0,0 +1,84 @@ +# Frozen source basis for Temporal persistence design + +Repository: /Users/kyle.mathews/.codex/worktrees/temporal-persistence-oracle/tanstack-db +Production revision: e21c280f3c4e56f6c0d05ec0eed87db29c623489 (origin/main fetched 2026-10-05). +Original reproduction revision: 84dc899bfc8d6a16487c1584dbb1cb177109aacc. +The new main commit is #2002, persisted Query write/refetch ordering. +Uncommitted reproduction is unchanged; rerun on e21c280f reports 21 tests, +13 pass / 8 fail. Runtime Node 24.19.0 / SQLite 3.53.3 / temporal-polyfill 0.3.0. + +This is one source system: TanStack DB's current shared SQLite persistence, +its value consumers, and its declared oracle boundaries. No external survey. + +E1 Observed implementation: sqlite-core-adapter.ts:327-425 recursively handles +Date/BigInt/nonfinite numbers, arrays and enumerable object properties. It has +no Temporal handling; the real adapter reproduction observes {} after reopen. +E2 Source contract: sqlite-core-adapter.ts:2440-2525 SQL-selects a candidate set, +then decodes, applies DB's expression evaluator, locally sorts, and paginates. +SQL may eliminate candidates before the residual evaluator can see them. +E3 Observed implementation: sqlite-core-adapter.ts:512-578 treats non-Date +objects by runtime identity for local ordering. db/src/utils/comparison.ts:228-255 +uses Temporal kind plus string representation for equality normalization; +:310-349 orders supported Temporal kinds through their compare function. +Equality and ordering equivalence are distinct contracts. +E4 Source contract: sqlite-core-adapter.ts:681-707 shares normalized reference +SQL with runtime predicates and index expressions. :2038-2110 rebuilds a stale +physical index when normalized SQL differs. Literal-bearing serialized index +specifications pass through decodePersistedJsonValue at :1100-1135. +E5 Source contract: sqlite-core-adapter.ts:921-954 lowers runtime IN to json_each +with one binding; index-context IN is literal. :2434-2481 drops SQL pushdown +when the final statement exceeds the driver cap. Public results and the host +binding cap must both hold. Binding-capacity oracle starts at :2711 in +sqlite-core-adapter-oracle.test.ts; expression-index owner is in Node tests. +E6 Source contract: sqlite-core-adapter.ts:1597-1930 applies rows, tombstones, +row metadata, collection metadata, position and replay under one transaction. +Repeated-key folds validate superseded values (:1684; lastMetadataByKey). +ordinary-transaction-work-oracle.ts owns distinct-key bounded statement work +and rollback/invalid superseded action witnesses. A final valid value does not +excuse an earlier invalid action. +E7 Source contract: persisted-oracle.test.ts:58-95 requires publication before +durability, observable rejected wrapped receipt and fail-stop for durability +failure, and no admitted suffix. New #2002 preserves durable FIFO while an +immediate source commit releases an earlier waiting publication. Adapter +atomicity does not mean rollback of already published Collection state. +E8 Source contract: remote-subset-wire.ts:23-37 enumerates the transport domain; +:130-138 validates before local/remote route choice. Ordinary custom-prototype +values are rejected via projectWireRecord and assertExactPrototype. Temporal +has no listed transport case. persisted.ts:792 uses this projection even for +the SingleProcessCoordinator request path. No direct-core test proves this path. +E9 Source precedent only: offline-transactions/src/outbox/TransactionSerializer.ts +:9-78 recognizes an allowlist, resolves global Temporal constructors, invokes +brand-checking prototype methods, and rejects absent constructors. It is not +a SQLite support contract or a dependency to add blindly. +E10 Test evidence: sqlite-temporal-value-oracle.test.ts is a direct core/file +SQLite reproduction covering Instant/PlainDate and Date/ISO controls, insert/ +update, metadata/replay, equality/range, one-row windows, index presence. +It is neither a provider-host test nor proof of index-plan use or all values. + +Authority labels: E1-E10 are source facts/declared laws/test results. The user's +request establishes the target of correcting silent Temporal loss and meshing +with existing oracle work. Proposed support policies remain analyst inferences. + + +## Primary oracle ownership to retain + +Paths below are relative to `packages/` at the frozen production revision. +They locate current executable contracts, not new independent lifecycle models. + +| Law | Primary owner | Needed Temporal extension | +| --- | --- | --- | +| Kind/value after physical reopen, nested payloads and metadata/replay | `db-sqlite-persistence-core/tests/sqlite-temporal-value-oracle.test.ts` (new reproduction) | Real brand checks; precision/calendar domains; explicit constructor failure; data versus tag collisions. Current 16 cases plus 5 calibrations are narrow diagnostic evidence. | +| Ordered actions, atomic durable rollback, superseded-value validation and bounded statement work | `db-sqlite-persistence-core/tests/ordinary-transaction-work-oracle.ts:22`, invalid-overwrite witness at `:1445` | Add supported/rejected native-value dimensions to existing legal action histories. Its current Row type is string/number. | +| Atomic rows/metadata/position generations and schema fences | `db-sqlite-persistence-core/tests/sqlite-resume-snapshot-oracle.test.ts:214` | Constructor/format failure at snapshot read and schema/index upgrade cuts; do not claim public rollback. | +| SQL truth before residual correction, matching index expressions and actual named-index use | `node-db-sqlite-persistence/tests/expression-index-oracle.test.ts:1`, normalized index replacement at `:1337` | Distinct equality/order keys, scalar/IN/index literals, precision/calendar, old physical index cases. | +| Statement-wide binding capacity and scoped-driver propagation | `db-sqlite-persistence-core/tests/sqlite-core-adapter-oracle.test.ts:2708` | Typed IN, Boolean composition and both cursor queries at existing cap boundaries. | +| Wrapped source receipts, fail-stop, publication/durability and FIFO | `db-sqlite-persistence-core/tests/persisted-oracle.test.ts:55` | Value-encoding failures before/after each existing cut, including queued immediate source writes. Precommit cancellation at `:3699` remains a different contract. | +| Query result ownership across refetch/write overlap | `query-db-collection/tests/ownership-lifecycle.oracle.test.ts` (#2002) | Receiving witness when a typed value or codec error enters accepted source commits; no second Query result controller. | +| Driver rollback/admission and receiving host execution | `db-sqlite-persistence-core/tests/contracts/sqlite-driver-contract.ts:5`; `node-db-sqlite-persistence/tests/node-persistence.test.ts:58` | Reuse driver harnesses. Direct node:sqlite cannot certify Expo/OPFS/Electron transport or scheduling. | +| Local/transported subset value admission | `db-sqlite-persistence-core/tests/persisted-oracle.test.ts` plus receiving coordinator suites | Decide and test typed wire transport. `SingleProcessCoordinator` also invokes the projection. | + +The ordinary-work budget is explicitly described as a proposed deterministic +work contract, not browser latency evidence. The #2002 receiving Node test +checks immediate source writes; the committed map leaves the reported two- +refetch Node schedule and Expo scheduling open. None of these owner limits is +retired by the Temporal design. diff --git a/docs/contributing/oracle-reviews/issue-2034-design/fracture.md b/docs/contributing/oracle-reviews/issue-2034-design/fracture.md new file mode 100644 index 0000000000..eb04053f03 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2034-design/fracture.md @@ -0,0 +1,84 @@ +# Fracture scanner — C1, frozen-scope reading + +Position: C1 from [candidate.md](candidate.md), with source E1–E10. Core claim: +built-in lossless support for Instant/PlainDate can refine the existing shared +SQLite system if value, query, boundary, rejection, and compatibility obligations +are satisfied together. Scope includes each claimed carrier/path; unidentified +SQL lowering, constructor provisioning and reader compatibility are explicitly +open, not assumptions that the implementation already meets them. + +Frozen premises: canonical tagged payload preserves runtime kind/value; DB +equality and ordering remain separate; SQL candidates must be exact or a +superset; current durable and wrapper laws remain; unsupported boundary claims +need separate evidence. Success is public semantic refinement under those +conditions, not merely successful serialization. Protected insight: a shared +value codec is the natural place to close the repeated loss across carriers. + +## Result + +No unconditional internal fracture of that conditional claim was established. +The run exposes one conditional fracture of the smallest tagged-format +implementation, and rejects two tempting overclaims as already-excluded designs. +This does not show C1 is implemented or cheap: several necessary mechanisms +remain unresolved. + +FRA1 — Adding an unescaped tag can violate preservation of existing plain data. +Route: internal extension and constructed counterexample. +Premise chain: retain existing plain objects (I2) → add a new recognized tag in +the existing object namespace (permitted simple implementation of C1) → decode +by marker membership (E1) → a previously ordinary record becomes Temporal. +Minimal case: before upgrade store a plain object with +__tanstack_db_persisted_type__='Temporal.Instant' and +value='2026-01-02T00:00:00Z'. Current tag recognition excludes that string, so +it remains a record. Extending the recognized tag set without an escape/version +rule makes the same bytes a native value. The object contains no genuine +Temporal brand; encode-time brand validation alone cannot disambiguate old bytes. +Admissibility: a plain record is within I2's domain; the example uses the exact +existing reserved-marker fields; it varies an explicitly open namespace choice, +not constructor availability or a host outside the claim. The consequence is +type mutation of ordinary data, not a security attack. +Evidence kind: construction grounded in current encoder/decoder source, not an +executed new implementation. It defeats the naive unescaped extension, not C1 +with its unresolved U3. The hidden condition is unambiguous representation +provenance across reader generations. Preserved insight: lossless tags still +work when a justified escaping/envelope/version policy distinguishes user data. +Weakening evidence: a documented exclusion already reserves future marker names, +or a compatible disambiguation mechanism prevents those bytes being accepted as +ordinary data. None was established in this run. Repair condition: decide and +witness the namespace and old-byte policy before claiming compatibility. + +FRA2 — Lossless strings do not imply correct SQL ordering: excluded wrong design. +A bounded Node/SQLite probe used Instant values +1970-01-01T00:00:00Z and 1970-01-01T00:00:00.000000001Z. +Temporal.Instant.compare returned -1; SQLite '? < ?' on their strings returned +0. Canonical ISO text has variable precision, so raw lexical comparison fails +on these adjacent instants. This is an executed scalar/SQLite result, not a +production-patch test. C1/R3 already deny that serialization text is necessarily +a comparison key. Therefore it is a calibration of that boundary, not an +internal fracture of the actual frozen candidate. + +FRA3 — Equal order is not equal identity: excluded wrong design. +The same probe compared PlainDate 2026-01-02 with its Japanese-calendar form +2026-01-02[u-ca=japanese]. Temporal.PlainDate.compare returned 0, while kind-plus- +text equality was false, matching DB's existing equality normalization rule. +A single comparator-derived key for every operator would violate I3. C1 already +separates these operations. The narrower retained claim is operation-specific +query semantics, not one universal SQL key. + +## Controls and limits + +Outside-standard rebuttal rejected: 'Any need to register a polyfill is bad API +design' does not by itself refute a candidate that explicitly requires a +constructor capability. It is a preference or tension to examine separately. +Vivid near-counterexample rejected: reopening on a device without the required +constructor and receiving the documented explicit error does not defeat the +supported-environment preservation promise. It does show that broader native- +host support has not been established. +A source durability failure after prior publication also does not refute C1's +adapter atomicity: C1 explicitly preserves distinct public/durable checkpoints. + +Distortion: the concrete marker and nanosecond examples make one compact corner +case unusually salient. They do not measure frequency, overall repair size, +user impact or complete cross-host behavior. The conditional fracture must not +be upgraded to a claim that all tagged persistence is unsound. No grammar rules +were revised during the scan and no production code changed. diff --git a/docs/contributing/oracle-reviews/issue-2034-design/hostile.md b/docs/contributing/oracle-reviews/issue-2034-design/hostile.md new file mode 100644 index 0000000000..ee75b49d87 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2034-design/hostile.md @@ -0,0 +1,156 @@ +# Hostile failure assay — C1 + +A fresh auditor received only [C1](candidate.md), its source basis, and the +instruction to assume failure. It did not receive sibling forms, a preferred +outcome, or the fracture/tension readings. It verified production revision +`e21c280f3c4e56f6c0d05ec0eed87db29c623489`. No candidate implementation exists. +The five findings below are unranked conditions, not a claim that every coherent +implementation of C1 fails. Source line numbers refer to that revision. + +## HA1 — Index literals can disappear upstream of the adapter + +**Broken claim:** C1 §3 requires agreement between runtime literals and persisted +index expressions. The wrapper independently serializes index expressions +through `toStableSerializable` (`src/persisted.ts:1134`), which reduces Temporal +objects to their enumerable fields. `buildPersistedIndexSpec` uses that result +(`:3785`). Both tested polyfill kinds have no enumerable fields. A correct row +codec and compiler cannot recover an index literal already reduced to `{}`. + +**Evidence:** source-backed, with the empty-field premise observed in a +standalone probe. The repaired wrapper path remains unresolved. The Node +expression-index oracle constructs specifications itself and calls the adapter +directly (`tests/expression-index-oracle.test.ts:505`), so expanding only its +literal domain misses this boundary. + +**Repair condition and disposition:** include the wrapper's specification +boundary wherever literal-bearing indexes are claimed. Preserve or explicitly +reject Temporal literals there, and test wrapper-created specifications through +captured SQL and the planner. Direct-adapter results alone cannot discharge this +obligation. + +## HA2 — Extending the oracle can reproduce the corruption in its model + +**Broken claim:** C1 §6 relies on existing owners to prove preservation through +action folding and rollback. The ordinary-work owner clones values using +`structuredClone` (`tests/ordinary-transaction-work-oracle.ts:97`). On Node +24.19.0 with temporal-polyfill 0.3.0, a standalone probe converted both Instant +and PlainDate into unbranded empty objects. + +**Evidence:** observed clone behavior, not a violation of the existing oracle, +whose row domain is string/number. Merely widening its inputs would make the +model discard the information that the extension must protect. A value-erasing +implementation could agree with that corrupted reference at durable checkpoints. + +**Repair condition and disposition:** retain the existing owner, but give its +new value domain independent intrinsic kind/value observations. Check successful +writes, rollback, replay and reopen where owned, and demonstrate rejection of a +value-erasing wrong design. Existing clone/comparison assumptions cannot transfer +unchanged. + +## HA3 — Candidate containment alone does not settle query outcomes + +**Broken claim:** C1 §3 promises existing query semantics while permitting +conservative SQL selection. SQL removes rows before the residual evaluator runs +(`src/sqlite-core-adapter.ts:2448`). DB ordering throws when two Temporal operands +have different kinds (`packages/db/src/utils/comparison.ts:310`). Preserving +every row for which a predicate returns TRUE does not establish what happens to +rows whose evaluation rejects. Negating an overapproximation also reverses its +containment direction; the current compiler directly composes `NOT` +(`src/sqlite-core-adapter.ts:905`). + +**Evidence:** source-backed premises and constructed defeaters, not executed C1 +failures. A decisive witness would show residual-only rejection becoming a +fulfilled result, or a negated conservative leaf losing a matching key. The +witness needs correctly encoded values so corruption cannot explain the result. + +**Repair condition and disposition:** define admissibility for partial +evaluations and complete Boolean expressions, and compare outcomes as well as +keys. C1's open SQL classifier includes an observation-contract decision; this +reading does not invent an unconditional requirement to evaluate every branch +or row. Whole-predicate residual execution can discharge some cases, subject to +the separately open cost requirement. Frozen I4 is necessary, not sufficient for +the full semantic claim. + +## HA4 — A format fence can invoke destructive reset semantics + +**Claim at risk:** preservation of existing durable data under a format rollout. +C1 explicitly leaves old-reader compatibility open; it has not promised a +reversible rollout. The additional constraint is that the available schema +mechanism is not a neutral codec-version fence. Its default is +`sync-present-reset` (`src/sqlite-core-adapter.ts:1284`). Mismatch handling deletes +rows, tombstones, replay records, index registry entries and collection metadata, +then resets the position (`:2827`). Leaving the encoding unversioned instead +lets an old decoder treat an unfamiliar Temporal marker as an ordinary record +(`:306`). + +**Evidence:** source-backed mechanisms; applying them to a Temporal rollout is a +constructed scenario. This is a conditional implementation failure, not a +refutation of C1's acknowledged open compatibility policy. + +**Repair condition and disposition:** decide and witness upgrade/downgrade +behavior for retained bytes, including metadata and replay, before selecting a +fence. An incompatible reader must not silently reinterpret values or trigger +an unintended reset. Existing physical-index rebuilding does not establish +value-format compatibility. Migration evidence remains unresolved. + +## HA5 — Excluded wire values need admission behavior + +**Broken claim:** C1 §5 permits excluding coordinator support while preserving +existing ownership/work behavior. Temporal has no wire-domain case; custom +prototypes fail projection (`src/remote-subset-wire.ts:702`). For reachable +non-single-process demand, a failed coordinator request is queued again; the +queue retains failures and schedules another retry (`src/persisted.ts:1901`, +`:3293`). Permanent unsupported input differs from a transient transport failure. + +**Evidence:** source-backed with an unresolved receiving witness. The ordinary +wrapper route bypasses these coordinator queues for `SingleProcessCoordinator` +(`:3238`). That coordinator's separately callable projection method does not +prove every single-process wrapper query fails. This qualifies the reach implied +by E8 and the frozen ownership table; it does not expand the support claim. + +**Repair condition and disposition:** either prove supported transport/receipt +paths, or make excluded operations observably inadmissible without perpetual +retry or retained demand. Documentation alone does not specify this behavior. +The wrapper/coordinator owner needs a receiving witness while demand is retained +and after release/abort. This does not justify a replacement retry lifecycle. + +## Concrete failure scene + +Create an index through the wrapper on `coalesce(when, I)`, where `I` is an +Instant and one row has `when: null`. Assume C1's row codec and direct-adapter +compiler are correct. If the wrapper's current specification serializer remains, +it sends `{}` in place of `I`. The receiving compiler cannot reconstruct the +intended expression. Runtime and index SQL diverge, or index creation rejects. +The wrapper can log index-creation failure while continuing +(`src/persisted.ts:3813`). Final row tests may pass through fallback while the +claimed index contract fails. + +This scene is constructed from verified source, not executed against a C1 +implementation. Its decisive checkpoint is the wrapper-emitted specification, +then actual normalized SQL and named-index use. + +## Controls, evidence boundaries and recording + +The auditor read the candidate, source trace, applicable repository guidance, +oracle guide and relevant owner limits. It did not read sibling designs or edit +repository files. Standalone probes established polyfill behavior only. Another +probe found ISO- and Gregorian-calendar PlainDates that compare equal but retain +different strings; the current two-day oracle explicitly excludes calendar +variation (`tests/sqlite-temporal-value-oracle.test.ts:33`). No repaired adapter, +native-host, cross-process, crash or performance result follows. + +Paths beginning `src/` or `tests/` above are under +`packages/db-sqlite-persistence-core/`, except the explicitly identified Node +expression-index owner. Full owner mapping is in [evidence.md](evidence.md). + +The primary agent checked the cited serializer, cloning, comparison, routing, +retry and reset mechanisms. This record preserves all five returned attacks, +their evidence classifications, the concrete scene and the controls. It narrows +HA4 to the compatibility condition C1 actually leaves open and makes the +SingleProcess routing qualification explicit. No frozen grammar rule was +silently revised and no finding is ranked. + +**Induced distortion:** assuming failure can turn every unresolved detail into +an apparent architectural defect. These are necessary conditions and receiving +evidence obligations. Broad residual execution or an enforced narrower scope +can discharge some of them; that is not evidence they are already discharged. diff --git a/docs/contributing/oracle-reviews/issue-2034-design/model.md b/docs/contributing/oracle-reviews/issue-2034-design/model.md new file mode 100644 index 0000000000..b61bc933a1 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2034-design/model.md @@ -0,0 +1,124 @@ +# Temporal persistence design grammar — frozen analytical state v1 + +Mode: exploratory. Provisional preservation list derived from source and the +user's agreed repair direction; not user-confirmed exact equivalence. +Target: the language of admissible typed-value persistence repairs in the +current TanStack DB shared SQLite system. Production e21c280f3, sources E1–E10 +in [evidence.md](evidence.md). No production patch is proposed as verified. + +## Preservation list + +| ID | Property | Authority | +| --- | --- | --- | +| I1 | An accepted supported Temporal value retains kind and semantic value after reopen, in every supported carrier. Ordinary ISO strings remain strings. | User-required direction and issue request; extension of established Date preservation. | +| I2 | Existing Date, BigInt, nonfinite and plain-data contracts remain unchanged; unrelated native classes acquire no support by accident. | Source controls E1/E5; last clause is inferred scope discipline. | +| I3 | Public subset equality, range, order and windows match DB's current semantics, including distinct equality and ordering equivalences. | E2/E3 and existing expression-index contract. | +| I4 | SQL candidate selection never discards a row the residual evaluator would accept; final limits apply after required residual evaluation/order. | Derived necessary condition from E2; not a new product rule. | +| I5 | Rejection preserves the adapter's complete prior durable state; action coalescing cannot hide invalid earlier actions. | Existing adapter/work oracle E6. Does not claim pre-publication rejection. | +| I6 | Changes preserve wrapper FIFO, error/receipt ownership and publication-before-durability behavior from #2002. | E7, user requirement to mesh with current work. | +| I7 | SQL expression/index representations remain aligned and respect host binding caps and transaction-driver propagation. | E4/E5. | +| I8 | Support is qualified by operation, constructor environment and execution boundary. Direct core success does not prove coordinator or host support. | Source boundary E8/E10 and user scope; explicit qualification is inferred. | +| I9 | Reader compatibility and damaged prior bytes have explicit policies; never infer a lost Temporal value from an ordinary empty object. | E1 plus irreversibility inference. Old-reader support is unresolved, not asserted. | +| I10 | Extend primary owners for each law and retain their limits; no second lifecycle or unconstrained codec framework is necessary to represent this value-domain repair. | User/code-weight instructions; no-framework conclusion is a provisional design constraint. | + +## Model: candidate primitives + +These are dimensions of one contract, not six proposed production classes. + +| ID | Candidate unit | Why it survives removal | Evidence | +| --- | --- | --- | --- | +| P1 | Typed value witness: kind, lossless value, valid native/polyfill brand, constructor capability | Removing kind conflates string/date/Temporal; removing the brand admits spoofed tags. | E1/E3/E9 | +| P2 | Carrier occurrence: row field, nested record/array, inline or separate row metadata, collection metadata, replay/tombstone payload, query/index literal | A top-level-only repair leaves independent serializer entrances uncovered. | E1/E4/E6/E10 | +| P3 | Representation mapping: runtime value ↔ tagged wire/storage value, plus operation-specific query representation | Stored spelling alone cannot establish query semantics or old-reader behavior. | E1/E3/E4 | +| P4 | Operation and observation: encode/revive, equality, order, candidate filtering, pagination, index lifecycle, accepted/rejected committed write | Storing a value does not imply it can be ordered or safely filtered in SQL. | E2–E7 | +| P5 | Execution boundary: direct core, transaction driver, persisted wrapper/coordinator, platform host | Erasing boundary borrows admission, ordering or native-host evidence from a different route. | E5/E7/E8/E10 | +| P6 | Evidence obligation: positive path reach, independent expected result, checkpoint, wrong-design control, declared limit | Removing it promotes a green serializer/control into end-to-end support. | oracle-rubric.md and E10 | + +## Relations, overlaps and grammar rules + +Use the following notation to describe a case, not a new runtime interface: + + Case := Value × Carrier × Operation × Boundary × Environment × HistoryWitness + Environment := constructor availability + reader/format generation + index generation + host cap + HistoryWitness := an existing owner's legal history populated with the new value domain + +A configuration is admissible only when the relevant rules below have an owner +and distinguishing evidence. The cross-product is constrained; it is not a +requirement to enumerate arbitrary combinations or a claim all cases are valid. + +| ID | Rule and active overlap | Source versus inference | +| --- | --- | --- | +| R1 | Kind and lossless value survive encode/revive; a missing required constructor fails explicitly. Brand recognition alone is not proof of support. | Kind/value is target I1; missing-constructor policy is proposed using E9. | +| R2 | The same preservation/rejection policy reaches every supported carrier. Nested location can change mechanics but not type semantics. | I1, E1/E6. Proposed carrier-completeness extension. | +| R3 | Equality, ordering and storage have separate obligations. A lossless representation need not be a sortable SQL key; comparator equality need not imply DB equality. | E3. | +| R4 | Pushdown is allowed only with an exact or demonstrably conservative candidate set. Unsupported/unknown semantics use a whole-predicate residual route unless Boolean composition itself is proved conservative. | I4 derived from E2. The classifier/mechanism for untyped field-to-field refs is unresolved U1. | +| R5 | Runtime scalar literals, IN's JSON binding, cursor predicates and index literals must preserve the same logical operand; normalized runtime/index SQL stays aligned. | E4/E5; Temporal extension proposed. | +| R6 | Each action required to serialize must remain validated, including superseded values and metadata. Adapter failure preserves durable rows, metadata, position and replay atomically. | E6 established, no new transaction scheduler. | +| R7 | The adapter cannot reclassify its later durability error as a source precommit abort. Preserve wrapper's public receipt/error and FIFO laws. | E7 established. | +| R8 | Constructor registration, codec reach and wire admission are separate capabilities. Expand a boundary only with that boundary owner's contract and receiving witness; otherwise state explicit non-support. | E8/E9; integration constraint inferred from current boundaries. | +| R9 | Rebuild changed normalized index definitions using the existing index path. New readers must preserve existing supported bytes. Old readers and existing literal marker-shaped objects require an explicit decision before claiming compatibility. | E4 + unresolved U3. | +| R10 | Add value dimensions to existing owner histories. The focused file-backed Temporal oracle owns value identity across reopen; it cannot own source-publication semantics, native host planning, or a universal transaction model. | E6/E7/E10 and oracle guide. | + +Active intersections (not assignable to a single serializer module): +O1=P1/P3/P4: type identity versus equality versus ordering. +O2=P2/P3/P4: query/index literals and stored fields meet at SQL interpretation. +O3=P2/P4/P5: serialization failure meets action coalescing, durable rollback, +and wrapper receipt settlement at different checkpoints. +O4=P1/P3/P5: constructor availability meets platform/coordinator admission. +O5=P3/P4/P6: compiler upgrades meet persisted physical indexes and direct SQL evidence. + +These overlaps have distinct failure consequences; a tree that assigns all of +them to serialization loses the relevant public contract. Current interfaces +support a shared adapter value helper and reuse of DB comparison semantics. +They do not establish a universal pluggable codec interface. + +## Dynamics, constraints and boundary conditions + +Dynamics are design transformations: substitute a lossless representation for +empty-object flattening; extend a supported carrier; add a constructor provider; +replace exact SQL execution with a conservative candidate route; augment an +existing oracle grammar with a native-value dimension. Each move preserves the +corresponding I/R constraints or explicitly records a changed support boundary. + +Constraints: R1–R10, actual transaction-driver ownership, no history rewrite, +no type coercion by ambient stringification, and no unrestricted promise that +all Temporal kinds admit ordering. + +Boundary conditions: current shared core plus existing wrappers, current +polyfill/native constructor availability, formats and driver limits. The first +candidate below targets Instant and PlainDate; it is not an inferred promise +for all eight Temporal kinds, custom classes, realms or provider hosts. + +## Unresolved rules and adjacent forms + +U1: Exact mechanism for safe SQL candidate selection with untyped field-to-field +refs, negation, mixed operands and unsupported functions; fallback work cost. +U2: Constructor provider API versus registered globals; support matrix for +native runtimes, other Temporal kinds and receiving host processes. +U3: Marker namespace collisions, reader generation compatibility and what +rebuild/reset policy is justified for previously damaged cache data. +U4: Coordinator wire support and full public wrapper reach; direct-core scope +cannot settle it. No priority between these support surfaces is invented. + +Generated adjacent forms, unranked: +F1 (substitute + exclude): tagged preservation for the two reported kinds, +Temporal-aware residual evaluation and order; withhold unproved SQL pushdown. +Retains I1–I7 where configured but requires U1 resolution; loses indexed query +work benefits for those requests. It does not add a codec framework. +F2 (augment F1): add exact per-operation SQL keys/literal lowering and matching +index expressions for proven Temporal domains. Adds capability/proof obligations +at O1/O2/O5; preserves fallback elsewhere. Precision/calendar and upgrade +witnesses required. Exact keys are a new design choice, not present source fact. +F3 (exclude): explicitly reject unsupported native values at serialization, +retaining existing supported kinds and adapter failure contracts. Stops new +silent loss but intentionally does not satisfy the requested Temporal support. +It cannot promise no prior Collection publication. This is an adjacent safety +form, not a substitute silently selected for F1. + +Distortion and loss: operation/type decomposition can hide interactions with +framework scheduling, mutable constructor registration and host transport. +The source is SQLite-centric; this grammar does not make provider field schemas, +all calendars or every consumer runtime authoritative. No form is a verified fix. + + +Controls and the frozen projection map are in [process.md](process.md). diff --git a/docs/contributing/oracle-reviews/issue-2034-design/process.md b/docs/contributing/oracle-reviews/issue-2034-design/process.md new file mode 100644 index 0000000000..8148da9d5e --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2034-design/process.md @@ -0,0 +1,131 @@ +# Design grammar process and controls + +Analytical state v1; exploratory mode. See [model.md](model.md) and [evidence.md](evidence.md). + +## Reconstruction and compression controls + +RC1 Reconstruct current Date control: P1(Date), each supplied carrier P2, E1 tag +mapping P3, E2 queries P4, direct core/node SQLite P5, passing control P6. +RC2 Reconstruct current Temporal failure: P1(Instant/PlainDate), E1 enumerable +object mapping yields {}, so R1 fails. SQL/residual and identity sort remain +P4 observations; grammar records failure rather than inventing preservation. +RC3 Reconstruct current #2002 receipt law: P5 wrapper plus E7 FIFO/publication +law constrains R7 independently of any P3 format. No source transition is +reimplemented in this grammar. +RC4 Reconstruct index and cap paths: E4 shared SQL shape/upgrade plus E5 IN and +whole-statement cap, with P4 operations and P5 scoped-driver capability. +All four source arrangements are representable. This is an analytical +reconstruction, not an executed implementation refinement proof. + +Ablation record: +P1→cannot distinguish type erasure/spoofing; P2→loses metadata-only/literal +carrier reach; P3→cannot express old bytes or query representation; P4→cannot +distinguish persisted value from meaningful filter/order; P5→incorrectly borrows +wrapper/host evidence; P6→green but unreached or correlated judgment admitted. +R1→{} accepted; R2→top-level-only fix admitted; R3→canonical-string order conflated +with semantic order; R4→false-negative prefilter admitted; R5→IN/cursor/index +translation can diverge; R6→invalid overwritten action disappears; R7→durable +rollback falsely claimed as public rollback; R8→direct-core results lend wire +support; R9→old index/reader mismatch hidden; R10→competing scheduler/model owner. +O1 removal erases same-order/different-equality distinctions; O2 literal/index +encoding diverges; O3 failure scope conflates durable and public state; O4 +constructor registration substitutes for transport; O5 index presence substitutes +for matching SQL. Each relation is retained for that distinguishing loss. +Demoted candidates: provider brand/version is a boundary annotation, not a +primitive; retry/generation machinery is excluded because no new lifecycle is +needed to express the repair; generic custom codecs are outside this extraction. + +Model-checking status: not applicable to this static compatibility grammar. +No new concurrency/state-transition model is asserted. Existing stateful +contracts are imported constraints whose tests remain with their primary +owners. No TLC run or proof of their integration is claimed. + +Range status: UNTESTED. No independently held-out marginal source was supplied. +All dates/calendars/precision cases used here are construction or future test +obligations, not independent range evidence. +Negative control: a plain object with Symbol.toStringTag='Temporal.Instant' +and a toString method is not thereby an admitted native Temporal value. R1 +requires real brand validation. Another exclusion: assigning lexical order to +a Temporal kind with no DB-defined ordering is not generated by R3. + +## Process freeze and projection support + +Analytical state v1 freezes E1–E10, I1–I10, P1–P6, R1–R10, O1–O5, RC1–RC4, +U1–U4 and F1–F3 before a human brief or stress readings are drafted. +Projection claims must map to these IDs: type preservation→I1/R1/R2; +query triangle→I3/I4/R3–R5/O1/O2; oracle ownership→I5–I8/R6–R10; +limits→U1–U4/range/distortion. Later stress readings may expose conditions but +must not silently rewrite this frozen grammar or count as held-out range tests. + + +The source reconstruction used one read-only subagent for existing oracle +ownership. The primary agent owned extraction, reconstruction, compression, +and the fracture/tension scans. A separate fresh subagent received only C1 +and its source basis for the hostile assay; it could not see F1–F3 or the other +stress readings. No new Field Log or broader workflow was started. + +Post-freeze probes belong to the fracture scan; they are not held-out grammar +range evidence. Node 24.19.0, SQLite 3.53.3, temporal-polyfill 0.3.0 produced: + +```json +{"instant":{"a":"1970-01-01T00:00:00Z","b":"1970-01-01T00:00:00.000000001Z","semanticCompare":-1,"sqliteLexicalEarlier":0},"plainDate":{"a":"2026-01-02","b":"2026-01-02[u-ca=japanese]","semanticCompare":0,"sameKindAndText":false}} +``` + +No source compiler or serializer was changed for these probes. They constrain +plausible implementations without claiming any candidate is implemented. + +## Post-freeze hostile reading and projection audit + +The fresh hostile auditor completed its reading against C1 at e21c280f3 without +sibling forms or the fracture/tension readings. The primary agent checked the +cited mechanisms and saved all five attacks as HA1–HA5 in +[hostile.md](hostile.md), including their repair conditions and dispositions. +No material attack was dropped and no finding was ranked. The agent's probe +results are attributed observations; its source-derived failure scenes were not +executed against a candidate patch. + +Two qualifications matter when reading the frozen layers: + +- E8's separately callable SingleProcessCoordinator method projects wire values, + but ordinary wrapper routing bypasses the remote-demand queue for that + coordinator (`persisted.ts:3238`). Neither source fact proves every public + single-process query rejects Temporal. HA5 retains a non-single-process + receiving witness as open. +- I4/R4's matching-row containment does not by itself establish preservation of + rejection outcomes. HA3 records this additional observation-contract question; + it does not silently strengthen the frozen rule or invent a guarantee that + every Boolean branch must execute. HA4 similarly treats format reversibility + as unresolved rather than attributing that promise to C1. + +The README projects the frozen grammar, then separately reports the requested +stress readings. These readings are addenda, not independent grammar range tests +and not a revised grammar version. A future revised grammar must explicitly +carry any adopted condition into a new version. + +| Consequential README claim | Exact support | +| --- | --- | +| Current revision, #2002 and repeated RED result | Evidence preamble; E7/E10; current-main replay receipt | +| Value preservation crosses serializer/query/public boundaries | P1–P5; O1–O5; E1–E4/E8 | +| Six constrained case dimensions and example | Case/Environment/HistoryWitness definitions; P1–P6; RC1/RC2 | +| Supported kinds/carriers/brands and unchanged plain data | I1/I2; R1/R2; E1/E9 | +| Storage/equality/order and SQL/index distinctions | I3/I4/I7; R3–R5/R9; O1/O2/O5; E2–E5; U1 | +| Durable rejection, superseded actions, wrapper receipts/FIFO, caps | I5–I7; R6/R7/R10; E5–E7; RC3/RC4 | +| Runtime/route qualifications and old-byte information limit | I8/I9; R8/R9; U2–U4; E8–E10 | +| Existing owners, no replacement lifecycle and receiving limits | I10/R10; E6/E7/E10; primary ownership table | +| Unranked adjacent forms and losses | F1–F3; U1–U4; dynamics/constraints/boundaries | +| Hostile findings and evidence limits | HA1–HA5 and concrete scene in hostile.md; qualifications above | +| Conditional marker fracture and executed scalar probes | FRA1–FRA3, controls and distortion in fracture.md; probe receipt above | +| Three unranked tensions and rejected publication framing | T1–T7, retained A–C, provenance and controls in tensions.md | +| Provisional status, range, exclusions and decomposition losses | Model preamble/distortion; range and model-check statuses; negative control; U1–U4 | + +Layer reconstruction audit: the primary brief preserves each consequence that +changes the result: type preservation, existing-data compatibility, distinct +query relations, durable/public checkpoints, binding/driver constraints, +boundary-qualified support, owner reuse, unranked alternatives and unresolved +mechanisms. The linked Model retains I1–I10, P1–P6, R1–R10 and O1–O5; Evidence +retains source authority and owner limits; Process retains RC1–RC4 and all +ablations, range/model-check exclusions and this support map. The three stress +records preserve failure constructions, controls, weakening evidence and +induced distortions. No confidence, recommendation, priority or support claim +was added by compression. User selection among the provisional tensions is +still pending; it is not inferred from silence. diff --git a/docs/contributing/oracle-reviews/issue-2034-design/tensions.md b/docs/contributing/oracle-reviews/issue-2034-design/tensions.md new file mode 100644 index 0000000000..5b8f3fb821 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2034-design/tensions.md @@ -0,0 +1,92 @@ +# Tension statement — initial scan of the Temporal persistence design + +Provenance inventory: +U1 user: reproduce #2034, use latest origin/main, mesh with existing persistence +oracles, and stress-test the agreed repair direction. +U2 prior proposal: tagged lossless values, matching query behavior, explicit +errors for missing support, compatibility and refetch limits. +S1 source: DB semantic equality/order, SQLite indexed expression contracts, +host-cap fallback and ordinary-write work laws. +S2 source: globally registered Temporal constructor precedent; separate +coordinator wire admission; no assumed polyfill/runtime capability. +S3 source: current object marker namespace, schema fences/index rebuilds, old +{} bytes with no recoverable Temporal information, publication-before-durability. +A1 inference: broad SQL fallback may impose material read work; no latency or +row-scan budget is established for this particular new value domain. +A2 inference: users may value transparent runtime portability; this is a +candidate concern, not a user-stated requirement to avoid registration. + +Candidate burst (generated framings, not discoveries): +T1 Exact query semantics versus retaining indexed selective execution when IR +has no field-type schema and values may be Temporal. +T2 Constructor-free deployment versus reviving real native values on hosts +without Temporal. +T3 Core-only scope versus a public wrapper whose coordinator validates a +separate supported wire-value domain. +T4 New tagged formats versus preserving existing plain records/older readers +that do not recognize the new interpretation. +T5 Immediate corruption prevention versus making all serialization failures +invisible to users before publication. +T6 A small production repair versus replacing all existing persistence oracles +with a larger new model. +T7 Restoring old typed values versus preserving ambiguous already-written {} +records in an offline cache. + +Control without a binary: SQLite accepts arbitrary object rows; current scalar +encoding loses Temporal state. Revival needs a constructor. Queries have two +execution stages; only the first can drop candidates irrevocably. Existing +models own different boundaries. Old bytes and old readers cannot learn a new +interpretation merely from a new codec. This inventory does not entail a choice +between correctness and performance, and contains no user demand that all +serialization errors precede publication or that oracle ownership be replaced. + +Discarded/qualified framings: +T5 is thin: the user asked to fix loss, not to reverse publication-before- +durability. It is a boundary clarification, not an established competing demand. +T6 is dissolved by the existing test/production budget distinction and owner +composition. More tests do not require a new production state machine. +T7 is a hard information limit for already-lost values; it strengthens the +compatibility question but cannot manufacture a recoverability guarantee. + +Three unranked working tensions remain, all provisional pending user fit: + +A. Semantic correctness and selective SQL execution. +We need exact Temporal query results, and we also need to preserve the useful +indexed execution that existing planning tests protect. Under untyped field +comparisons and Boolean composition, an unproved SQL filter can lose valid rows; +a broad residual route can give up index benefits. Roots: U1/U2 and S1. New move: +A1 connects safe fallback with read work, not a measured regression. Weakening: +prove an exact or selective conservative lowering for the requested domain, or +accept a bounded performance scope. Framing loss: may hide that costs differ by +query shape and that existing write-work bounds do not govern all read scans. + +B. Real value reconstruction and runtime/path portability. +We need reopened values to be actual Temporal values, and we also want the fix +to work through the environments and public APIs users already run. Under a +host without the required constructors or a coordinator that rejects Temporal +wire values, adding a SQLite tag does not establish that second capability. +Roots: U1/U2, S2. New move: portability as a desired degree is A2; the user has +not required a dependency-free constructor strategy. Weakening: a documented +registration or provider contract plus receiving witnesses across claimed paths. +Framing loss: may overstate friction for apps that already install a global +polyfill, or understate processes with different runtime initialization. + +C. Format extension and compatibility of existing data/readers. +We need new unambiguous typed encodings, and we also need an explicit policy +for existing plain records, old readers and already-damaged caches. Under the +current marker namespace, a new recognized tag may reinterpret a prior record; +older readers cannot revive a new tag, and {} alone cannot identify lost dates. +Roots: U1/U2, S3; tag collision is a constructed, source-grounded example. +Weakening: a justified reserved namespace/escaping/version boundary and a stated +cache-upgrade/refetch policy. Framing loss: combines several compatibility cuts +whose ownership and migration costs differ; it must not imply that all stored +user data can be discarded as a cache. + +Distortion: this method selects collisions and can make them look symmetrical +or fundamental. The rejected T5/T6 controls show that some apparent conflicts +are ordinary contract distinctions. No option is ranked or selected for the +user. User-fit selection remains pending; these are not proof that any one +tradeoff should govern the implementation. + +The subsequent [user recheck](user-recheck.md) records the corrected direction +and supersedes the pending user-fit status above without rewriting this scan. diff --git a/docs/contributing/oracle-reviews/issue-2034-design/user-recheck.md b/docs/contributing/oracle-reviews/issue-2034-design/user-recheck.md new file mode 100644 index 0000000000..55cf3c593d --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2034-design/user-recheck.md @@ -0,0 +1,52 @@ +# User clarifications and tension recheck + +This addendum follows the frozen v1 grammar and its stress readings. It records +the user's corrections without rewriting the candidate the auditor examined. + +The user permits overwriting existing persisted data, requires native +reconstruction (the input data type is the returned data type), and identifies +the existing SQL-plus-residual pipeline as the intended integration point. + +## Rechecked readings + +- **A — Dissolved as the presented product choice.** The adapter already + compiles SQL, decodes candidate rows, applies the DB predicate evaluator, + sorts locally, and finally paginates (`sqlite-core-adapter.ts:2434–2549`). + Correctness and useful SQL filtering are simultaneous requirements in this + design. No measured cost or demonstrated incompatibility established a need + for the user to choose one. Temporal support should extend this pipeline. + Sound candidate selection, Boolean polarity, rejection behavior and actual + index use remain implementation/proof obligations. In particular, the current + compiler marks a complete expression unsupported when any child is unsupported + (`:835`); this reading does not claim arbitrary safe conjunct extraction is + already implemented. Local cleanup can remove excess candidates, but cannot + restore a matching row discarded by SQL. Current pagination happens after + cleanup; the earlier conversational example of SQLite reading only the next + ten rows did not describe this adapter's present execution path. +- **B — Dissolved as an unresolved product priority.** Native reconstruction is + required. Strings or empty objects do not satisfy the requested behavior. + Constructor provisioning and receiving wire/host support remain concrete + requirements wherever support is claimed, rather than competing goals to + present to the user again. +- **C — Weakened by explicit migration policy.** Preserving existing persisted + bytes is not a requirement for this repair; overwriting/rebuilding them is + acceptable. No data reset has been performed. New accepted writes must still + preserve their input type, including ordinary records, and the chosen format + boundary must be explicit. Permission to replace old data does not make a + newly written marker-shaped record interchangeable with a native Temporal + value. Downgrade behavior and exact reset mechanics remain unspecified. + +The existing Node expression-index oracle observes captured SQL before residual +filtering, final adapter output, and named-index plans independently. Its +overbroad-predicate control (`expression-index-oracle.test.ts:1914`) demonstrates +why passing final-row assertions alone cannot establish efficient SQL execution. +Temporal additions should retain those existing laws and distinguish any +intentional conservative selection from regressions in exact cases. + +This recheck used the original reproduction goal, the full recorded readings, +the user's two clarification messages, and the source mechanisms above. The +source confirms the user's architectural correction; it does not establish that +Temporal support is already implemented or that all queries will be selective. +No new tension menu is manufactured. The current direction is native roundtrip +through the existing SQL/residual architecture, with old-data replacement +allowed. Production code and the frozen stress-test specimen remain unchanged. diff --git a/docs/contributing/oracle-reviews/issue-2034-design/v2-evidence.md b/docs/contributing/oracle-reviews/issue-2034-design/v2-evidence.md new file mode 100644 index 0000000000..fe54d1d95d --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2034-design/v2-evidence.md @@ -0,0 +1,240 @@ +# Temporal persistence v2: evidence and limits + +Base: `e21c280f3c4e56f6c0d05ec0eed87db29c623489`, including #2002. +The implementation is an uncommitted worktree diff; file hashes below identify +the measured sources. Runtime: Node 24.19.0; core reopen uses node:sqlite 3.53.3; +the expression-index and shared adapter contracts use the real better-sqlite3 +driver. Temporal fixtures use temporal-polyfill 0.3.0. + +## Executed observations + +| Check | Result | +| --- | --- | +| Expanded initial reopen oracle against unchanged production | 13 passed, 15 failed: the original eight Temporal failures plus new precision/calendar/constructor cases. | +| Targeted expanded witnesses against original production files (before the final routing and scalar-index additions) | 33 assertion failures, 1 passing control, 432 unselected tests. Two failures observe permanent wire rejection retried four times; twelve observe native action-history loss; seven observe native query/constructor loss; twelve observe index/query/metadata failures. All original production files were temporarily restored from HEAD and the repair was restored in a finally block. | +| Correct reconstruction with deliberately lexical native order keys | 8 assertion failures, 5 passing selected cases. Nanoseconds, supported-range endpoints and calendar order/equality distinguish this wrong design. Native decoding remains intact; both final query results and raw SQL results reject the mutant. The codec was restored byte-for-byte. | +| Intermediate plain-data boundary controls | The uncorrected independent IN key sets lose the native row under NOT IN; whole-record escaping loses a nested-field query. Both fail at their public key checkpoints, then pass with paired membership and marker-field escaping. | +| Late routing admission | Two native leader-to-follower witnesses fail with five coordinator calls each before the final admission repair. After removing the duplicate hydration dispatch and validating newly remote demand, all four native rejections and both valid string controls pass. | +| Membership index compatibility | The first new string-index witness reaches DDL and rejects because SQLite forbids a subquery in an index expression. This is a DDL failure, not an assertion-mutant kill. Equivalent balanced OR clauses pass both the two-value and 1,025-value named-index witnesses. | +| Final affected owner suite | **471 passed, 1 existing TODO, 0 failed** across six test files. Includes both existing fixed and random oracle campaigns, Collection event tests, shared core contract/binding-cap checks through the Node driver, resume snapshots, ordinary transaction work, persisted wrapper histories and expression-index planning. | +| TypeScript | Passed for changed source and the typed-value, transaction, wrapper and expression-index owners, using source/dependency aliases. | +| ESLint | Passed with zero errors; 25 existing require-await warnings in the transaction/wrapper owners remain. | +| Formatting and diff whitespace | Changed TypeScript files formatted; diff whitespace check passed. | + +Final suite command (local cached tools): + +```sh +/Users/kyle.mathews/programs/tanstack-db/node_modules/.bin/vitest run \ + --config /private/tmp/temporal-owners-vitest.config.mjs \ + --reporter=json --outputFile=/private/tmp/temporal-owners-final.json +``` + +The configuration aliases DB, db-ivm and persistence to this worktree's source. +Dependencies come from the existing checkout because a clean installation was +blocked by registry failures earlier in the task. No prebuilt TanStack package +supplies the tested implementation. This is not a full monorepo test run or a +verified clean dependency installation. + +## Owner mapping and bounded claim + +- **Typed-value owner:** actual file close/reopen; direct/nested rows, row and + collection metadata, replay, rank/text families, equality/range/Boolean/IN, + 901-value IN, field comparison, cursor tie groups and continuation, ordered + windows, constructor errors, malformed native brands and marker-shaped data. + Its native observations compare kind and text, not empty enumerable state. +- **Expression-index owner:** the same independent rank/text families drive + captured SQL before cleanup, final adapter keys and actual named-index use. + Wrapper-created coalesce indexes additionally observe native metadata literals + and distinct signatures for adjacent native constants. Native IN retains one + bound JSON parameter. Existing exact scalar SQL and plan laws remain. +- **Ordinary transaction owner:** twelve bounded native histories cross the + six value families with success/late rollback, repeated actions, metadata and + rejection of an invalid native action hidden by a later valid overwrite. + Its existing Map reducer retains immutable native values; its new observation + compares kind/text and demonstrates structuredClone would erase the premise. +- **Persisted wrapper owner:** four rejection witnesses plus two string controls prove rejected native + remote-subset literals cannot enter the retry queue or call the upstream + loader. Both immediate routing and a leader-to-follower transition during hydration are reached; admissible string requests dispatch exactly once. Existing transient retry, receipt, fail-stop and FIFO laws still run. + +This closes the reported silent loss for the two supported kinds through the +reached shared-core and local-wrapper paths and the bounded histories above. +It is not a universal proof. Other Temporal kinds reject. Native-engine +constructors, multiple constructor providers, native mobile/browser receiving +runs, multiprocess row transport, crash recovery and native truncate/delete +histories remain unproved and are assigned in the coverage map. Ordering +requires compatible same-kind operands; arbitrary Temporal arithmetic or +date-extraction expressions are outside this repair. No performance latency +claim follows from index-plan or statement-count observations. + +## Oracle guide audit + +| Requirement | V2 evidence | +| --- | --- | +| ORC-001 | User-confirmed native reconstruction, existing query/index/transaction laws, and explicit type/route limits in the model and executable prose. | +| ORC-002 | Fixture ordinals and literal kind/text expectations; existing independent Map reducer; no production SQL/comparison helper computes expected results. | +| ORC-003 | Existing owners retain contract/model/grammar/driver/checkpoints; the shared temporal-value-oracle companion owns the rank/text fixtures. | +| ORC-004 | Six named families reconstruct precision, signed years, endpoints and calendar ties. Kind/history/index axes retain the initial sixteen cells; Boolean/IN/coalesce/constructor cases distinguish adjacent wrong designs. No invalid ordering history is silently classified as valid. | +| ORC-005 | Real file SQLite, real better-sqlite3, Collection metadata/wrapper entry points, raw SQL, plans and observed coordinator calls. | +| ORC-006 | Original-production assertion failures, lexical-key mutant, ambiguous-IN and record-path controls; no timeout/setup failure counted as a kill. | +| ORC-007 | New coverage is bounded enumeration, so new random campaigns are not required. Existing generated owners retain and execute their fixed/random campaigns and replay interfaces. | +| ORC-008 | No new state machine. Immutable value retention preserves native kind/text that structuredClone would merge into empty records. | +| ORC-009 | Case ranks and kind/text projections are explicitly model-only; runtime terms remain the repository's terms. | +| ORC-010 | Existing owner cleanup and replay machinery retained; controlled source replacements restore in finally blocks. File harness cleanup remains under afterEach. | +| ORC-011 | Independent rank and canonical-text formulations separate ordering from equality. The lexical mutant proves lossless strings alone cannot satisfy the query law. | +| ORC-012 | This versioned record preserves base, results, limitations and exact source hashes. | +| ORC-013 | Adjacent nanoseconds, calendar-equal order with different equality, supported endpoints, lookalike strings, repeated invalid writes and an actual named-index checkpoint distinguish the claimed boundaries. | +| ORC-014 | Real Node SQLite supplies the storage/plan premise. Native mobile/browser and multiprocess receiving claims are explicitly unresolved. | + +## Code weight + +Production changes are additive because the existing code has no native codec, +native SQL representation, or type-preserving index metadata path to simplify. +The repair adds no scheduler, queue, retry state, lifecycle or codec registry. +Code weight is reported separately from tests and documentation below. + + +## Remaining ownership + +| Unproved boundary | Owner and required witness | +| --- | --- | +| Engine-native or alternate registered provider | Typed-value owner: the same reopen/brand/precision fixtures in those runtimes. | +| Mobile/browser native SQLite reception | Each adapter's receiving contract: native row/metadata/replay roundtrip after a real host reopen. | +| Multiprocess native row transport | Browser/Electron coordinator receiving owners: native values through actual wire/structuredClone and both producer/receiver observations. No support is claimed here. | +| Native truncate/delete and crash histories | Typed-value plus ordinary-work/resume owners: native tombstones, rollback, baseline/replay and real restart checkpoints. | +| Format generations and old marker-shaped records | Typed-value/resume owners: frozen old bytes and explicit rebuild paths; an old record exactly imitating a newly reserved native tag is not a proved compatibility case. | +| Wider lifecycle schedules | Existing persisted owner: native demands across release/abort, ownership change and reset. The current new routing witnesses cover immediate routing and one controlled leader-to-follower cut. | + +## Measured change weight + +The following counts include all untracked task artifacts and the frozen v1 +records, measured against the base. Added/deleted lines are distinct from net +weight; formatting changes are included. + +| Category | Added | Deleted | Net | +| --- | ---: | ---: | ---: | +| Production | 265 | 37 | +228 | +| Tests and fixtures | 1313 | 53 | +1260 | +| Documentation, including design/evidence | 1563 | 0 | +1563 | +| Dependency manifest/lock | 5 | 1 | +4 | + +## Tested source hashes + +SHA-256 identifies the uncommitted source that produced the final green report. +The test reports/configuration under `/private/tmp` are local execution receipts, +not portable checked-in tooling. All six test-file counts are recorded below. + +| Test file | Cases | +| --- | ---: | +| persisted-oracle.test.ts | 269 | +| sqlite-resume-snapshot-oracle.test.ts (includes ordinary transaction work) | 49 | +| sqlite-temporal-value-oracle.test.ts | 29 | +| expression-index-oracle.test.ts | 66 | +| node-sqlite-core-adapter-contract.test.ts | 33 | +| collection-events.test.ts | 26 | + +- `packages/db-sqlite-persistence-core/src/persisted.ts`: `95a205e968087775da9dd9ad895192f6b8d437f105dd9863d85d851e6f06319a` +- `packages/db-sqlite-persistence-core/src/sqlite-core-adapter.ts`: `046cd875bb0f457072979de2f31a0e5ea2c7136007ce8a60b2a58dcda3fa3495` +- `packages/db-sqlite-persistence-core/src/sqlite-value.ts`: `3a6f1a281bd67c5585740bce289ec42c9803cff1e3c1c0a51aa05dec0972eaff` +- `packages/db-sqlite-persistence-core/tests/ordinary-transaction-work-oracle.ts`: `e3a7fc817abdeff689271e21751ecca359230598618138ba23d16c4a4d14107e` +- `packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts`: `96575030d69bce4668c1d91e7cc8cc943656888c40d44b7d0f907e907a70f9e1` +- `packages/db/src/collection/indexes.ts`: `f88d72741341a4973d3ee74088333821bee80fb6701d006df2d025fc9f153550` +- `packages/db/src/index.ts`: `1a9fe7be53366621c71899793397c3615c990023412106f746756e4e1ed53252` +- `packages/node-db-sqlite-persistence/tests/expression-index-oracle.test.ts`: `eb5804034b2becc1bb535cfb75a18a1bb5c32d7f6fba653a60d659a530844235` +- `packages/db-sqlite-persistence-core/tests/sqlite-temporal-value-oracle.test.ts`: `1721f687513c663361de844e26cb01df8792289ba6e34d5afdfa28e22e8be0e0` +- `packages/db-sqlite-persistence-core/tests/temporal-value-oracle.ts`: `08760fd1f28cf7d72b107bec4daf2aa07cc043f81b05e1f23708ace36fbcbf14` + +## Review repair receipt on current main — 2026-10-05 + +Base: `cfb03f201bb21d82b7f537a9fbe59d3623f395d6`. This includes #1997 and the +later persistence revert #2038. Earlier results above remain historical. + +The review preserved 30 raw items: eight confirmed repairs, ten refutations, +five deferred investigations and seven duplicates. No design choice remains +unresolved. Deferred owners and required witnesses are in the coverage map. +The complete raw review and append-only evaluation ledger are task-local; they +are not product files. The independent review recommendation is hire, with +calibration feedback on rarity versus technical confidence and static evidence. + +New receiving tests first produced 12 failures and one valid string control. +They cover ordinary NUL equality, persisted native numeric expressions, late +ownership recovery and combined semantic/cleanup failure. Repairs preserve +ordinary bound strings, both native representations through path alternatives, +current-main safe candidate semantics and the existing retry/error boundaries. +The review also restored hostile-control reach and exact replay isolation. +Scalar membership DDL tests now state #1997's existing full-read fallback. + +Final affected campaign: **517 passed, 0 failed, 1 existing TODO**. The previous +main run passed 521; upstream #2038 removed four tests. This is not a test waiver. +Exact seed/path replay: **1 passed, 107 skipped**, with no unrelated native cases. +TypeScript succeeds. ESLint reports **0 errors and 25 existing require-await +warnings**. Changed TypeScript formatting and whitespace checks succeed. + +Hostile current-base controls: + +- Remove numeric-path identity: six native families fail semantic assertions. +- Remove NULL retention from native NOT leaves: six mixed-domain families fail. +- Admit negated native coalesce as exact: six families lose the NaN match. +- Remove recovery queue wire validation: both native kinds fail, while the string + control passes. Restored code passes all receiving checks. + +These controls retain the intended checkpoints. Setup failures do not count as +kills. Existing overbroad equality, inverted equality and parameterized-path +controls also reach their semantic/index checks again. Public row correctness, +raw SQL candidates, persisted expression values and plan use remain separate. + +Verification uses cached dependencies with aliases to all task source packages, +not old build output. Vitest runs at most two workers. The affected files are the +core typed-value, resume/ordinary-work, Boolean-arity and wrapper owners, Node +expression-index and adapter-contract owners, and Collection events. Commands: + +```sh +vitest run --config /private/tmp/temporal-owners-vitest.config.mjs --maxWorkers=2 --minWorkers=1 --pool-options.threads.maxThreads=2 +TANSTACK_DB_WS5A_SEED=1659005 TANSTACK_DB_WS5A_PATH=0 vitest run --config /private/tmp/temporal-owners-vitest.config.mjs packages/node-db-sqlite-persistence/tests/expression-index-oracle.test.ts --maxWorkers=2 --minWorkers=1 +tsc --noEmit --project /private/tmp/temporal-owners-tsconfig.json +``` + +The source snapshot is `/private/tmp/temporal-review-final-snapshot.json`. +Sorted changed-TypeScript path/hash map SHA-256: `7b64b1c65485a64494d2d47f77a71c056eea4fa3508ddb61d6bdf7d23e85cbc8`. +Receipts are `/private/tmp/temporal-review-current-main.json`, +`/private/tmp/temporal-review-replay.json` and +`/private/tmp/temporal-mutant-{numeric-identity,not-null,not-coalesce,late-wire}.json`. + +Weight before this evidence addendum: production +350/-39 (net +311), tests ++1755/-77, and documentation +1627/-0. Native reconstruction requires the codec +and query representations absent from the old implementation. The review uses +existing admission, cleanup and residual filtering instead of another lifecycle. +Most documentary weight is the explicitly requested grammar and historical +hostile/fracture/tension evidence. These counts include untracked task artifacts. + +This closes the reached native preservation/query and admission traces only. +Engine-native/mobile receiving behavior, wider ownership histories and old-byte +migration remain bounded by the explicit owner limits. Rebuild is the selected +policy for ambiguous old marker records and already-erased values. + +## CI binding-capacity follow-up — 2026-10-05 + +At formatter commit `9fa641974`, CI's broader core suite found one additional +work regression. The existing root/transaction-driver capacity witness uses +100 ordinary string equalities with a 100-binding cap. The added identity +conjunct doubled them to 200 bindings and forced a full read. The returned rows +were correct, but the existing 100/101 boundary assertion failed: expected 100 +bindings, observed zero. The earlier seven-file local run omitted this owner. + +The unchanged receiving test reproduced locally. Ordinary string predicates now +retain their original one-binding candidate comparison. Residual filtering +removes native order-key collisions. Persisted Boolean expressions and native +predicates still compare canonical identity. This also removes the redundant +parameter-duplication branch; no assertion or cap changed. + +The binding-capacity campaign, NUL cases and six mixed-domain families pass +(18 selected tests). The new scalar/native collision observation checks the one +runtime binding, exact public keys, candidate containment and exact stored +Boolean expression values separately. TypeScript and changed-file ESLint pass. +Receipts: `/private/tmp/temporal-ci-binding-red.json` and +`/private/tmp/temporal-ci-binding-green.json`. Production change: +6/-12 lines; +receiving observations: +26 lines. The bounded binding-capacity owner now joins +the affected local suite. + +The expanded eight-file suite passes **559 tests, 0 failures, 1 existing TODO** +after the repair (`/private/tmp/temporal-ci-binding-surrounding.json`). This +includes the complete CLI adapter contract and prepared-statement capacity owner. diff --git a/docs/contributing/oracle-reviews/issue-2034-design/v2-model.md b/docs/contributing/oracle-reviews/issue-2034-design/v2-model.md new file mode 100644 index 0000000000..e0c82968bc --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2034-design/v2-model.md @@ -0,0 +1,160 @@ +# Temporal persistence grammar v2 — implementation contract + +This replaces the open choices of v1 for the implementation requested by the +user. Source: e21c280f3; user decisions: native reconstruction is required, +existing persisted data may be replaced, and the existing SQL/residual pipeline +is the integration point. V1 and its hostile readings remain historical evidence. + +## Frozen model + +M1 Value: support Instant and PlainDate, the two reported kinds. Resolve their +constructors from globalThis.Temporal, as the offline serializer does. Require +them on encode and decode; prototype toString performs the brand check. Reject +other genuine Temporal kinds explicitly. Do not silently stringify or flatten +them. Preserve existing Date/BigInt/scalar behavior. No pluggable codec API. + +M2 Carrier: rows, nested records/arrays, row/collection metadata, replay and +tombstones use the same native encoding. Wrapper index specifications use that +encoding too. Plain records containing the reserved marker are escaped on new +writes, so decoding cannot promote them to native values. + +M3 Representation: each native tag stores canonical text and a sortable key. +Instant uses an offset, zero-padded decimal epoch-nanosecond integer; PlainDate +uses its ISO-calendar year/month/day with an offset, padded year. Neither uses +floating point or Date conversion. Canonical kind/text is equality identity; +the sortable key is ordering identity. PlainDates with different calendar IDs +can have the same order but different equality identity. + +M4 SQL: extend the shared runtime/index reference expression to select native +sort keys. Temporal literals use the same sort key. Equality and IN add a +kind/text identity check when required, retaining the indexed key restriction. +Coalesce propagates both representations. Boolean composition operates on exact +predicates in this declared domain, including NOT; preserve the existing +unsupported-expression and binding-cap fallbacks. Runtime and persisted index +SQL must remain syntactically aligned. The existing residual evaluator and local +sort remain authoritative, with native Temporal comparison in local sort and +pagination after cleanup. No new query execution pipeline. + +M5 Query domain: native scalar refs/literals, nested refs, coalesce, equality, +IN, same-kind ordered comparisons, Boolean composition, ordered windows and +cursor predicates. Ordered operands must have compatible declared types; +cross-kind native ordering is invalid, not an invented total order. Native +calendar annotations and nanosecond precision remain observable. Arbitrary +Temporal arithmetic/date extraction, other native kinds and custom classes are +not introduced by this repair. + +M6 Histories: extend current owner grammars with native values. Preserve action +order, validation of superseded actions, full durable rollback, replay positions, +wrapper receipts/FIFO and existing publication-before-durability. Immutable +Temporal values may be retained by the model; observations must compare native +kind and text instead of relying on structuredClone or empty-object equality. + +M7 Boundaries: direct shared core and wrapper index serialization are included. +Existing coordinator wire contracts remain narrower: reject invalid wire input +before remote-demand retry admission; do not transport native instances through +structuredClone. Native host/multiprocess receiving support is not claimed. + +M8 Upgrade: new readers retain existing supported encodings and rebuild changed +index SQL through the existing path. Already-corrupted empty objects remain +plain data; an application can replace the cache using the existing schema +version/reset options. No automatic reset or irreversible data action is needed +to implement this additive repair. Downgrade readers are not promised support +for the new format. New accepted data still has the M1/M2 preservation contract. + +Case := value family × carrier × operation × existing legal history × boundary. +The active overlaps are storage/equality/order, runtime/index literals, +serialization/action folding, and public admission/transport. These dimensions +are model annotations, not proposed production classes or lifecycle states. + +## Evidence and executable owners + +E1 sqlite-temporal-value-oracle: file reopen, nested values, metadata, replay, +boundaries in precision/year/calendar, marker escaping and constructor failure. +E2 expression-index-oracle: raw SQL keys before cleanup, native equality/order, +IN/NOT/coalesce, and actual index use. Add a wrapper-produced specification path. +E3 ordinary-transaction-work-oracle: independent native kind/text observations +for repeated actions, rollback and invalid superseded native values; keep work +bounds and current models. +E4 persisted-oracle: preserve existing receipt/FIFO laws and reject permanent +wire admission failures before they enter retry. Reuse its current owner paths. + +## Selected transformations and overlaps + +| Form | Route and fixed boundary | Dependency change and cost | Limit | +| --- | --- | --- | --- | +| F1 native storage | Substitute the shared codec; preserve adapter row/metadata/replay APIs (M1–M3). | Registered constructors become a runtime dependency; immutable values gain canonical text and order keys. | Other kinds reject; no erased-value recovery. | +| F2 native indexed queries | Augment shared runtime/index expressions (M3–M5). | Equality identity accompanies ordering; IN retains correlated pairs; residual cleanup stays in place. | Same-kind ordering only; no arbitrary native arithmetic. | +| F3 explicit wire boundary | Exclude native literals at existing remote-demand admission (M7). | Validate before dispatch; preserve transient retries for admissible values. | This is a transport limit, not general native transport support. | + +The user selected F1 and F2 together and accepted the stated F3 receiving limit +in the implementation plan. These are adjacent transformation paths from the +same existing subsystem, not competing performance/correctness products. +Storage and queries overlap at representation; queries and index metadata +overlap at native literals; action folding and serialization overlap at +validation; public demand and transport overlap at admission. Splitting these +into independent codecs or models would lose those obligations. + +The case dimensions are finite test annotations. Dynamics are value encoding, +query compilation and existing action histories; constraints are M1–M8; boundary +conditions are the two supported kinds, one registered constructor provider, +real Node SQLite and the declared shared-core/local-wrapper routes. + +## Post-freeze implementation discoveries — 2026-10-05 + +D1 (M2/E2): Collection.getIndexMetadata was an additional receiving entrance. +Its JSON clone erased native literals and its signature collapsed them to empty +records. Copy expression structure while retaining immutable values, and include +kind/text in the signature. Six wrapper witnesses failed before this repair. + +D2 (M2/M4/E1): IN must match correlated (order, identity) pairs. Two ordinary +string literals must not combine to impersonate one native value under NOT IN. +Escape only an ordinary record's marker field so its other nested JSON paths +remain queryable. Both distinctions have pre-fix public-result failures. + +D3 (M7/E4): a leader without a local subset owner can become a follower while +hydration waits. Validate at entry for an already-remote request, and after +hydration inside the existing admission cleanup boundary for a newly remote +request. Remove the redundant hydration-time ensure path; dispatch owns the +remote request. String controls dispatch exactly once; unsupported native input +never reaches dispatch or retry. No new lifecycle or retry state is added. + +D4 (M4/E2): SQLite forbids membership subqueries in index definitions. Runtime +IN uses paired membership; index expressions use equivalent balanced OR clauses. +Balancing preserves large membership definitions without exceeding expression +depth. Two and 1,025-value string-index witnesses check final keys, raw SQL and +named-index use. This preserves an existing scalar index route affected by F2. + +These addenda refine the receiving paths and SQL forms. They do not replace the +frozen native-value law or revise existing publication/durability contracts. + +See [evidence](v2-evidence.md) for executed controls and remaining owner gaps, +and [process](v2-process.md) for reconstruction and projection provenance. + +## Current-main integration and review repairs — 2026-10-05 + +The branch now includes main cfb03f201, including the safe candidate contract +from #1997. M4's exact native examples retain their raw-key/index checks. +Classification admits native raw-ref equality, native membership, same-kind +ranges and raw-ref NOT leaves. Range candidates retain SQL NULL projections +because core NaN orders above native values. NOT uses the original leaf, never +an approximate candidate. Mixed native/scalar field pairs fall back outside the +native pair. Positive native coalesce equality remains selective; NOT/coalesce +and unsupported Boolean shapes keep residual filtering. NaN is not nullish in +the core evaluator, so SQL coalesce cannot certify negation for that domain. + +D4's scalar membership DDL law still holds. Its original exact raw-key and plan +claim preceded #1997: eq(in(ref, strings), true) now deliberately reads a candidate +superset. The two and 1,025-value witnesses retain successful DDL and exact final +keys, with full candidate reads as the current contract. Native plan assertions +remain unchanged. + +M7 also covers an ownerless leader that completes local loading before it becomes +a follower. Sequence-gap recovery validates retained demand at the existing +remote queue admission. Invalid native input reaches the existing terminal +error path without a coordinator call; a valid string dispatches once. Transient +transport retry keeps its prior law. No second lifecycle or transport codec is added. + +M8 does not claim lossless migration for old ordinary objects whose reserved +marker now names a recognized encoding. A receiving old-writer/new-reader probe +confirms that ambiguity. The user permits replacing old data; the guide now +states the rebuild requirement. New accepted records retain M2 preservation. diff --git a/docs/contributing/oracle-reviews/issue-2034-design/v2-process.md b/docs/contributing/oracle-reviews/issue-2034-design/v2-process.md new file mode 100644 index 0000000000..080588a06c --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2034-design/v2-process.md @@ -0,0 +1,121 @@ +# Temporal persistence v2: process and controls + + +Frozen before tests/production changes. Reconstruction: Date controls and the +reported Temporal loss map to M1–M3/E1; current SQL/residual stages map to M4/E2; +ordinary action folding and #2002 receipts map to M6/E3/E4. The revised grammar +does not change those lifecycle laws. +Ablation: remove M1 -> type erasure; M2 -> uncovered carriers/collisions; +M3 -> precision/calendar errors; M4 -> lost candidates or unused indexes; +M5 -> unsupported total ordering; M6 -> false-green model or partial durable +state; M7 -> repeated permanent wire failure; M8 -> ambiguous upgrade claims. +Negative cases: spoofed Temporal tags must not create native values; unsupported +native kinds must not become empty objects. Range beyond the declared query +domain is unproved. No independently held-out range source is available. Formal +model checking is inapplicable: no state machine is added or replaced. +Adjacent transformations retained: shared codec substitution; native SQL/index +representation augmentation; explicit unsupported-boundary exclusion. The user +selected their combination. Additional framework/lifecycle alternatives are not +needed. Model decomposition can hide host/transport behavior; E4 and the stated +receiving limits prevent borrowing that evidence from SQLite. + +Execution: add witnesses to E1–E4, observe failures on unmodified production, +implement M1–M8, then run affected owners and static checks. Record concrete +results and remaining limits after execution. A green final-row check alone +does not establish SQL selectivity or native-host support. + + +## Source and authority ledger + +S1: production base e21c280f3c4e56f6c0d05ec0eed87db29c623489, including +#2002; the paths below and their existing executable owners constrain the repair. +S2: [initial reproduction](../issue-2034-temporal-persistence.md) records the +reported native loss; the [v1 stress readings](README.md) record the earlier +hostile, fracture and tension probes. They are not new independent v2 assays. +S3: user-required properties are native reconstruction, permission to replace +old data, and integration with existing SQL/residual mechanisms. The latest +instruction explicitly authorizes grammar → relevant oracles → implementation. +Other preservation properties are sourced contracts or exploratory engineering +inferences, not independently user-confirmed new product choices. + +| Observation or requirement | Source entrance | Inferred implementation rule | +| --- | --- | --- | +| Native values become empty records; Date/string controls survive | sqlite-value.ts; encode/decodePersistedJsonValue; typed-value owner | M1–M3 registered codec and separate identities | +| Runtime predicates and expression indexes must match; cleanup follows SQL | compileSqlExpression; expression-index owner | M4 shared native representations | +| DB comparison distinguishes canonical identity from chronological rank | DB Temporal comparison/equality; precision/calendar fixtures | M3/M5 rank/text reference model | +| Repeated actions validate before folded durable writes | ordinary-transaction-work-oracle.ts | M6 retain immutable native values in the independent Map model | +| Wrapper serializes index metadata and owns receipts/remote demand | persisted.ts; Collection indexes.ts; persisted-oracle.test.ts | M2/M7 preserve index literals and exclude invalid wire input | +| Old erased objects carry no reconstruction information | reproduction; user replacement permission | M8 additive format plus existing explicit reset options | + +All source paths above are relative to their owning package; exact changed +source paths/hashes are in the evidence layer. The public guide documents the +user-visible constructor, query and receiving limits. + +## Reconstruction, ablation and admission audit + +C1 Reconstruction: the reported row/nesting/metadata/replay loss maps to E1; +selective queries and actual index use map to E2; action-order/rollback maps to +E3; wrapper receipt and permanent-input behavior map to E4. Original Date and +string cells remain controls. D1–D4 were discovered at those real entrances, +then recorded as addenda rather than silently changing M1–M8. + +C2 Ablation: M1 removal permits type erasure; M2 removal loses a carrier or +marker-shaped record; M3 removal permits lexical precision/calendar ordering; +M4 removal loses candidates, Boolean polarity or index use; M5 removal invents +an unsupported total order; M6 removal hides invalid overwritten actions or +allows partial durable state; M7 removal admits permanent transport retries; +M8 removal implies erased bytes or old readers can recover native values. +Only those distinctions survive. Case ranks, observations and runtime names +are model annotations, not additional product primitives. + +C3 Range: independently held-out range evidence is untested; all precision, +calendar, native-brand, routing and SQL cases here helped construct or verify +this repair. They cannot also count as independent universality evidence. + +C4 Exclusion: ordinary records with a spoofed own tag stay plain; invalid +native brands and unsupported native kinds reject. Cross-kind ordered operands, +arbitrary native arithmetic and untested host/transport routes remain outside +the claimed domain. Missing global constructors are explicit failure inputs. + +C5 Executable controls: original production, a lexical-key mutant, native +ordinary-work histories, paired-IN and marker-path counterexamples, routing +barriers and scalar membership indexes. The evidence layer records assertion +failures separately from rejected index DDL. No timeout or setup error is +counted as an assertion kill. + +C6 Formal modeling: this grammar specifies value representations and exclusions +inside the existing lifecycle owners; it does not introduce a new transition +system or liveness claim. No new TLA+/TLC run is claimed. The routing addendum is +bounded by the executable hydration barrier and existing wrapper campaigns; +passing tests do not prove all possible schedules. This is a deliberate limit +of the static grammar plus existing-owner approach. + +C7 Generation/compression: retain only F1–F3 in the model. A custom codec registry, +a replacement query pipeline, a new retry machine and a general cross-kind +order add unsupported mechanisms or erase named invariants. They were excluded. + +## Freeze and projection trace + +The original v2 M1–M8/E1–E4 text was saved before implementation. The complete +support package is frozen after D1–D4 and final validation; the brief is then +regenerated from that package. The original exploratory v1 remains unchanged. + +| Consequential brief claim | Support | +| --- | --- | +| Native types and existing SQL/residual pipeline are preserved | M1–M5; E1/E2; F1/F2 | +| Rows, metadata, replay and index literals share encoding | M2; D1/D2; E1–E3 | +| Native chronology differs from canonical equality | M3; E1/E2; lexical mutant | +| Retry admission remains narrow; routing can change during hydration | M7; D3; E4; routing RED/green | +| Index definitions require an equivalent non-subquery form | M4; D4; membership DDL/green | +| Existing action/rollback/receipt laws remain | M6; E3/E4; final affected suite | +| Counts, static checks, limitations and code weight | Evidence receipt and source hashes | +| Old data/reconstruction limits and untested routes | M8; C3/C4/C6; evidence owner gaps | + +Layer reconstruction: the brief retains native kind/value, efficient SQL plus +cleanup, equality/order distinction, action/rollback laws, constructor/route +limits, overwrite policy, bounded evidence and absence of a universal proof. +The linked model recovers all legal transformations and exclusions; evidence +recovers concrete checkpoints; this process layer recovers observation versus +inference, ablation, missing range evidence and formal-model limits. No claim is +introduced only in the brief. Decomposition still hides untested host behavior +and schedules; those losses remain explicit in the package and coverage map. diff --git a/docs/contributing/oracle-reviews/issue-2034-design/v2.md b/docs/contributing/oracle-reviews/issue-2034-design/v2.md new file mode 100644 index 0000000000..f44b40c619 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2034-design/v2.md @@ -0,0 +1,59 @@ +# Temporal persistence v2 + +The implemented design preserves native `Temporal.Instant` and +`Temporal.PlainDate` values through the existing SQLite query pipeline. It keeps +SQL filtering and index use, reconstructs native values, then uses the existing +residual filtering, ordering and pagination stages. + +The grammar is: + +```text +supported value × carrier × operation × legal history × execution boundary +``` + +Its selected rules are concrete: + +- Use registered global Temporal constructors for brand validation and native + reconstruction. Preserve both kind and value; reject missing constructors and + other Temporal kinds. Strings remain strings. +- Encode native values consistently across rows, nested data, metadata and replay. + Escape an ordinary record's marker field without moving its other JSON fields. +- Separate lossless canonical text from sortable keys. Instant keys retain + nanoseconds; PlainDate keys preserve ISO chronological order across calendars + and signed years. Equality additionally retains kind and calendar text. +- Share reference/literal representations between runtime SQL and persisted + indexes. Exact equality and correlated IN membership remain safe under NOT. + Coalesce retains both representations; existing SQL fallbacks and final cleanup + remain. Ordered comparisons require compatible same-kind operands. +- Preserve action validation, durable rollback, wrapper receipts/FIFO and the + publication-before-durability boundary. Reuse existing oracle owners. +- Retain the narrower remote-subset wire domain and reject permanent input + errors before retry admission. Multiprocess native-value support is not added. + +The wrapper oracle exposed an additional receiving boundary: Collection index +metadata previously JSON-cloned native literals into strings and gave distinct +Temporal literals the same signature. The implementation now preserves native +literals while copying the expression structure and includes kind/text in the +signature. Its test observes metadata, emitted SQL and actual index use. + +The original implementation failed 33 targeted expanded witnesses before the final routing and scalar-index additions. A deliberately +wrong lexical-order implementation fails eight cases while still reconstructing +native values correctly. The routing witnesses also catch a request that becomes remote during hydration; valid requests now dispatch exactly once. Small and large membership indexes retain SQLite compatibility. The repaired affected suite reports **471 passed, +1 existing TODO, 0 failed**. TypeScript and lint pass; lint retains existing +require-await warnings. Full results, source hashes and the oracle audit are in +[evidence](v2-evidence.md). + +Old data may be replaced, as the user specified. This additive implementation +does not automatically erase it: existing schema-version/reset options can +replace already-damaged caches. Empty objects cannot reveal their former type, +and older readers cannot interpret the new native encoding. + +The claim is bounded to the reached paths and histories. Engine-native +constructors, native mobile/browser runs, multiprocess row transport, +truncate/delete histories with native values and arbitrary Temporal operations +remain unproved. Independently held-out range evidence and formal model checking are absent; host behavior and untested schedules remain a decomposition limit. Index-plan checks establish index use, not a latency guarantee. + +The detailed [model](v2-model.md) preserves the initial frozen rules and explicitly +dated implementation discoveries. [Process](v2-process.md) records reconstruction, +ablation, controls and the projection map. No new lifecycle model or generic +codec framework was introduced. diff --git a/docs/contributing/oracle-reviews/issue-2034-high-effort-review.md b/docs/contributing/oracle-reviews/issue-2034-high-effort-review.md new file mode 100644 index 0000000000..0803ca7952 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2034-high-effort-review.md @@ -0,0 +1,151 @@ +# Temporal persistence review follow-up + +Reviewed commit: `6562f8c1ebbb15698e66c98ebe39c440df251984` (PR #2039). +The review supplied ten unverified claims. This record preserves the decisive +observations, the narrow repair, and the remaining work. It does not extend the +approved native-value or multiprocess support contract. + +## Observed lifecycle and value boundaries + +Controlled probes drove the actual persisted wrapper, Collection, and coordinator: + +- Two active demands, with the native demand first or last, reached sequence-gap + recovery after a leader became a follower. Both orders entered terminal error. + A later coordinator message caused no additional hydrate or ensure, and another + load rejected with the same terminal error. Release remained available. A + native-first demand stops the recovery suffix by the selected fail-stop law; + it does not leave a ready Collection repeatedly entering gap recovery. +- A controlled applied receipt held a hydrated row visible while leadership + changed. The load then rejected wire admission. A subsequent targeted change + removed the cached row, and unload delivered the matching release. Rejection + does not roll back already-published cache rows; it also does not make them + permanently immune to invalidation. These observations are limited to the + controlled component boundary, not a real browser leadership schedule. +- Instant, PlainDateTime, and ZonedDateTime demands loaded twice through a + recording persistence adapter without a global Temporal constructor. Request + identity uses the request object; `toStableSerializable` serves persisted + index specifications, not subset keys. +- Real SQLite queries with PlainDateTime or ZonedDateTime literals succeeded + through the prior fallback on an empty table and now reject explicitly. + Instant queries reject without the registered constructor and succeed after + registration. These are the selected v2 restrictions. Engine-native Chrome + and polyfill interoperability still require their receiving owner. +- Cold and hydrated local loads preserved the exact upstream error for both + synchronous throws and promise rejections. Both exposed promises. Coordinated + native loads failed before invoking the upstream source; scalar controls + preserved the upstream error. The real wrapper registers any upstream loader + as a remote owner before it can take that route. The purported local upstream + failure followed by newly remote retry admission was not a legal path in this + source/owner configuration. + +## Discarded string membership work + +The existing boolean-arity owner observed result rows, receiving SQL, bindings, +index plans, and expression visits. It did not observe SQL construction discarded +before the receiving SELECT. Correct output therefore hid unnecessary work. + +The repair keeps canonical membership identity for native literals and persisted +index expressions. Runtime membership over ordinary strings no longer constructs +an identity clause that the safe classifier discards. + +The owner now checks lists of 0, 1, 32, and 1,025 strings. An independent work rule +allows zero SQL-literal quoting of values carried through the JSON list binding. +The observation counts the quoting primitive for those exact list values, even +when the resulting string never reaches SQLite. Exact result keys and binding +counts remain separate assertions. + +Before the repair, the unchanged work assertion failed with 2, 64, and 2,050 +quoting operations; the empty-list control passed. After the repair, all four +cases and the original large indexed OR-scope witness passed. + +A separate SQL mutation removed the native paired-membership conjunct from a +negated PlainDate membership query. The ISO date and Japanese-calendar date had +one ordering key but different identities. Correct SQL returned the Japanese +row; the mutated SQL returned no rows. Removing the native check is therefore +not a safe simplification, even with residual filtering. + +The repair changes one predicate and adds no production state or helper. +Production weight is +3/-1 lines. Tests add four bounded cases plus nearby prose. +This is a construction-work claim, not a measured latency improvement. + +## Remaining efficiency and upgrade work + +Deterministic counters on an empty real SQLite table measured two native parses +for EQ and twelve for an IN list containing three native elements. The count +includes literal compilation and membership construction, but no row decoding. +The typed-value and expression-index owners retain a follow-up to reuse encoded +literal data without changing validation, calendar identity, or binding limits. +There is no claim here that every parse is removable or that a latency limit was +violated. + +Successful follower loads and leader-to-follower loads each performed two wire +projections, including the coordinator's projection. A transient failure produced +four projections across two coordinator attempts. Initial and post-hydration +validation are mutually exclusive. Any future projection reuse must preserve +wire admission at ownership transitions, rather than simply delete guards. +Native wire support remains outside v2; it needs both sending and receiving +protocol behavior and real browser/Electron evidence. + +An old-writer/new-adapter probe rebuilt a field-expression index once. It retained +an unchanged raw `row_version` index and performed no rebuild on the next ensure. +A separate old/new Collection index-metadata probe confirmed that native-literal +signatures change while their version remains 1. Installing both signatures +retained two active registry entries. Existing schema/reset paths can remove +obsolete entries; this PR does not promise automatic orphan reclamation. +Applications that retain their cache should expect first-use rebuild work for +changed expressions. A signature-version bump alone would not reclaim old rows. + +## Verification and oracle audit + +The targeted review probes passed 23 checks after the repair. Eight surrounding +suites passed 563 tests with one existing TODO. TypeScript passed. The focused +string-work test failed on the reviewed implementation and passed after repair. +The native identity mutation failed at the intended candidate-row checkpoint. + +For the bounded work extension, ORC-001/002 use the JSON-binding contract and a +separate primitive counter. ORC-003/005 place the law, driver and observations in +the existing owner. ORC-006 is established by the original implementation's +construction counts. ORC-009 uses existing vocabulary. ORC-010 restores the probe +and closes SQLite in cleanup. ORC-012 is this record. ORC-013 covers empty and +nonempty sizes, including one beyond the binding cap. ORC-004/007 do not apply to +this fixed enumeration; no new generated grammar is claimed. ORC-008 introduces +no model state machine. ORC-011's plausible unsafe simplification is challenged +by the independent calendar-membership fixture. ORC-014 claims only Node SQLite +and controlled coordinator boundaries, with native-host ownership still open. + +The ten-item disposition audit is: one fixed-now, three refuted as stated, three +duplicates of selected design restrictions, and three deferred investigations. +Each compound claim retains its valid observation even when its claimed +consequence was refuted. No design decision is required to apply the narrow fix. + +## Quoting-probe calibration follow-up + +CodeRabbit review 5422260805 examined +`f0f3bdc9ea2004919b1b4c5a80ac4e7403079080` and proposed an executable positive +control for the literal-quoting probe. A scratch adapter restored the discarded +string-identity construction but switched quoting from `replace` to `replaceAll`. +The original four work checks incorrectly passed this mutant. + +The work owner now creates a valid coalesce expression index with a control +literal and requires a nonzero probe count before resetting the counter for the +bound query. This also calibrates the size-zero case. All four mutant cases now +fail at that positive-control assertion, while the real production owner passes +all six tests. The spy test is explicitly sequential and restores the prototype +in cleanup. Production code is unchanged by this follow-up. + +An initial constant-only index fixture failed SQLite setup on both paths. Those +failures are excluded from mutant evidence; only the valid coalesce-index +assertion failures establish detection. The control proves current sensor reach, +not a universal measure of every future quoting algorithm. Unrelated future +transformations of identical fixture strings could conservatively fail this +primitive-work check and would need investigation. + + +## Medium-review clarification + +The [mixed-demand follow-up](issue-2034-recovery-review.md) corrects the earlier +HE-001 refutation wording. Terminal Collection failure and a stopped recovery +suffix are observed M7 behavior; only the claimed repeated-gap consequence was +refuted. The former two-order scratch evidence is now retained and expanded in +the primary oracle, with abort/release/string neighbors, exact original errors, +post-terminal fencing and hostile controls. No production policy changed. diff --git a/docs/contributing/oracle-reviews/issue-2034-index-admission.md b/docs/contributing/oracle-reviews/issue-2034-index-admission.md new file mode 100644 index 0000000000..5648280182 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2034-index-admission.md @@ -0,0 +1,69 @@ +# Optional persisted index serialization + +The [law enforcement audit](issue-2034-law-enforcement.md) strengthens the +original witnesses below and supersedes their earlier completeness claims. + +CodeRabbit review 5422905753 identified an escaping serializer exception on +`e61cda199`. The oracle reproduced it on `5d97c7790` before production changes. + +## Law and repair + +Persisted indexes are optional query accelerators. Failure to construct their +persistence specification belongs to the same best-effort boundary as failure +to store them. It must not fail Collection startup or leak an unhandled runtime +rejection. A healthy sibling index and subsequent ordinary Collection reads +remain usable. This extends the existing local-index-failure witness to the +previously untested serializer boundary; it introduces no recovery policy. + +Both calls to `buildPersistedIndexSpec` now execute inside their existing +`try` blocks. Existing warnings and local completion markers handle failure. +There is no new state, retry, or fallback. Unsupported native row values still +reject: the optional index fixture stores ordinary rows and uses native values +only in index metadata. + +## Oracle and calibration + +The persisted owner previously injected adapter failure after adapter entry. +It did not reach a native serializer failure before that entry. Its new finite +matrix crosses startup/runtime index addition with supported Instant, +unsupported ZonedDateTime and Duration, and Instant without global constructors. +A rejecting index precedes a healthy sibling. The independent expected admission +set comes from the approved persistence domain, not the serializer's output. + +The real Collection and persisted wrapper drive all cases. At startup, preload +must resolve. After queued index work settles, assertions compare readiness, +exact rows, exact local and coordinator index signatures, and warning paths and +error classes. A subsequent ordinary subset load must retain the exact row. +Supported controls require both indexes and no warnings. Runtime unhandled +rejections are also reported by the test runner. + +| Subject | Result | +| -------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | +| Original source, before the fix | Six failures: three startup rejections and three runtime warning mismatches; two supported controls pass. | +| Only the local spec builder moved inside its catch | Six failures remain, including runtime one-warning-versus-two assertions; two supported controls pass. | +| Both existing catches cover spec construction | All eight new cases and the existing failed-local-completion witness pass. | +| Surrounding affected owners | 782 tests pass in 11 files, zero failures, one existing TODO. | + +The partial-fix mutant uses a scratch source-loader substitution with a reached +receipt. No setup failure or timeout counts as a kill. The tests restore global +Temporal and warning instrumentation with failure-preserving cleanup. + +This closes the escaping-spec-construction class at both wrapper boundaries +within the declared startup/runtime histories. The real SQLite index and native +value owners retain their separate receiving claims and passed in the surrounding +run. This witness does not establish browser scheduling or arbitrary third-party +coordinator behavior. No known in-scope counterexample remains. + +## Validation and weight + +TypeScript passes. ESLint has zero errors and 22 existing warnings in the changed +files. Prettier and whitespace checks pass. Production adds two and removes four +lines (net minus two); the existing oracle gains 113 lines. No oracle owner is +added. The coverage map and changeset record the strengthened boundary. + +Scratch evidence: `/private/tmp/temporal-cr3-index-red.json`, +`temporal-cr3-coordinator-red.json`, `temporal-cr3-coordinator-reach.json`, +`temporal-cr3-index-green.json`, and `temporal-cr3-surrounding.json`. +The complete lossless review evaluation is in +`/private/tmp/temporal-cr3-evaluation.md`: nine items, one fixed and eight +duplicates of that fix or previously evaluated guidance and accepted limits. diff --git a/docs/contributing/oracle-reviews/issue-2034-law-enforcement.md b/docs/contributing/oracle-reviews/issue-2034-law-enforcement.md new file mode 100644 index 0000000000..c4ca02be58 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2034-law-enforcement.md @@ -0,0 +1,110 @@ +# Temporal persistence: law enforcement audit + +Audit base: `bc00255f8dd577214b5fbf049487dab333b5a9ab`. This supplements +HE-008 and CR3-001. The question is whether the law reaches a discriminating +assertion, not whether the reported example or surrounding suite passes. + +## Conclusion and reviewer assessment + +The previous closure statement was too strong. The production repairs still +satisfy the expanded checks, but three wrong implementations passed the relevant +old assertions: a validator that changes a caller's Date, a coordinator boundary +that silently swallows asynchronous index failure, and release with a newly +copied options object. The latter already had reference-identity checks in other +acquisition histories; those did not establish that the rich local/follower +receiving history enforced the same promise. + +CodeRabbit's index finding remains accurate and its two-call-site repair is +appropriate. Hire for that scoped control-flow review. Its review did not supply +executable law coverage. Our earlier evaluation added a useful reproduction but +did not sufficiently challenge read-only builtin state, independent collaborator +outcomes, or the identity observer. This follow-up repairs those test gaps. +No production behavior, transport domain, or failure policy changes. + +## Laws, authority, and enforcement + +| Law and authority | Independent expectation and reachable histories | Production path and assertion cuts | Calibration and scope | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Admission is read-only, even if a later field is rejected. The wire API validates caller-owned data; freezing a wrapper does not authorize mutation of builtin state. | Compare explicit timestamp, byte, container, key-shape and alias observations with independently specified before/after values. Cross successful validation with an invalid limit after a rich predicate, and retain the original expectation after rejection. The Node receiving owner captures a pre-validation structured clone. Include Date, buffer/view, Map, Set, and shared records; the receiving owner retains all supported rich kinds. | Real validation and encoding. Compare caller state immediately after each call or caught error. Browser receiving compares against a pre-validation snapshot, not live input. | Date-mutating validator passed 22 old targeted checks. It now fails three assertions: accepted input, rejected suffix, and rich receiving. Finite graphs and schema positions are explicit; this does not claim arbitrary proxy safety. | +| Projection creates a fresh, detached data snapshot at each call while preserving aliases within the appropriate schema role. Established wire-value semantics and the existing fresh-projection law. | Capture literal before/after graphs. Between calls mutate Date time, bytes, a shared record, and Map/Set contents. First output must retain before values, second must carry after values. Rich receiving covers cycles, sparse arrays, typed arrays, maps, sets, dates, regexps and special scalars. | Real encoder plus actual Browser coordinator code through controlled local/follower transport. Compare output meaning with captured expectations, identity against caller nodes, backing-buffer aliases, and per-call freshness. Separate role fixtures reject an object reused under an incompatible schema. | Returning a caller Date as the snapshot fails two final assertions; rejected-suffix control still passes. Existing identity-cache and role-confusion witnesses remain. No real-browser scheduling claim follows from the controlled transport. | +| Live lifecycle references stay local and release uses the exact object delivered to the owner. Existing acquisition identity contract. | Local route retains exact signal/subscription; follower route excludes both. Each release must use its corresponding delivered options reference, once. | Real coordinator receiving and release. Check local references on delivery, field absence remotely, release count and reference identity after both releases. | Replacing release options with `{ ...options }` passes the former rich-fixture deep-equality assertion and fails the new `toBe` assertion. Neighboring acquisition tests retain their own exact-reference checks. | +| Wire rejection precedes remote retry/owner admission and is rechecked when a retained request changes route. Approved exclusion of native Temporal from the wire contract, not from supported local SQLite storage. | Independent hostile values and named error paths; both native kinds and string controls; immediate remote demand, held hydration followed by role change, local success followed by gap recovery; release/abort versus retained demand. | Existing persisted and Browser owners. Assert exact error/path, zero owner/RPC entry for invalid requests, no retry admission, and expected terminal Collection state for invalid retained recovery. Valid string controls dispatch; valid siblings after invalid recovery demand remain fenced by the terminal Collection error. | Prior no-admission mutant has 49 reached failures with two controls; omitted detachment check has three failures with a passing DataView control. Existing error/retirement histories remain green. Wire support policy is unchanged. | +| Admission must not construct discarded full request snapshots; each attempted coordinator request has at most one detached payload copy. User-authorized HE-008 work law. | Independent schedule predicts zero, one, or two attempts. Six routes crossed with 0/1/33 bytes: follower, ownership transition, retry, abort, release, gap recovery. Counters measure buffer and ordinary-record copying, including descendants, with positive controls. | Real persisted wrapper and wire encoder behind controlled coordinator. At settlement assert attempt count, label and bytes, caller isolation, shared backing buffers, signal exclusion, and both copy budgets. | Final assertions reject all 18 cases with original guards. This measures those copy operations and the discarded-full-snapshot design, not every allocation primitive or elapsed time. Separate validation traversal remains intentional. | +| Failure of an optional index cannot fail Collection readiness or ordinary data access, and must remain observable through its existing warning channel. Existing best-effort index boundary. | Twelve serializer cases: startup/runtime crossed with both supported Temporal kinds, ZonedDateTime, Duration, and missing globals for each supported kind. Twenty-four collaborator histories: startup/runtime × local/coordinator/both × synchronous/held settlement × success/failure, with a healthy sibling. | Real Collection index addition, serializer and persisted wrapper. At collaborator entry/held cut assert no Collection error and no premature warning. While runtime work is held, assert ready status and the unchanged row. After settlement assert exact local/coordinator call multiplicity, exact warning causes by reference, no unhandled rejection, ready status and exact row. After a further subset load assert the same row again. | Original serializer boundary and local-only repair each fail eight cases with four supported controls in the final matrix. A swallowed coordinator Promise rejection passes 13 previous index checks but fails four new delayed-failure cases; 20 controls pass. Broader index semantics stay with Collection and real SQLite expression-index owners. | +| Local index completion evidence must describe completed successful work, and coordinator RPC must occur outside the local hydration scope. Existing local completion and scheduling contracts. | A held local result cannot yet authorize its coordinator request. After failure its marker/capability are false/absent; after success both identify the completed adapter. Sibling outcomes are independent. Existing crossed-tab grammar varies 2–4 tabs, 1–2 indexes, and routing direction. | New optional-index matrix asserts intermediate absence of target coordinator entry before local settlement, then exact marker and adapter identity. Existing controlled-scope and generated crossed-tab owner asserts coordinator entry after local scope release. | Removing the local await fails eight intermediate assertions while sixteen controls pass. The initial mutant run also contaminated two following cases with delayed errors; cleanup now drains rejection delivery before removing the observer. Those collateral failures are not credited as kills. | + +For each row the law is stated independently of production, its declared +histories reach the relevant code, and asserted observations distinguish the +named wrong design. No known counterexample remains inside these stated +boundaries. Finite witnesses and specific mutants do not establish a universal +proof over arbitrary graphs, hosts, or future implementations. In particular, +construction counters are calibrated sensors, not a total-allocation metric. + +## Finding ledger and loss audit + +| ID | Audit finding | Disposition and durable destination | +| ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| LA-001 | Live-input expected values can hide validation mutation of builtin state. | fixed-now: before-validation snapshots and accepted/rejected input checks in the existing persisted and Browser owners; demonstrated old false green. | +| LA-002 | Rich graph alias checks did not independently establish detachment and later-call freshness for mutable builtins. | fixed-now: explicit source detachment plus before/after mutation histories; shared-Date mutant rejected. | +| LA-003 | The serializer reproduction did not fully encode independent collaborator failure, delayed settlement, error observability, and truthful completion. | fixed-now: 12 serialization cases and 24 collaborator histories; swallowed-error false green and early-completion mutants calibrated. | +| LA-004 | Reconcile wire-domain, retirement, and copy-work claims with actual observers. | already-fixed for the production law: existing hostile/path/retirement and copy-budget owners apply. Tightened the work witness's label and record-identity observations and reran original guards. Preserve the explicit allocation/host limits above. | +| LA-005 | Rich receiving release used deep equality for a reference-identity law. | fixed-now: exact reference/count assertion; copied-release mutant passes the former observer and fails the replacement. | + +Five audit items = four fixed-now oracle gaps + one already-fixed production +law with strengthened observation and explicit bounds. No new production bug is +claimed. No new deferred work or unresolved design decision. Earlier review +ledgers remain lossless; their accounting totals are not replaced by these five +audit items. This report supersedes their stronger-than-evidence oracle closure +claims for the boundaries examined here. + +## Validation and receipts + +- Current production: 812 passed, zero failed, one existing TODO across 11 + affected test files. The new focused set passed 32 tests before the final + full-owner run. +- Date-mutation false green: 22 passed before the test repair; final mutant + three failed at input-preservation assertions. +- Shared-Date output mutant: two failed, one rejected-suffix control passed. +- Swallowed coordinator rejection: 13 old checks passed; expanded matrix has + four failures and 20 passing controls. +- Premature index completion: eight intended failures, 16 controls passed after + cleanup isolation; two initial collateral failures are excluded from evidence. +- Copied release object: former observer passed; exact-reference observer fails. +- Original and partially repaired index boundaries: eight intended failures and + four supported controls each on the final serializer matrix. +- Original wire guards: 18 intended copy-budget failures on the final observer. + +Scratch receipts use `/private/tmp/temporal-audit-*` for focused/mutant results +and reached-source records; `temporal-law-audit-final.json` is the final +11-file run. Source substitutions live only in scratch configs. Production +source is unchanged; tests strengthen two existing oracle owners. The full +append-only audit is `/private/tmp/temporal-law-audit-ledger.md`. + +TypeScript passes. ESLint has zero errors and 32 existing warnings across the +two affected owners. Source and report formatting plus whitespace checks pass. + +## CI observer correction + +CI run `37404114580` on `b700bbf0f` exposed a test-environment mismatch: +the SQLite-core package uses jsdom, while the earlier combined local run used +Node. Both new caller-state cases failed because the expected ArrayBuffer from +`structuredClone` belonged to a different realm. A diagnostic proves its bytes +are still `[7, 8]`, its prototype differs, and the same deep comparison fails +before any production traversal. Its first scratch configuration had a syntax +error; that setup failure is not counted as evidence. + +The persisted witness now compares explicit timestamps, bytes, container +contents, property keys and alias relationships with independently specified +before/after observations. It calibrates the fixture before production entry. +It still checks caller isolation after both validation and encoding, on rejection, +and across later projection calls. This does not add cross-realm support to the +product or waive any byte/identity assertion. + +The repaired cases pass in jsdom and Node. The input-Date mutation still fails +both jsdom cases; returning a shared Date still fails the success case while the +rejected-suffix control passes. The local SQLite-core selection passed 520 tests +across 11 files with one existing TODO in jsdom. Earlier 812-test evidence remains +an explicitly Node-based affected-owner run. TypeScript, lint and formatting pass. +Receipts: `/private/tmp/temporal-ci-jsdom-red.json`, +`temporal-ci-realm-diagnostic-red.json`, `temporal-ci-core-jsdom-green.json`, and +`temporal-ci-observer-{jsdom,node,date-jsdom,shared-jsdom}.json`. diff --git a/docs/contributing/oracle-reviews/issue-2034-recovery-review.md b/docs/contributing/oracle-reviews/issue-2034-recovery-review.md new file mode 100644 index 0000000000..d0c1843e08 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2034-recovery-review.md @@ -0,0 +1,138 @@ +# Mixed-demand recovery review + +Evaluated source: `b407d29015d2e5a611149001bf66f79c0df3cd68`, PR #2039. +The resumed medium review supplied one behavioral concern, a documentation and +coverage caveat, five positive assessments, a review-scope caveat, and its +execution limitation. All nine entries are retained below. Production is unchanged. + +## Verdict and reviewer assessment + +The reported Collection-wide failure is real. M7's recorded implementation +contract explicitly sends invalid retained native demand through the existing +terminal error path during fallback sequence-gap recovery. Valid demands after +that failure do not continue in the failed sync run. The stream does not advance +through the failed recovery; later messages are fenced rather than repeatedly +replaying the same gap. Per-demand isolation would change this contract. + +The prior HE-001 label, “refuted as a recovery/liveness defect,” was too broad. +Its repeated-gap consequence was refuted; the terminal failure and stopped +suffix were observed and preserved deliberately. This record corrects that +wording without claiming a production repair. + +Hire for scoped review with verification support. The reviewer correctly follows +the error through the outer terminal boundary, lowers severity, and flags the +policy ambiguity. Its “no oracle” statement misses the existing single-demand +witness and the earlier two-order scratch probe. The useful new distinction is +that the mixed-demand law was not retained in the permanent owner. No tests were +run by the reviewer, and its positive findings are bounded evidence rather than +proof over every legal input. + +## Finding ledger + +| ID | Claim | Technical verdict and evidence | Action and durable value | +| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| MR-001 | A local leader admits native demand, becomes a follower, and a gap recovery stops on that demand; later valid demands are not queued, position does not advance, and the Collection fails. Same earlier finding remains unfixed. | Accurate behavior in fallback recovery. M7 and the existing terminal coordinator boundary select it. Both native kinds and both demand orders reach it. Later deliveries and loads are fenced by the exact original error. “Unfixed” misclassifies the retained policy. | Duplicate of the existing M7/HE-001 boundary, with the prior overbroad refutation corrected. No catch-and-continue production change. | +| MR-002 | “Reports a collection error” understates terminal failure; no oracle covers the lost siblings. | Partly correct. The existing singleton oracle tests terminal failure, and the prior scratch probe tested both orders. The permanent owner omitted mixed-demand dispatch coverage, reentrant sibling error identity, and the post-terminal suffix. Both calibrated wrong implementations passed the old selection. | Fixed now: extend the existing persisted oracle and clarify the guide. No new owner or lifecycle. | +| MR-003 | Removed hydration ensure duplicated the direct ensure; no await between post-wait validation and routing. | Source confirms the removed dispatch duplication. Existing entry/post-hydration admission witnesses and original-guard controls remain applicable. The gap in MR-002 is another path. | Already fixed in earlier work; preserve exactly-once direct-dispatch controls and role-transition admission. | +| MR-004 | Validation-only mode does not mutate input and preserves the projection traversal's caching. Repeated discarded projections were fixed. | Confirmed within the existing law audit: accepted/rejected rich input observations, detached aliased snapshots, reached Date-mutation and shared-Date controls, and 18 original-guard copy-budget failures. Separate validation traversals remain. | Already fixed. Do not convert bounded copy observations into total-allocation or latency claims. | +| MR-005 | Temporal fixed-width keys order the full ranges; equality matches core including calendars. | Consistent with the codec rule and endpoint/precision/calendar witnesses. Tests sample boundary families; this review is not an exhaustive proof of every representable value. | Duplicate of typed-value and expression-index evidence; retain its explicit host/range limits. | +| MR-006 | SQL placeholders are not duplicated; mixed IN and native range/NOT candidates are supersets; NUL literal construction terminates. | Consistent with prior receiving SQL, candidate-row, polarity, binding and NUL controls. Those assertions, rather than the review's unexecuted approval, supply evidence. | Duplicate of existing compiler and expression-index law checks. | +| MR-007 | Clone retains Date/bigint and native values, changed index SQL rebuilds through DROP, obsolete entries are documented. | Supported value-domain claims agree with current receiving tests. “Matches old JSON clone” is not universal: preserving native values intentionally changes JSON's lossy behavior. Old/new registry and explicit reset histories retain upgrade evidence. | Duplicate of bounded follow-up and index-value owners. No automatic destructive migration. | +| MR-008 | Other high-review items were not adjudicated; unsupported PlainDateTime/ZonedDateTime and matching global constructors merit a higher-effort pass. | The review's scope limit is honest. HE-003/004 already reproduce those restrictions and trace them to selected v2 M1. Wrapper request identity itself needs no global constructor. Engine-native Chrome/polyfill interoperability remains an explicitly unclaimed receiving boundary. | Duplicate of selected restrictions and existing host evidence limits; no feature expansion or new blanket completeness claim. | +| MR-009 | The reviewer ran no tests because its worktree lacked node_modules. | That accurately describes its evidence limitation, not an inability to run current-source checks. Cached dependencies and source aliases execute this checkout without installing or changing dependencies. | Already fixed as an evaluation evidence gap: local executable checks and calibrated controls below. | + +## Law, model, histories and observations + +Authority is the existing terminal coordinator-recovery boundary, M7 in +[the v2 model](issue-2034-design/v2-model.md), the glossary's distinction between +abort and acquisition release, and the primary persisted owner's error laws. +The review does not authorize extending the Temporal wire domain or isolating +failed recovery per demand. + +The new bounded rule names two independent demands: a candidate and a valid +sibling. Its inputs are candidate kind (Instant, PlainDate, string), insertion +order (candidate first/last), and lifetime (retained, aborted, released). All 18 +cells run. The model predicts terminal versus ready state and the permitted +remote-dispatch coverage. Aborted or released candidates cannot trigger wire +admission failure. A retained native candidate terminates recovery. An earlier +valid demand may already be in flight; a later valid sibling is fenced. With +only admissible demand, both retained demands must eventually dispatch. + +The model uses those fixture labels and the stated contract, not production +queues or the production validator. Dispatch attempts are recorded in order +with exact request objects. Presence checks judge demand coverage; duplicate +transport attempts are not forbidden by this law. The trace is not presented as +an exactly-once or bounded-work result. + +The driver uses the real Collection and persisted wrapper, an ownerless elected +coordinator, and recording persistence. It completes both local loads, applies +abort/release if selected, becomes follower and emits a sequence gap. The +coordinator omits `pullSince`, so this tests fallback reload, not successful +replay-delta recovery. Remote settlement remains held while failure is observed. + +Observation cuts are explicit: + +1. Before the role change, both local loads complete and remote attempts are empty. +2. While transport is held, recovery reports terminal error for retained native + demand, or remains ready for controls. The error callback reenters a sibling + load before core marks the Collection failed; the runtime must already reject + with the original error object. +3. At admission failure, dispatch coverage contains only the permitted prefix. + Successful controls check complete coverage after remote settlement, allowing + serial dispatch. +4. After terminal error, a late transport rejection, two more gap messages, and + advancement of the controlled retry clock cause no more hydration or remote + attempts. Another sibling load rejects with the same error; reporting occurs once. +5. Every retained lease can still be released with the exact original object. + Aborted demand still owns a release; previously released demand is not released twice. + +The comparison does not inspect private stream counters. Source reading confirms +that failed recovery returns before stream advancement; the public law checked +here is that no later recovery or load silently continues in the terminal run. +No assertion claims row correctness, real browser elections, arbitrary custom +coordinators, successful pullSince recovery, or native row transport. + +## Calibration and verification + +Two plausible wrong implementations demonstrate old false greens: + +- Recover only the first active demand: all three old singleton cases pass. + The final expanded checker rejects seven cases at dispatch-coverage or + Collection-status assertions; eleven distinguishing controls pass. +- Store a newly created terminal error while reporting the original: all three + old cases pass. The expanded checker rejects all four retained-native/order + cases at exact-error identity; fourteen controls pass. + +Mutants transform source in temporary test configurations; production files were +never edited. These final kills are assertion failures, not setup errors or +timeouts. The first draft incorrectly required full successful dispatch while +transport remained held; two controls rejected that draft on real production. +The corrected successful checkpoint waits for settlement. Initial mutant +receipts also included two timeouts and are excluded from the final calibration. + +The final focused owner passes 18/18. The SQLite-core jsdom selection passes 538 tests across 11 files, with one existing TODO and no failures. TypeScript passes. ESLint reports zero errors and 22 existing warnings. Source/report formatting and whitespace checks pass. The final timer observer starts its controlled clock before releasing the late rejection; the focused GREEN and both RED controls were rerun after that ordering improvement. Scratch receipts are temporal-medium-focused-green.json, temporal-medium-core-green.json, temporal-medium-first-only-final-red.json, temporal-medium-error-identity-red.json, temporal-medium-types.log and temporal-medium-eslint.log. + +Oracle-guide audit: ORC-001/002 use the existing M7/error/lease contracts and +independent fixture-label rule. ORC-003/005 keep prose, rule, grammar, real driver +and cuts beside the executable tests. ORC-006 uses both reached mutants. +ORC-004/007 generated-campaign requirements do not apply to this fixed exhaustive +18-cell matrix; all axes and their distinguishing observations are stated here. +ORC-008 adds no state machine. ORC-009 retains glossary vocabulary. +ORC-010 preserves primary failure, restores spies/timers and releases held work. +ORC-011 has no named shared semantic classifier: the independent law does not +call production validation. ORC-012 is this record. ORC-013 uses string, +abort/release and both order controls. ORC-014 explicitly limits the controlled +coordinator result; Browser/Electron receiving owners remain separate. + +## Loss audit and remaining decisions + +Nine entries = one fixed-now coverage/documentation gap, three already-fixed +items, and five duplicates. No new production fix, deferral or design decision +is claimed for this review. The approved Collection-wide terminal policy is +retained. Complete law enforcement is claimed only at the five cuts across the +18 fallback-recovery histories above; receiving-host and wider lifecycle claims +remain bounded as stated. + +The separate late-added-handler question from the later CodeRabbit summary +(CR4-002) remains unresolved. This review does not answer it. It neither requires +nor justifies changing recovery policy for ordinary startup-declared handlers. diff --git a/docs/contributing/oracle-reviews/issue-2034-temporal-persistence.md b/docs/contributing/oracle-reviews/issue-2034-temporal-persistence.md new file mode 100644 index 0000000000..fd7fa516c7 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2034-temporal-persistence.md @@ -0,0 +1,167 @@ +# SQLite Temporal persistence reproduction + +Issue: [#2034](https://github.com/TanStack/db/issues/2034). +Owner: [SQLite typed-value oracle](https://github.com/TanStack/db/blob/main/packages/db-sqlite-persistence-core/tests/sqlite-temporal-value-oracle.test.ts). + +## Revision and scope + +Recorded 2026-10-05 against production commit +`84dc899bfc8d6a16487c1584dbb1cb177109aacc` (`origin/main`). +Branch: `codex/temporal-persistence-oracle`. +The oracle is an uncommitted diagnostic addition with SHA-256 +`ec38aeca3145a6b51561247d3dc3996ab50a05317414d070147728130ffd656b`. +Production code is unchanged. This is a reproduction, not a bug-class repair. + +The requested Temporal preservation behavior comes from the issue. Existing +core tests establish Date/BigInt preservation but do not establish a complete +Temporal support policy. Explicit rejection remains an alternative product +choice. These tests deliberately expect preservation and remain RED; they are +not expected-failure tests and must not be presented as passing CI. + +The independent model assigns two fixture ordinals to earlier/later values. +It predicts literal type/text observations and query keys without production +serialization, comparison, or SQL helpers. It does not prescribe stored bytes. + +## Execution receipt + +Runtime: macOS arm64, Node 24.19.0, SQLite 3.53.3, Vitest 3.2.4, +`temporal-polyfill` 0.3.0. The issue used polyfill 0.3.2; this reproduction uses +the repository's existing locked version. + +The full bounded matrix ran 21 tests: **13 passed, 8 failed**, exit 1. +All 16 production cases reached a file-backed SQLite close/reopen. +Eight Date/ISO-string control cases and five checker-calibration tests passed. +All eight Instant/PlainDate cases failed at public value checkpoints. + +| Observation after reopen | Temporal result | +| --- | --- | +| `loadSubset` hydration and `scanRows` | Direct and nested values restored as `{}`; inline and separate row metadata also lost their type and value. | +| `loadCollectionMetadata` | Direct and nested values restored as `{}`. | +| `pullSince` from the previous committed position | Row values, separate row metadata, and collection metadata in the replay payload restored as `{}`. | +| Equality at the earlier value | `[]` instead of the earlier row's key. | +| Greater-than the earlier value | `[]` instead of the later row's key. | +| Ascending one-row windows, offsets 0 and 1 | Insert order happens to match; after the values swap, both pages retain the old order and fail. | + +Both index configurations produce these results. Indexed cases verify that +`ensureIndex` installed a real SQLite index and that it survives reopen. They +do not assert optimizer index use. The existing expression-index owner retains +that responsibility. Query failures on corrupted values do not independently +establish a compiler defect with correctly encoded Temporal values. + +The selected `PlainDate / update / indexed=false` replay ran one test, skipped +20, and failed all eight named checkpoints (exit 1). Its hydration diff includes: + +```text +expected stamp: { kind: "PlainDate", text: "2026-01-02" } +actual stamp: {} +expected equality keys: ["a-late"] +actual equality keys: [] +expected first page: ["a-late"] +actual first page: ["z-early"] +``` + +The normal package configuration includes this new `*.test.ts` file. With a +complete dependency installation, its standard focused command is: + +```sh +pnpm --filter @tanstack/db-sqlite-persistence-core test \ + tests/sqlite-temporal-value-oracle.test.ts \ + --coverage.enabled=false --typecheck.enabled=false +``` + +The fresh installation could not complete: the configured package proxy returned +403 for Rollup 4.64.0, and the public registry returned 503. The stalled retry +was stopped. The standard command above was therefore not the local receipt. +Local execution used cached test tools, a temporary Vite configuration, and +explicit aliases to **this worktree's** DB, db-ivm, and persistence source. +External libraries came from the existing checkout. No prebuilt DB distribution +or mocked persistence adapter supplied the result. + +Exact local commands: + +```sh +/Users/kyle.mathews/programs/tanstack-db/node_modules/.bin/vitest run \ + --config /private/tmp/temporal-persistence-vitest.config.mjs \ + --reporter=json --outputFile=/private/tmp/temporal-persistence-final.json + +/Users/kyle.mathews/programs/tanstack-db/node_modules/.bin/vitest run \ + --config /private/tmp/temporal-persistence-vitest.config.mjs \ + -t 'PlainDate.*update.*indexed=false' +``` + +The JSON report and selected replay log are local scratch receipts at +`/private/tmp/temporal-persistence-final.json` and +`/private/tmp/temporal-persistence-replay.log`. The results above preserve the +verdict-critical evidence without depending on those temporary files. + +TypeScript checking of the oracle and its imported source passed (exit 0), using +a temporary config with source and cached dependency aliases. ESLint passed +without warnings (exit 0); Prettier formatting passed. A full package test run +and a full frozen-lockfile installation are not claimed. + +## Why previous tests missed it + +The shared core adapter's typed-value test supplies Date and BigInt. The Node +expression-index grammar supplies scalar values, Date, and BigInt. Neither +supplies real Temporal objects or observes their type plus text across reopen. +More runs over those domains cannot reach the issue. + +The new owner adds that missing value domain and explicit native-type +observation. Its file-handle lifecycle is separate from the shared contract's +injected harness because an adapter recreation alone does not prove physical +SQLite close/reopen. Existing tests and expectations are preserved. + +## Oracle guide evidence + +| Requirement | Outcome | +| --- | --- | +| ORC-001 authority and limits | Established Date controls; requested Temporal extension explicitly identified as diagnostic, with support/rejection policy unresolved. | +| ORC-002 independent judgment | Literal type/text model and ordinal-based key expectations; no production semantic helpers. | +| ORC-003 responsibilities | Opening law/limits, expected-value and expected-row model, 16-cell grammar, real SQLite driver, and named public comparisons are in the oracle. | +| ORC-004 grammar controls | Four kinds × insert/update × absent/present index; unique-cell/count calibration. Each axis's contribution is stated in the header. Two adjacent ISO days bound the range. Unique keys and increasing positions exclude duplicate inserts and stale transaction positions. | +| ORC-005 production reach | Each test commits through the real adapter, replaces the SQLite handle and adapter, verifies index presence/absence, and checks hydration, scan, metadata, replay, and query results. | +| ORC-006 calibration | Five passing checks reject empty-object loss, ISO-string type erasure, same-kind wrong values, omissions, duplicates, and reversed output through the same checker. Unmodified production itself fails at the intended checkpoints; no production mutant was installed. | +| ORC-007 campaigns/replay | Random-campaign requirement does not apply to bounded enumeration. All 16 cells execute. Selected-name replay was checked and reproduces the same mismatch. | +| ORC-008 state minimality | No state machine added. A pure recomputation retains kind, value ordinal, key, and insert/update history because each changes a checked output. | +| ORC-009 vocabulary | Uses adapter transaction, row metadata, collection metadata, replay, and checkpoint terms. Type/text records are explicitly model-only observations. | +| ORC-010 failure fidelity | Soft assertions retain all reached mismatches before teardown; Vitest separately reports afterEach failures. Registered harness cleanup closes the handle and removes its owned temporary directory. No shrinking. | +| ORC-011 second formulation | No shared semantic comparator is used: the query model uses fixture ordinals. No separate formulation is claimed or needed to identify this serialization loss. | +| ORC-012 review evidence | This record names the production revision, oracle hash, outcomes, execution limitations, and open scope. No closure claim. | +| ORC-013 distinguishing boundary | Same-type distinct dates, equality versus strictly greater, swapped values, both page offsets, and index presence distinguish empty-object/type-erasure designs within the finite grammar. No arbitrary-value or threshold proof. | +| ORC-014 host handoff | SQLite itself stores the file and supplies reopen results; Temporal comes from a real polyfill. The thin sequential driver does not claim production Node-driver, Expo, OPFS, or native mobile conformance. | + +## Open scope and ownership + +Issue #2034 and this typed-value owner retain the support/rejection decision, +other Temporal kinds, native constructors, precision/calendar variations, +truncate/delete and longer histories, compatibility with old bytes, and host +receiving witnesses. A future support repair must cover literal encoding and +index/comparison behavior in the expression-index owner as well. The current +reachable counterexamples keep the bug class open. + + +## Current-main replay and design follow-up + +On 2026-10-05 the worktree was fast-forwarded without conflicts to +`e21c280f3c4e56f6c0d05ec0eed87db29c623489`, including #2002. The unmodified +Temporal oracle again ran 21 tests: 13 passed, 8 failed, exit 1, using the same +cached-tool/source-alias setup. Its captured report is +`/private/tmp/temporal-persistence-current-main.json`. This extends the RED +receipt; it does not supersede the original revision record above. + +The exploratory [design grammar and stress readings](issue-2034-design/README.md) +map the proposed value-domain repair to current primary oracle owners. They do +not implement a fix, revise established lifecycle laws, or claim closure. + + +## Implemented v2 follow-up — 2026-10-05 + +The [v2 design grammar](issue-2034-design/v2.md) now specifies and implements +native Instant/PlainDate preservation, shared SQL/index representations and +remote-demand admission limits. Existing owners were extended before the repair; +original-production and hostile controls demonstrate distinguishing failures. +The final affected suite has 471 passing tests, one existing TODO and no failures; +TypeScript and lint pass (25 existing lint warnings). The linked evidence keeps +exact source hashes, receiving limits and separate production/test/doc weight. +This supersedes the open implementation status above while preserving its RED +receipts. It does not establish native multiprocess or untested host support. diff --git a/docs/contributing/oracle-reviews/issue-2034-wire-validation-work.md b/docs/contributing/oracle-reviews/issue-2034-wire-validation-work.md new file mode 100644 index 0000000000..44854ef688 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2034-wire-validation-work.md @@ -0,0 +1,120 @@ +# Remote subset validation work follow-up + +The [law enforcement audit](issue-2034-law-enforcement.md) strengthens the +original witnesses below and supersedes their earlier completeness claims. + +Baseline: `e61cda19997c169a59b6e345197a36eca9e75ec7`. +This follow-up supersedes the open HE-008 disposition in the bounded review. +The user authorized encoding the work law before implementing the repair. + +## Law and repair + +Remote admission validates request data without constructing a discarded wire +snapshot. Each coordinator attempt may construct at most one detached payload +snapshot. Demand retired during hydration needs no payload snapshot. Validation +retains the existing accepted domain, exact rejection paths, and read-only input +behavior. Coordinator snapshots remain detached and fresh at each projection +call; local signal/subscription references and acquisition identity remain local. + +The three persisted admission guards now use `validateRemoteSubsetOptions`. +Validation and projection share the existing traversal and checks. Validation +retains source nodes only as per-call cycle markers and skips writes and payload +construction. A zero-length typed-array view checks ArrayBuffer detachment +without copying its bytes. Only the coordinator constructs the wire snapshot. +There is no request cache, new retry state, wire-format change, or new value kind. + +This removes duplicate copying, not the validation traversal at separate +admission and coordinator boundaries. It does not claim a latency bound or zero +allocation: path strings, descriptors, visit maps, and zero-length detachment +checks remain. Future traversal reuse would need a separately justified ownership +boundary; it is not necessary to close the discarded-projection finding. + +## Oracle evidence + +The persisted owner previously protected rejection and snapshot semantics but +had no construction-work assertion. The new bounded matrix uses follower load, +leader-to-follower hydration, one transient failure plus retry, abort during +hydration, release during hydration, and retained-demand gap recovery. Each runs +with 0, 1, and 33 byte payloads. The independent schedule predicts 0, 1, or 2 +coordinator attempts. A controlled coordinator uses the real wire encoder. + +The check counts backing-buffer copies and ordinary-record property copies, +including copies descended from an earlier copy. A direct encoder positive +control calibrates both observation seams. At request settlement it checks the +attempt count, detached bytes, shared backing-buffer identity, signal exclusion, +and copy budget. Frozen input and the existing later-call snapshot witness +protect read-only validation and fresh projection behavior. + +The Browser coordinator owner applies its existing independent hostile-value, +exact-path, and incompatible-schema-role fixtures to validation-only admission. +Its rich local/follower receiving witness also validates a frozen graph containing +cycles, aliases, maps, sets, dates, RegExp, views, and every supported typed-array +kind before checking delivered semantics and exact lifecycle release identity. +These are controlled Node transport seams, not real-browser/OPFS claims. + +| Experiment | Result at the intended checkpoint | +| --- | --- | +| Initial test-first matrix on unchanged production | 12 copy-count failures; value and isolation observations passed. | +| Final matrix with original persisted guards loaded from the baseline | All 18 cases reject excess copies: success 2 versus 1, retry 4 versus 2, cancelled demand 1 versus 0. | +| Current production | All 774 tests pass across 11 affected suites, with one existing TODO. | +| Omit validation-only admission | 49 assertion failures: 45 independent rejection/path checks and four native admission histories; two string controls pass. | +| Omit detached-buffer validation | Three expected-rejection assertions fail; detached DataView remains a passing control because its own accessor check still rejects. | + +All mutant runs use scratch source-loader substitutions with reached-file +receipts. No production source was left mutated. An initial Browser run failed +before setup because the scratch configuration lacked a subpath alias; that run +is not RED evidence. The corrected alias runs the real coordinator sources. + +TypeScript passes with cached dependency and Vite worker declarations. ESLint +reports zero errors and 32 pre-existing require-await warnings across the two +owners. Prettier CLI and whitespace checks pass. + +## Guide audit and limits + +- ORC-001/002: the authorized work bound and established wire/ownership contracts + supply the law. Schedule counts, literal bytes, and named error paths supply + independent expected observations. +- ORC-003/005: prose beside the new matrix separates law, bounded histories, + production path, observations, and settlement checkpoint. Positive controls + and observed coordinator entry establish reach. +- ORC-004/007/008: not applicable to the new checks. The matrix exhausts its + declared fixed schedules and sizes; it adds no generated property or stateful + model. Existing generated owner campaigns run unchanged. +- ORC-006: original guards and the two hostile mutants fail at the intended + assertions, with passing controls. No timeout is counted as an assertion kill. +- ORC-009: demand retirement, coordinator attempt, request data, and snapshot + remain distinct. Counts concern coordinator calls, not packets or publications. +- ORC-010: the new matrix uses the owner's failure-preserving cleanup and restores + global spies before cleanup diagnostics. Other receiving fixtures retain their + existing resource cleanup. +- ORC-011: a shared-validator fault is challenged by independently specified + hostile values and exact paths, not merely validator/encoder agreement. +- ORC-013: cancelled/live demand, first/retried attempts, and valid empty/detached + buffers distinguish nearby wrong boundaries. Frozen requests reject input + rewrites; the existing later-call witness rejects global identity caching. +- ORC-014: construction credit is limited to the persisted wrapper and real + encoder behind a controlled coordinator. Browser receiving tests use real + coordinator code and structuredClone with controlled host seams. Actual browser, + Electron, and native-host scheduling remain with their existing owners. + +The closed class is discarded full request snapshots in the three persisted +admission guards across this bounded history matrix. Validation traversal cost, +arbitrary third-party coordinator work, and runtime latency are not measured. +No known counterexample remains within the stated boundary. + +## Code weight and receipts + +Production adds 153 lines and removes 83 (net +70). Tests add 304 lines and remove +one across two existing owners; there is no new oracle owner. The extra branches +select copy construction in one traversal instead of duplicating validation or +introducing a prepared-request lifecycle. + +Final production blob IDs: +- `packages/db-sqlite-persistence-core/src/persisted.ts`: `d8b6ad58093d2387487642886256e3830a27b65e` +- `packages/db-sqlite-persistence-core/src/remote-subset-wire.ts`: `b40804f1fe5c202856f089fe1cd16941bcb11a0d` + +Scratch receipts under `/private/tmp`: +`temporal-wire-work-red.json`, `temporal-wire-old-guards-final-red.json`, +`temporal-wire-no-admission-red.json`, `temporal-wire-no-detachment-red.json`, +`temporal-wire-work-surrounding.json`, `temporal-wire-work-types.log`, and +`temporal-wire-work-eslint.log`. Each mutant has a matching `*-reach.json`. diff --git a/docs/contributing/oracle-reviews/issue-2036-admission-follow-up.md b/docs/contributing/oracle-reviews/issue-2036-admission-follow-up.md new file mode 100644 index 0000000000..8675ff65e4 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2036-admission-follow-up.md @@ -0,0 +1,325 @@ +# Accepted sync transactions after persistence reads + +## Status and revisions + +The high-effort review found a real adjacent failure in PR #2037 at +`5688e232eff34eeac555f2586a34672bad25865b`. The earlier merge-ready assessment is +withdrawn. The local repair passes the tests below. The maintainer has now +specified persisted-baseline precedence; the dated follow-up below supersedes +the initial pending-policy assessment. No broad bug-class closure is claimed. + +This record supplements the [original audit](issue-2036-queued-hydration-publication.md). +The initial evaluation checked that exact PR head and these candidate blobs: + +- Production: `9c5d3563c0e2e72005c1ed9b5756c951b3035857`. +- Oracle: `048adb1f38d39e73614407b6eb7ad190fe47b580`. +- Pre-PR comparison: `cfb03f201bb21d82b7f537a9fbe59d3623f395d6`. + +The author and bot commits remain intact. The first evaluation left changes +local while the policy question was pending. The dated follow-up records the +subsequent maintainer decision and verification. Historical hashes identify +historical evidence; they are not silently replaced with new hashes. The initial +formatting-only successor reflowed one `flatMap` expression. + +## Missing history and repair + +The existing metadata-ownership test already supports beginning during H1 and +committing after H1 finishes. The old 24-cell grammar only begins before any +hydration. It cannot reconstruct this supported admission timing. + +The new eight-cell grammar begins during H1, retains its captured metadata +ownership, and commits after H1 finishes. H1's adapter scope remains held after +its task completes. In ordinary mode, one or two later subset loads reserve the +mutex first. In immediate mode, existing admission logic holds those loads +behind the source. The driver records the load count at source durability to +prove these different orders. The other axis is whether H1 supplied the source +key. All receipts settle before rows, metadata and status are compared. + +At the reviewed head, all four ordinary cases reject the source receipt with +`a persisted sync transaction cannot cross a hydration cycle`, retain the old +row, and set Collection status to `error`. The four immediate cases pass. +Instrumentation confirms ordinary admission is accepted at sequence 1 but +publication checks sequence 2 or 3. The original matrix always has +`queuedBecauseHydrating === false` at source admission. + +The candidate retires `openTransaction.hydrationSequence` only after ordinary +commit admission. The publication check remains unconditional. Queued replay +retains its sequence, and all transactions retain their captured +`hydrationContext`. The flag is not cleared or reinterpreted: it also controls +metadata treatment and immediate replay. The candidate is two production lines +smaller than the PR, and adds three lines over the integration main. + +## Lossless review reconciliation + +The reviewer supplied eight numbered findings and two omitted notes. Each is +retained separately below. Overlap does not remove an item. + +| ID | Technical judgment | Action and durable value | +| --- | --- | --- | +| H01 | Confirmed P1. Four legal ordinary histories reproduce accepted-commit failure at the receipt/status/row checkpoint. | `fixed-now` locally. Eight adjacent histories remain in the persistence oracle. | +| H02 | Correct distinction between captured hydration ownership and commit's replay branch. Clearing the existing flag would conflate other responsibilities. | `duplicate` of H01. The suggested retirement of the sequence on ordinary admission is the implemented alternative. | +| H03 | Confirmed missing timing dimension. Instrumentation proves the original matrix never flags its source transactions. | `fixed-now`. The new grammar varies timing, captured key membership, count and admission order. | +| H04 | Confirmed scope change, not a proven corruption result. Accepted inserts and deletes survive queued collection-reset and coordinator full-reload notifications on the PR; they reject on pre-PR main. | `fixed-now` oracle coverage after the maintainer decision below. Accepted sync transactions follow the older persisted baseline. Compatible rereads and ordered truncate replay now have 90 permanent histories; invalidated resume points remain outside this evidence. | +| H05 | The claimed contradiction is refuted: the comment says fresh inserts reset metadata, not complete row values. A quiescent pre-PR comparison preserves omitted detail in all 24 cases. The reference does not import production classifiers. | `refuted` as a defect in this scheduling change. Added prose identifies retained legacy upsert/partial-value behavior. This is compatibility evidence, not an independent proof that all legacy insert semantics are ideal. | +| H06 | The original prose explicitly limited the result to 24 histories, but its checked broad title could imply more coverage. H01 defeats that wider reading. | `fixed-now`. The coverage item is open, with supported histories and remaining reset, cancellation, cleanup and failure witnesses named. | +| H07 | The old record identifies an older blob. The claim that formatting invalidates the results is too strong: the exact reviewed head passes its 39 focused checks, and fresh controls reproduce the intended failures. | `fixed-now` evidence receipt. This record preserves historical provenance and pins the tested local candidate blobs. | +| H08 | Recording occurred before adapter completion, but the claimed false green is refuted. Injecting a rejection after that recording makes all 24 original cases fail on receipt/status/durable-state assertions. | `fixed-now` recorder precision: record titles after adapter success. No expectation is weakened. | +| H09 | Confirmed behavior change without a demonstrated defect. A delete issued while its public row is absent removes the row after later hydration supplies it. Receipts fulfill and public/durable state agree. | `fixed-now` permanent delete dimension in the 90-history grammar, including on-demand commits before the persisted row is public. The omitted-public-delete mutant fails all ten no-truncate delete histories. | +| H10 | The speculative leftover-replay/reset failure was not reproduced. Two commits queued during H1 drain before a reset queued behind that scope, on both reviewed head and candidate. | `confirmed-open` with an evidence gap, not a confirmed product defect. A smallest failing schedule is still required; the owner and bounded successful control are recorded in the coverage map. | + +Current totals: ten raw items = seven fixed locally + one duplicate + one +refuted + one open evidence gap. H04 was initially a design decision and H09 +was initially deferred; the dated follow-up resolves both within its stated +scope. H10 still lacks a failing history. No item is discarded. + +## Initial evaluation: evidence and limits + +The final candidate passes 23 package test files: 746 tests, two existing TODOs, +and no type errors. Standalone TypeScript passes. ESLint reports no errors and +22 existing `require-await` warnings. No warnings occur in the added block. +The same cached third-party toolchain limitation as the original audit applies. +DB and DB-IVM resolve to builds from this worktree. + +| Control | Result at the intended checkpoint | +| --- | --- | +| Restore the reviewed publication guard | Four new ordinary cases fail receipt/status/row comparison. | +| Restore integration-main production | Original 24 cases and four new ordinary cases fail. | +| Remove ordinary commit validation | Existing begin-before/commit-after test fails its missing-throw assertion. | +| Retire captured metadata context with the sequence | Four owner-supplied cases fail metadata comparison. | +| Remove ordinary insert's explicit metadata deletion | All 12 original insert cases fail metadata comparison; updates pass. | +| Reject adapter write after the original early recorder | All 24 original cases fail, disproving the proposed false-green mechanism. | +| Establish baseline before source writes on pre-PR production | All 24 original row/metadata comparisons pass, supporting compatibility. | +| Queue reset or coordinator full reload before accepted insert/delete | Six route/operation probes pass on reviewed head and candidate, and fail on pre-PR main. The subset route is the comparison control. | +| Queue two hydration-owned commits, then reset | Both replay receipts fulfill and the second value survives reset; no leftover failure is established. | + +Every negative result above is an assertion failure, not a timeout or setup +failure. Temporary mutants were removed and candidate hashes rechecked. An +initial nonexistent config path and one transient formatting syntax error were +setup failures, corrected and excluded from behavioral evidence. + +The reset probes use real runtime coordinator-message handling and a controlled +adapter. They do not establish browser cross-tab delivery or choose reset +precedence. RFC #1659 invariant 7 allows durability/replay or observable failure; +it does not by itself choose the result of accepted work across a full reset. +Source-truncate notification, schema-changing resets, native-host execution, +and arbitrary reset/replay interleavings remain outside these probes. + +## Initial evaluation: oracle-guide delta audit + +| Requirement | Follow-up evidence | +| --- | --- | +| ORC-001 | Supported begin-during/commit-after history derives from the existing metadata-ownership contract; full-reset policy remains explicitly open. | +| ORC-002 | Constant source records and captured key-membership expectations are independent of production sequence/queue helpers. The new source insert supplies its complete expected row. | +| ORC-003 | Local prose states the law, finite grammar, adapter hold, immediate-order difference, observations and settlement checkpoint. | +| ORC-004 | Four ordinary failing histories are reconstructed; four immediate histories preserve the alternate legal order. Captured key membership distinguishes wrong metadata rebinding. | +| ORC-005 | Real source begin/write/commit and subset paths run. Adapter counts witness the source-versus-hydration ordering; receipts, full rows, metadata and status are compared. | +| ORC-006 | Old guard, metadata-context loss, commit-check loss and stale insert metadata all fail the intended assertions. | +| ORC-007 | Not triggered by these bounded enumerations. Existing generated campaigns remain unchanged. | +| ORC-008 | No state is added to the independent reference model. | +| ORC-009 | Shared terms retain glossary meaning; captured metadata ownership and replay admission are explicitly distinguished. | +| ORC-010 | Observations precede bounded cleanup; load and scope gates release in `finally`; each unload is inside the existing cleanup helper. | +| ORC-011 | The named insert-semantics concern is compared with pre-PR quiescent behavior. This establishes compatibility only; ideal legacy insert semantics are not independently proved. No product expectation was rewritten. | +| ORC-012 | All ten review inputs, exact revision/blob receipts, requirement outcomes and unresolved boundaries are preserved here. No class closure is asserted. | +| ORC-013 | Original and adjacent admission timings distinguish the old guard from the candidate; captured key membership kills context loss. | +| ORC-014 | Evidence remains at controlled adapter/coordinator boundaries. Live Electric/OPFS schedules retain their named receiving owner in the coverage map. | + +## 2026-10-05: persisted-baseline precedence decision + +The maintainer specified: "persisted reads are by definition more stale than a +new network read" and requested an oracle update. For the compatible-baseline +histories here, persisted rows precede newer accepted sync transactions from the +sync adapter. Reading those rows cannot revoke acceptance. An authoritative +truncate replay has its own position in source order: it supersedes earlier +writes, and later sync transactions apply to that replacement. + +The previous recommendation to preserve rejection after every reset or full +reload is withdrawn. The six original probes supplied coordinator notifications +that requested another persistence read. They did not establish an invalidated +resume point or a schema-changing destructive reset. Observable failure remains +required when work actually fails; it does not justify rejecting valid work. + +### Final candidate and verification + +The follow-up starts from PR head +`5688e232eff34eeac555f2586a34672bad25865b`. These exact final blobs are the +verification receipt, independent of the later commit that stores this record: + +- Production: `9c5d3563c0e2e72005c1ed9b5756c951b3035857` (unchanged from the initial local repair). +- Oracle: `fff6e71f309c9340de9555c1937675d1e6d21200`. +- Original implementation comparison: `cfb03f201bb21d82b7f537a9fbe59d3623f395d6`. + +The 24 original and eight adjacent histories remain. The new 90-case grammar +has five mode/route combinations, three source operations, three truncate-replay +positions, and two schedules. On-demand Collections acquire a subset or receive +reset/full-reload notifications. Eager Collections receive those notifications. +Each insert/update/delete history runs with no truncate replay, replay before +the change, and replay after it. Commit acceptance occurs either before the +queued read runs or after its publication. Expected results do not depend on +that timing. + +The reference computes the final snapshot directly from source order. A later +replacement wins. Otherwise the newer change decides the shared key, and +unrelated baseline rows survive only without a truncate replay. Row values are +complete and metadata is explicit, so this extension does not redefine partial +updates or the omitted-field question in H05. The cursor is opaque collection +metadata, not proof of a compatible provider resume point. + +The driver holds an earlier persistence write and invokes the real Collection, +persisted wrapper, and coordinator-message handler. The loaded rows and durable +completion trace prove that the older baseline runs before the newer source +changes. The final checkpoint compares every operation outcome, Collection +status, exact public/durable rows, row metadata, and cursor metadata before +cleanup. The mixed operation list includes applied receipts and a subset-load +promise; it does not treat them as the same boundary. + +| Verification | Result | +| --- | --- | +| New grammar on the local repair | All 90 histories pass. | +| Original implementation on the new grammar | All 45 queued histories fail at the outcome/row/status comparison; all 45 serial controls pass. | +| Omit public application of source truncate | All 60 histories containing truncate replay fail final comparisons. The 30 histories without it pass. | +| Omit public application of source delete | All ten no-truncate delete histories fail exact public-row comparison. The other 80 histories pass; truncate makes the omitted delete unobservable at their final checkpoint. | +| Final configured package suite | 23 files pass, 837 checks pass, two existing TODOs, and no type errors. | +| Standalone TypeScript | Exit 0. | +| ESLint | Exit 0, no errors, 22 existing require-await warnings outside the new block. | +| Experimental Prettier CLI and whitespace check | Pass. | + +The mutant runs produced assertion failures, not timeouts or setup failures. +Their test revision preceded the final mixed-operation naming and parameter +formatting changes. The final suite ran on the exact oracle blob above. Every +mutant was removed; the production hash matches the initial local repair. + +Local logs and mutation scripts remain in `.review-2036-2037/high-effort/` and +are not PR content. Earlier cached tool links broke. Fresh installation failed +at the configured registry with 403 and at the public registry with a fetch +failure. Verification used local cached packages: Node 24.19.0, Vitest 3.2.4, +TypeScript 5.9.3, Prettier 3.9.9, and compatible Rollup 4.59.0 instead of locked +4.64.0. Package imports resolve to DB and DB-IVM builds from this checkout. +The lockfile and package manifests did not change. CI must check the locked +toolchain. Failed dependency/formatter setup attempts and the initial recorder +type error are excluded from RED evidence. The optional changeset validation +script is absent; the existing patch changeset covers the only changed package. + +The production follow-up remains +4/-6 lines against the published PR, or ++3/-0 against its integration main. The oracle follow-up is +457/-5 lines +against the published PR, including the earlier eight-history repair. This +precedence extension adds no production state or behavior beyond that repair. + +### Additional review and loss audit + +A fresh correctness review found no blocking model, driver, or product defect. +The simplification review recommended only clearer naming for the mixed wait +list. It recommended keeping the finite grammar and all observations intact. + +| ID | Raw finding or note | Evaluation and disposition | +| --- | --- | --- | +| P01 | The new receipts list also contains a subset-load promise. | Direct source confirms the terminology mismatch. `fixed-now`: use pendingOperations/retainOperation and name both settlement kinds. | +| P02 | The final checkpoint does not establish intermediate publication or individual receipt timing. | Correct limit of the declared settled-result law. `deferred` stronger claim to the persistence-history owner and coverage map. | +| P03 | Controlled notifications do not establish real cross-tab delivery or invalidated-resume behavior. | Correct controlled-boundary limit. `deferred` to the named persistence-history and browser OPFS/Electric owners in the coverage map. | +| P04 | Pending-reset-policy and missing-delete documentation is stale after the new evidence. | Direct source confirms both statements need revision. `fixed-now` in the coverage map and this record. | + +The new review totals are four = two fixed-now + two deferred with named owners. +The original ten-item totals are seven fixed-now + one duplicate + one refuted ++ one confirmed-open evidence gap. H10 still needs the reviewer's smallest +failing schedule. The original reviewer remains a strong hire for finding the +supported begin-during/commit-after history by inspection. Its overlapping +items and overstated test-integrity claims retain their original dispositions. + +### Oracle-guide audit for the precedence extension + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | The maintainer decision establishes precedence. The glossary defines sync transaction, applied receipt, publication and truncate replay. Compatible-baseline and settled-result limits are explicit. | +| ORC-002 | The reference derives exact snapshots from ordered replacement and newer insert/update/delete effects. It reads no production flags, sequence counters or classifications. | +| ORC-003 | Prose precedes the grammar, explains the independent algebra, describes controlled production scheduling, and names the final observations. | +| ORC-004 | Bounded enumeration covers 5 mode/routes × 3 operations × 3 replay positions × 2 schedules. Each axis changes a path, effect or precedence boundary. Both notification routes, eager startup, absent-key delete, and both replacement orders are reconstructible. Invalidated resume points, errors and still-open cross-read transactions are excluded explicitly. | +| ORC-005 | Real begin/write/truncate/commit, subset acquisition and coordinator-message handling run. Exact loaded baseline, pre-release observations and completion traces witness the path. All modeled rows, metadata and cursor values reach comparisons. | +| ORC-006 | The original guard fails 45 queued histories while serial controls pass. Public-truncate and public-delete mutants fail 60 and ten histories respectively at assertions. | +| ORC-007 | Not triggered: the extension is a bounded enumeration, not a random generated property. Existing generated campaigns remain intact. | +| ORC-008 | No mutable reference state is introduced. The reference directly computes the final snapshot from input source order. | +| ORC-009 | Shared concepts use glossary terms. Source-step labels identify input sync transactions, while the mixed wait list distinguishes applied receipts from subset-load settlement. | +| ORC-010 | Observations precede cleanup. The held persistence gate releases in finally, retained rejections are observed, and the existing bounded cleanup helper preserves primary and secondary failures. | +| ORC-011 | Serial controls supply a second scheduling formulation for the same source history. Original production agrees with the reference on all 45 controls, while queued cases expose the guard error. H05's separate quiescent compatibility evidence remains intact. | +| ORC-012 | Both review inventories reconcile above. Exact head/blob receipts, all applicable requirement outcomes, limitations and coverage-map destinations are retained. | +| ORC-013 | Before/after truncate distinguishes ordered replacement from unconditional source preservation. Queued/serial controls distinguish acceptance from publication timing. Eager/on-demand no-truncate deletes distinguish already-public and absent-key admission. | +| ORC-014 | The claim is limited to controlled compatible notifications and recording adapters. The coverage map names live Electric/browser OPFS, invalidated resume points, schema changes, and intermediate publication as separate receiving evidence. | + +No broad closure claim follows. Cancellation, cleanup, read failure, arbitrary +buffered replay/reset interleavings, and the semantic basis of the still-open +transaction restriction retain their named persistence-history owner. + +## 2026-10-05: integration with the current sync API + +PR head `4c9962e4ec30e2579dfbbd4bc7ad4e61d10c408f` passed the +standalone-branch checks recorded above. Those results did not establish that +it passed with current main. CI job `112035444776` in run `37388315568` +checked out merge `7ef5161` against main `49abb32ff`. Main commit +`eac6e8b6a` (#2030) had removed `begin({ immediate })`. Three calls in the +new oracle still used that API. Four histories also expected the reservation +that the removed option no longer supplied. CI correctly rejected both the +calls and those ordering observations. + +This follow-up merges main `482196ec4c4369ec540b510ced926bb7d82d7231` +without rewriting published history. DB-IVM and DB were rebuilt from the merged +sources before testing. The unmodified integration reproduced all four runtime +failures and all three type-error locations. The earlier local success was a +base-integration gap, not evidence of a flaky test or stale dependency output. + +The existing adapter capability `metadata.persistence.reserveCommitTurn()` +replaces the removed option in these fixtures. Its documented contract reserves +an open sync transaction's turn ahead of later subset reads; Electric uses it +for overlapping stream transactions. All 24 original and eight adjacent +histories remain. Their rows, metadata, receipts and exact ordering comparisons +are unchanged. The two admission orders are now named unreserved/reserved +commit turns. The 90 precedence histories and their reference algebra are +unchanged. This repair adds no production behavior beyond main's integration. + +### Revision and execution receipt + +- Integrated production blob: `314a0ea3ea7a858a7f69bfd33dadda8566100063`. +- Final oracle blob: `68ae72caf3713875b0ba466a9698434eb62cea5b`. +- Calibration oracle blob: `b9459c822b3f47c314dd3b42ebf2dcfa41726873`. + The only subsequent oracle edit wraps one grammar comment; executable code + and assertions are identical. The full suite uses the final blob. +- Both dependency builds use main `482196ec4`; this PR does not modify those + sources. Cached third-party tooling has the same Rollup limitation recorded + above. The merge imports main's manifests and lockfile without local edits. + +| Check on the integrated candidate | Result | +| --- | --- | +| Unmodified integration | Same four ordering failures and three type-error locations as CI. | +| Updated fixture | All 122 histories and their three typechecked definitions pass. | +| Remove sequence retirement | 73 histories fail: the original 24, four unreserved begin-during-read histories, and 45 queued precedence histories. The other 49 runtime histories pass. | +| Restore the reviewed flag-based guard | All four unreserved begin-during-read histories fail; the four reserved controls pass. | +| Omit public truncate | 60 precedence histories fail, 30 pass. | +| Omit public delete | Ten no-truncate delete histories fail, 80 pass. | +| Omit commit-turn reservation from the updated fixture | Four reserved histories fail the exact read-count observation; four unreserved controls pass. | +| Final full package suite | 23 files, 836 passing checks, two existing TODOs, no type errors. | +| Standalone TypeScript | Exit 0. | +| Final package lint | No errors; 25 existing require-await warnings. | + +All hostile controls failed at assertions, with no type, timeout, or setup +failure. Every temporary mutation was restored. The only additional correction +sorts two imported names in `sqlite-boolean-arity-oracle.test.ts`; that lint +error was already present on main and does not alter the oracle's behavior. + +### Audit delta and loss reconciliation + +ORC-001 and ORC-009 now cite the documented commit-turn capability for the +fixture's ordering control. ORC-002's independent expected values do not change. +ORC-003 and ORC-004 retain all histories and describe both supported orders. +ORC-005, ORC-006 and ORC-013 have fresh path and hostile-control evidence above, +including a direct omission control for the replacement API. ORC-007 remains +inapplicable to the bounded enumeration; ORC-008 adds no model state. +ORC-010's cleanup and ORC-011's serial controls remain intact. This appended +revision receipt satisfies ORC-012 for the integration repair. ORC-014 retains +the controlled-adapter limit; calling the same capability as Electric does not +prove live Electric or host delivery. The coverage item remains open. + +CI01, the user-reported integration failure, is confirmed and `fixed-now`. +CI02, the inherited import-order lint error, is confirmed and `fixed-now`. +The ten original review items retain seven fixed-now, one duplicate, one refuted, +and one confirmed-open evidence gap (H10). The four additional review items +retain two fixed-now and two deferred to their named owners. Current-head +CodeRabbit review `87aabed8-7ed1-4853-a6c0-d83e12999aeb` has no new code +findings; its repeated docstring metric remains duplicate C04. No review item +or tested history was discarded to make this integration pass. diff --git a/docs/contributing/oracle-reviews/issue-2036-queued-hydration-publication.md b/docs/contributing/oracle-reviews/issue-2036-queued-hydration-publication.md new file mode 100644 index 0000000000..5a5ddd1d6f --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2036-queued-hydration-publication.md @@ -0,0 +1,145 @@ +# Accepted source publication after queued hydration + +This is the original 24-case audit. The [admission follow-up](issue-2036-admission-follow-up.md) +records a subsequently reproduced timing gap and withdraws the merge-ready assessment. + +## Reviewed boundary + +Issue #2036 reports a source commit accepted before subset hydration starts, +then rejected when publication follows that hydration. PR #2037 limits the +publication-time sequence check to hydration-owned replay. Commit admission +and writes during hydration keep their existing sequence checks. + +The independent law is the existing RFC #1659 durability obligation: an +accepted source commit remains an obligation across persistence scheduling. +The covered history has successful hydration, no cancellation, and one sync +run. Observation occurs after all source and subset receipts settle, before +cleanup. This record does not claim every interleaving or live-provider reach. + +## Revisions and preservation + +- Original main: `e21c280f3c4e56f6c0d05ec0eed87db29c623489`. +- Original PR: `d18a7c72557e8e8067cc494e84c11ad11ab0baca`. +- Integration main: `cfb03f201bb21d82b7f537a9fbe59d3623f395d6`. +- Merge before the oracle extension: `17a56e64bcabc24dd4ec74597530f1adf644fdbf`. +- Reviewed code and oracle head: `c9a2c631c073bae95c08dc306d642a5687c471ad`. +- Tested production blob: `50920367aa97667de71ca319c46e810d35a0d1a6`. +- Tested oracle blob: `4a5665e0955015ed24730f3d98047a41ff6cec07`. + +The integration preserves the author's commit and merges current main normally. +It retains main's reversion of the separate immediate-publication cycle repair. +The new immediate cases hold a predecessor at durability, after its publication. +They do not enter that unsupported publication cycle. + +## Loss audit of the original two witnesses + +The expanded grammar contains both original fixtures at hydration count two, +ordinary admission, and a dependent successor. It crosses insert/update with +one/two/three hydrations, ordinary/immediate admission, and absent/present +successor. These 24 histories are a complete finite enumeration. + +| Original observation | Receiving assertion | +| --- | --- | +| Hydration does not start before source commit | Zero adapter subset calls before source begin/commit | +| Hydrated baseline precedes source publication | Public title recorded at every adapter subset call | +| All source and subset receipts fulfill | Exact fulfilled outcomes after `Promise.allSettled` | +| Collection remains ready | Exact Collection status | +| Final source row and unseen baseline field survive | Independent complete-row expectation for public and durable rows | +| Fresh insert resets metadata, update preserves it | Exact row metadata per operation | +| Successor reads pending metadata and wins | Staged cursor read and final durable cursor | +| Source transactions persist in FIFO order | Durable mutation keys plus source-title order | +| Cleanup preserves the primary failure | Existing bounded cleanup helper and gate release | + +`foldDurabilityLedger` folds complete source obligations in commit order. It +has no production generation, queue, or classification helper. Expected source +rows include the preceding baseline's untouched detail. The driver sends only +the source title patch, so production must preserve that detail itself. + +No existing main assertion or test expectation changes. The extension replaces +the PR's two fixtures with their finite generalization, without a failure gate. + +## Evidence + +On original main, both unmodified PR fixtures reject with the reported +hydration-cycle error through `applyTransactionToCollection` and +`applyBufferedSyncTransactionUnsafe`. The expanded 24 histories fail at the +receipt/status/public-row/durable-row assertion. + +The same 24 histories fail against integration main without the publication +guard. With the guard, all 39 selected new and existing checks pass without +type errors. The following wrong designs fail at their intended assertions: + +| Wrong design | Distinguishing witness | Outcome | +| --- | --- | --- | +| Reject any publication after a sequence advance | Every accepted-before-hydration matrix cell | 24 assertion failures | +| Remove commit-time sequence validation | Existing begin-before-hydration, commit-after-hydration history | Missing-throw assertion failure | +| Preserve stale metadata for ordinary inserts | Every insert matrix cell | 12 metadata assertion failures; update cells pass | + +These are assertion failures, not setup failures or timeouts. All temporary +production variants were removed. Historical full-suite evidence on original +main plus the guard was 739 passing tests. That count does not certify the +later integration revision. + +On the integration revision with the expanded oracle, the full package suite +passed: 23 files, 737 tests, two existing TODOs, and no type errors. Standalone +`tsc --noEmit` passed. ESLint reported zero errors and 22 existing +`require-await` warnings outside the new cases. After the review moved each +subset unload into the existing bounded cleanup helper, the final focused run +again passed all 39 selected checks with no type errors. The added block was formatted, and `git diff --check` passed. The cached +formatter also proposed unrelated changes to existing type unions; those +changes were excluded. + +Independent correctness and simplification reviews found no production or +oracle-correctness defect and no worthwhile simplification. Both identified the +same audit mismatch: subset unloads were outside the bounded helper. The final +fixture puts each unload through that helper so one cleanup failure cannot +skip later actions or mask the primary failure. No failing product history was +inferred from this harness correction. + +The commands use the checked-in package configuration. A local run uses: + +```sh +cd packages/db-sqlite-persistence-core +PATH="$PWD/../../node_modules/.bin:$PATH" ../../node_modules/.bin/vitest run \ + tests/persisted-oracle.test.ts \ + -t 'publishes committed source|rejects a source transaction|preserves.*metadata|rejects a transaction begun before hydration' \ + --coverage.enabled=false --maxWorkers=2 --reporter=dot +``` + +Node 24.19.0, Vitest 3.2.4 and TypeScript 5.9.3 ran with jsdom. Registry failures +prevented a fresh lockfile install, so the run reused cached third-party tools. +DB and DB-IVM were built from this worktree, and runtime workspace imports +resolve to those builds. No runtime source alias or SQLite-host shim was added. +This is not an exact-lockfile toolchain receipt. + +## Oracle-guide audit + +| Requirement | Outcome and evidence | +| --- | --- | +| ORC-001 | Pass. Existing durability law, controlled history and observation boundary stated above and in the executable owner. | +| ORC-002 | Pass. Independent ledger and baseline expectations do not import production semantic machinery. | +| ORC-003 | Pass. Opening contract, local model/grammar prose, production driver and settlement comparison remain visible in the oracle file. | +| ORC-004 | Pass. Original cells reconstruct exactly. Each axis contributes metadata behavior, count boundaries, admission mode or successor dependence. Domains are explicit. Open-crossing histories remain rejection controls. | +| ORC-005 | Pass. Zero pre-commit loads and per-hydration titles witness reach. The recorder distinguishes receipt rejection, wrong status, row loss, stale metadata, omission and durable reordering. | +| ORC-006 | Pass. The three wrong designs above reach their intended assertion checkpoints. | +| ORC-007 | Not applicable. This change adds a finite enumeration, not an important randomized property. Existing random campaigns remain unchanged. | +| ORC-008 | Not applicable. The existing reference model gains no state fields or transitions. | +| ORC-009 | Pass. Shared concepts retain glossary terms. The ledger abstracts complete source obligations and does not model hydration state. | +| ORC-010 | Pass. Results are captured before cleanup. The existing helper bounds every cleanup action and preserves primary and secondary diagnostics. | +| ORC-011 | Not triggered. No shared semantic fault needing another formulation was identified. Provider ordering is a separate receiving-witness gap. | +| ORC-012 | Pass for this bounded audit. This versioned record identifies every requirement, exact revisions and tested blobs. No universal bug-class closure is claimed. | +| ORC-013 | Pass for the declared finite domain. Adjacent counts, operation/admission/successor cases and existing open-crossing controls distinguish plausible wrong consequences. | +| ORC-014 | Limited to the controlled receiving boundary. Live Electric/OPFS handoff remains open with a named owner below. | + +## Remaining witnesses + +The coverage map assigns cancellation, cleanup/restart and hydration-failure +compositions to `packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts`. +Those paths need this accepted-before-hydration schedule, not merely their +existing separate histories. + +The exact real-provider schedule belongs in +`packages/browser-db-sqlite-persistence/e2e/electric-hydration-straddle.opfs.spec.ts`. +Its existing during-hydration history does not establish this handoff. The +controlled fixture does not prove live Electric delivery, browser scheduling, +OPFS behavior, or application-level downstream error propagation. diff --git a/docs/contributing/oracle-reviews/issue-2046-hydration-commit-deadlock.md b/docs/contributing/oracle-reviews/issue-2046-hydration-commit-deadlock.md new file mode 100644 index 0000000000..2e3e955cac --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2046-hydration-commit-deadlock.md @@ -0,0 +1,231 @@ +# Issue 2046: source commits inside shared hydration scopes + +Current status: [PR #2049](https://github.com/TanStack/db/pull/2049) contains the +committed production repair and strengthened oracles. See the +[latest receipt](issue-2046-hydration-loss-audit.md#high-effort-review-receipt) +for current results and content hashes. The historical RED-stage ledger below +is retained for provenance; its open verdicts and unchanged-production statements +are not the PR's current status. + +The initial entry below records the frozen 24-core/12-browser candidate. The +[follow-up loss audit](issue-2046-hydration-loss-audit.md) supersedes its oracle +enforcement claims and results. In particular, the original aggregate receipt +check and browser cleanup capture were weaker than described here; the follow-up +preserves those losses and strengthens the executable checks. + +## Historical scope and verdict: initial RED stage + +[Issue 2046](https://github.com/TanStack/db/issues/2046) reports one **P1 +persistence deadlock**, expressed through nine claims below. The report is an +issue, not a pull request. Checked source: +`65992aacda530fde89f4c8ffa803b60cd708c425` (`origin/main`, fetched 2026-10-06). +The worktree branch is `codex/issue-2046-red-oracles`. + +The defect on the initial main baseline was confirmed through both the controlled persisted +history owner and real Chromium OPFS. The requested deliverable at that stage was red +reproductions. Production was unchanged; a temporary repair was calibrated and +then restored byte-for-byte. No issue comment, PR, push, or release had been +made at that stage. + +## Reviewer assessment + +The reporter accurately identified the default coordinator, the lost scoped +adapter, both hydration entry points, the effect on a peer Collection, and two +working controls. The report has high technical depth and signal, appropriate +urgency, and a narrow implied repair: honor the supplied scoped adapter while +retaining the registered adapter for ordinary commits. The calibration supports +that repair within the tested bounds. + +The release cutoff needs qualification. The repository changelogs list #1868 +under core 0.3.0/browser 0.2.24, earlier than the report's core 0.4.0/browser +0.2.25 cutoff. This evaluation did not execute historical published packages, +so it does not establish the earliest affected release or pre-0.4.0 success. + +## Finding ledger + +The task-local append-only ledger was created from the complete issue body +before test edits. There were no issue comments. IDs preserve source order; +related observations are not treated as additional independent product defects. +All nine remain `confirmed-open` against unchanged production. For verified +control claims, that disposition refers to the associated open defect; those +controls need no behavior change. + +| ID | Original claim / suggested remedy | Evidence | Technical verdict | PR action | Durable value / destination | +|---|---|---|---|---|---| +| R1 | A buffered startup commit hangs without rejection; source remains loading. Expected receipt and readiness progress. | Four core scoped startup histories fail at public-adapter reentry. Two real OPFS startup cases record pending receipt, source loading, peer loading, no receipt error. | Confirmed, P1. Illustrative ~100 ms timing is not promoted into a latency contract. | Leave red; repair remains open. | Primary routing matrix and OPFS receiver. | +| R2 | On-demand `subscribeChanges(..., { includeInitialState: true })` hydration also deadlocks; source stays ready. | Four core scoped subscription cases and two OPFS subscription cases reach the same cycle. Source ready, peer loading, receipt pending. | Confirmed, P1. Subscription status itself is already ready in the observed trace; it is not a settlement witness. | Leave red. | Subscription history axis and explicit hydration completion gate. | +| R3 | Other Collections sharing persistence cannot become ready. | Real peer b starts during a's held hydration. Its status is loading at all four cycle checkpoints; it reaches ready in every repaired history and every workaround control. | Confirmed, P1, for a peer queued behind the cycle. Does not imply already-ready peers become loading. | Leave red. | Public peer-status check in OPFS receiver. | +| R4 | SingleProcessCoordinator drops scopedAdapter and uses its scheduled adapter; BroadcastCollectionCoordinator forwards it. Implied fix: retain the scope. | Coordinator source and caller contract; causal reentry assertions; temporary scopedAdapter fallback repair makes all histories pass. | Confirmed, P1; proposed repair is narrow and idiomatic. | Keep production unchanged; repair open. | Routing assertion, calibration patch described below. | +| R5 | Shared scheduling + default coordinator + buffered hydration commit are required for the reported cycle. | Controlled unscheduled adapter cases pass. Real BrowserCollectionCoordinator cases pass. Moving the commit to onFirstReady passes. | Confirmed for this bounded cycle. Custom broken coordinators and other deadlocks are not excluded. | Retain all ablations. | All three grammar axes remain executable. | +| R6 | Commit from onFirstReady is a workaround. | Four real OPFS after-ready histories pass, crossing both coordinators and both baselines. | Confirmed control. | No behavior change. | Executable after-ready control. | +| R7 | Passing BrowserCollectionCoordinator is a workaround. | Six real OPFS cases pass, including both hydration phases and both baselines. | Confirmed control on one Chromium tab. | No behavior change. | Real coordinator receiving witness. | +| R8 | Introduced by 98639f03/#1868; first affected core/browser/DB versions are 0.4.0/0.2.25/0.11.0, earlier versions work, current main still broken. | #1868 added shared scheduling and the scoped coordinator argument without updating SingleProcessCoordinator. Main is executable RED. Changelog places #1868 under core 0.3.0/browser 0.2.24. | Current defect and causal change supported. Earliest-release and earlier-success assertions remain unverified and conflict with the changelog placement. | Do not claim a validated release cutoff. | Preserve historical evidence gap here; a release-bisect witness is needed before publishing a cutoff. | +| R9 | Empty database suffices; existing data does not remove the bug. | Both empty and one-row baselines reproduce in core and actual OPFS. Each browser case opens an isolated database. | Confirmed within the named baselines. | Retain both cases. | Baseline axis in both owners. | + +## Law coverage and enforcement + +**Authority.** `PersistenceAdapter.runInHydrationScope` requires nested work to +use the supplied unscheduled adapter and forbids retaining it past the callback. +`PersistedCollectionCoordinator.requestApplyCommittedTx` explicitly accepts +that adapter. The persisted-history contract promises durable or observably +failed source work. The applied-receipt and Collection-readiness contracts make +unreported self-deadlock unacceptable. No new recovery or product policy is +introduced. + +**Original gap.** The primary `persisted-oracle.test.ts` recording adapter had +no shared hydration scope by default. It could flush buffered commits without +exposing coordinator reentry. `shared-logical-scheduling-oracle.test.ts` +exercised core-adapter scopes directly, without a wrapped source commit through +the default coordinator. The browser fairness grammar scheduled independent +persists and cold hydrations, not a persist awaited inside its owning hydrate. +Each separate test could be green while this composition was broken. + +**Primary owner.** The appended `hydration source durability routing` matrix in +`packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts` adds 24 +bounded histories: phase {startup, subscription, after-ready} × adapter +{scoped, unscheduled} × baseline {0,1} × source transaction count {1,2}. +Both source transactions insert distinct keys. The independent snapshot rule +is baseline plus source inserts; the independent routing rule forbids +reacquisition of an already-owned hydration scope. The model does not simulate +the production scheduler. + +At the held-read cut, receipts remain pending and source inserts are absent +from public rows. At the durability-admission cut, an event records any +public-adapter call under the owning scope. That event competes against +successful completion; the original fails an assertion, not a test timeout. +On success, the oracle compares public rows, recording-adapter durable rows, +ordered mutation-key batches, receipt completion, and Collection readiness. +The fixture releases its diagnostic reentry gate in `finally`, allowing all +work and Collection cleanup to settle without replacing the original failure. + +**Real-provider receiver.** +`packages/browser-db-sqlite-persistence/e2e/hydration-commit-oracle.opfs.spec.ts` +and its directly named page driver run 12 histories: the same three phases × +{default, BrowserCollectionCoordinator} × {empty, one stored row}. The driver +uses `createBrowserWASQLitePersistence`, its real shared scheduler, actual +`BrowserWASQLiteDriver`, and Chromium OPFSCoopSyncVFS workers. Startup commits +inside `sync()`, matching the report. Subscription commits occur after a real +row SELECT completes but before its result returns to hydration. All wrapped +adapter calls forward to production unchanged. Source/peer readiness and +receipt settlement are observed; successful histories also read durable rows. +The subscription history awaits completion of the actual hydration scope, +since a `ready` subscription status alone does not establish load settlement. + +The original reaches a real scheduled `applyCommittedTx` while hydration awaits +that call. The observation is frozen at that cycle, then the worker is closed. +Playwright closes each isolated page/context even on assertion failure. Success +also checks Collection cleanup and provider-close diagnostics. This is a +causal finite cycle witness, not a claim of proving unbounded eventuality by +waiting a chosen number of milliseconds. + +**Encoding/enforcement answers.** The stated finite grammar is fully reachable +and its routing, held-read, and successful-result obligations are asserted at +the cuts above. Original main is the hostile subject: eight core routing +assertions and four real OPFS cycle assertions reject it. The temporary narrow +repair passes those same histories. No expected-failure marker, waiver, +classifier, timeout acceptance, or skipped failing case was introduced. +Successful durable-row checks prevent a coordinator from satisfying the law +merely by returning without storing the insert. + +**Limits/open cells.** This reproduces one scheduler/coordinator bug class; it +does not close production repair. The bounded grammar does not establish +update/delete/truncate, metadata-only commits, failures, abort/cleanup races, +multiple scoped peers, distinct schema adapters, restarts, arbitrary payloads, +or other browsers/hosts. The persisted-history owner retains those source and +lifecycle dimensions as future witnesses; the OPFS owner retains the receiving +host cases. Exact release attribution needs historical package execution. +These limits are recorded in the coverage map, rather than erased by a green +calibration. No general claim of class closure is made. + +## Calibration and exact verification + +Temporary change, **not retained**: + +```diff + public async requestApplyCommittedTx( + collectionId: string, + tx: PersistedTx, ++ scopedAdapter?: HydrationPersistenceAdapter, + ): Promise { +- const adapter = this.collectionAdapters.get(collectionId) ++ const adapter = scopedAdapter ?? this.collectionAdapters.get(collectionId) +``` + +| Run | Result | Interpretation | +|---|---|---| +| Complete persisted-history suite, original production + new matrix | 8 failed, 395 passed, 1 existing todo | Only the eight scoped hydration histories are red. | +| Real OPFS matrix, original production | 4 failed, 8 passed | Both hydration phases fail with default coordinator, with either baseline. Both workarounds pass. | +| Complete persisted-history suite, temporary repair | 403 passed, 1 existing todo | Same law, histories, and assertions. | +| Real OPFS matrix, temporary repair | 12 passed | Same actual provider/coordinator paths. | +| Restored production | Same 8 core and 4 OPFS failures | Final deliverable intentionally remains red. | +| Core package TypeScript + focused browser oracle TypeScript | Passed | Full browser package check requires unrelated Electric/pg dependencies unavailable in the partial dependency setup. | +| ESLint, changed TypeScript/config files | No errors | 22 pre-existing require-await warnings in the large primary oracle; none in the appended matrix. | +| Existing real OPFS cold-hydration control | Passed | Provider setup was independently checked. | + +Runs used Node 24.19.0, Vitest 3.2.4, Vite 7.3.6, TypeScript 5.9.3, +Playwright 1.60.0, Chrome 154.0.8037.98, and wa-sqlite 1.5.0. Full frozen +installation failed on the configured registry and the public-registry +fallback. Cached tools were reused; source aliases target this worktree's +`db`, `db-ivm`, and persistence-core source, not another checkout's builds. +The lockfile remains unchanged. + +Historical session-only commands, from the worktree root, are retained below +as execution provenance. They depend on untracked cache/temporary files and +are **not runnable from a fresh checkout**. Use the +[fresh-checkout instructions](issue-2046-hydration-loss-audit.md#reproducing-the-retained-checks-from-a-fresh-checkout) +for the current repository configurations. + +```sh +node node_modules/vitest/vitest.mjs run --config node_modules/.cache/issue-2046/vitest.config.ts --reporter=dot +node node_modules/typescript/bin/tsc --noEmit -p packages/db-sqlite-persistence-core/tsconfig.json +node node_modules/typescript/bin/tsc --noEmit -p /private/tmp/issue-2046-audit/browser-types.json +``` + +From `packages/browser-db-sqlite-persistence`: + +```sh +PATH="$PWD/../../node_modules/.bin:$PATH" node node_modules/@playwright/test/cli.js test --config playwright.opfs.config.ts hydration-commit-oracle.opfs.spec.ts --reporter=line --timeout=15000 +``` + +The ordinary package test glob includes the appended core matrix; the existing +Playwright OPFS configuration now registers the browser spec. The local source +alias runner and append-only working ledger remain ignored/task-local. Raw +source and logs were stored in the session-only, untracked directory +`/private/tmp/issue-2046-audit/`. That path is not a repository prerequisite or +a durable source for reproducing these checks. + +## Oracle guide audit + +| Requirement | Outcome | +|---|---| +| ORC-001 | Scope contract, coordinator API, and receipt/readiness authorities named; finite limits stated. | +| ORC-002 | Independent input-derived rows and no-reentry rule; no production scheduler model copied. | +| ORC-003 | Prose beside both executable owners identifies contract, relation, grammar, driver, and cuts. | +| ORC-004 | Bounded enumeration, not a random generated property. Reconstruction reaches both reported traces. Ablating scope, coordinator choice, or timing loses the relevant distinguishing cells. Baseline 0/1 and commit count 1/2 are explicit bounds. Open uncommitted transactions and storage failures are excluded. | +| ORC-005 | Real wrapper/default coordinator exercised in core; actual Chromium OPFS receiver supplies shared scheduling. Exact public rows, readiness, and receipt facts checked at named cuts. | +| ORC-006 | Original production rejected at the routing/cycle assertion; temporary repair passes. Setup failures during fixture development were not credited as RED evidence. | +| ORC-007 | Not applicable: finite matrices, no random generated property or shrinking. Test names directly select each history. | +| ORC-008 | No new stateful reference machine. Gates and scope flags are driver instrumentation, not semantic expected-state transitions. | +| ORC-009 | Glossary terms retained. Scoped/unscheduled describes the supplied adapter boundary; no new product lifecycle state. | +| ORC-010 | Core finally releases controlled gates and uses existing cleanup diagnostics. Browser freezes observations, closes worker/database, and relies on isolated page/context disposal on red assertions. No shrinker can replace the failure. | +| ORC-011 | Controlled single-process scope premise could hide production scheduling behavior. The real browser scheduler/SQLite receiver supplies that premise independently; browser coordinator and post-ready controls distinguish it. | +| ORC-012 | This record is tied to the exact source head and records all requirements. No production bug-class closure claim. | +| ORC-013 | Scoped versus unscheduled, during versus after hydration, and default versus browser coordinator distinguish the conditional routing law. | +| ORC-014 | Real Chromium OPFS startup/subscription cases receive the controlled core premise, including real row reads and the default shared scheduler. Other hosts remain outside the claim. | + +## Loss audit and remaining work + +Returned to the complete raw body, including its cause chain, condition list, +example, table, release assertions, database-reset note, and workarounds. Nine +items remain accounted for: **9 confirmed-open; 0 fixed-now; 0 stale; 0 refuted; +0 accepted-design; 0 deferred; 0 design-decision; 0 duplicate**. There is one +underlying P1 defect, not nine separate bugs. No permission to defer an +in-scope repair was inferred: this request explicitly asks for red reproduction. + +The evaluation/reproduction work is complete on unchanged main. Production +repair is intentionally open. There is no blocking product-policy choice. +The historical release cutoff is an evidence gap, not a refutation or a +validated pre-0.4.0 compatibility claim. Worktree changes add tests, runner +registration, and contract evidence only; production line delta is zero. + +Weight: production +0/-0; tests, fixtures, and runner registration +522/-0; contract/review documentation +241/-0. diff --git a/docs/contributing/oracle-reviews/issue-2046-hydration-loss-audit.md b/docs/contributing/oracle-reviews/issue-2046-hydration-loss-audit.md new file mode 100644 index 0000000000..63ed87ac90 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2046-hydration-loss-audit.md @@ -0,0 +1,566 @@ +# Hydration source durability: loss audit and oracle strengthening + +## Current status + +[PR #2049](https://github.com/TanStack/db/pull/2049) contains the approved +call-local adapter repair, committed oracles, and a patch changeset. The +production base advanced to `a37e69ab6aa35fd6a2a72d727de2b5168d10b1a0` before the +repair was committed. The [latest review receipt](#high-effort-review-receipt) +is authoritative for the current executable hashes, 640 passing core checks +(with one existing todo), and 25 passing Chromium OPFS checks. + +This document preserves a chronological audit. Earlier statements about +uncommitted tests, unchanged production, no PR, old baselines, and smaller +counts describe those historical stages only. Each SHA-256 binds its named +stage; it is not an assertion that every historical digest matches HEAD. +The final executable hashes at reviewed commit `90cbda1e5` all matched the +receipt below, including the later cleanup correction to the core test hash. + +## Historical identity and scope: pre-fix loss audit + +At this pre-fix stage on 2026-10-06, the production baseline was +`65992aacda530fde89f4c8ffa803b60cd708c425` (`origin/main`). Worktree branch: +`codex/issue-2046-red-oracles`. The tests were uncommitted; the executable SHA-256 +bindings below identify their reviewed content without pretending that the +production commit contains these tests. Production and the lockfile were unchanged at this stage. The [initial review](issue-2046-hydration-commit-deadlock.md) remains +an historical record of the smaller 24-core/12-browser candidate. + +The user requested Field Lab's **Hidden-signal recovery assay (`loss-audit`)** +and separately authorized improving the oracle. The candidate was frozen before +three fresh, read-only scanners received separate sources with sibling findings +hidden: S1 the oracle guide; S2 persistence contracts; S3 the existing oracle +portfolio. Recovery and implementation judgment are separated below. At this stage, no Field +Log, external message, production fix, commit, push, or PR had been created. + +This improves enforcement of the local composition: source transactions buffered +by persisted hydration must use the current scoped adapter, preserve their +ordered durable obligations, settle each acceptance/receipt at its own boundary, +and release that scope for ordinary/peer work. It does not claim all persistence +laws universally proved or the production bug repaired. + +Frozen candidate SHA-256 (paths are repository relative): + +| File | SHA-256 | +|---|---| +| `packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts` | `412f3ba03af8f1d82da5c6a6f8d3dd15c975261646d988bf9995a4ce30415e55` | +| `packages/browser-db-sqlite-persistence/e2e/hydration-commit.opfs.ts` | `ae78b9254ab52c2fbb9a29dd9042aef8c4502efd0ae43b46e7914bca88328e4f` | +| `packages/browser-db-sqlite-persistence/e2e/hydration-commit-oracle.opfs.spec.ts` | `8cdb922972059c9a0a5d667f03a9d8b1802ff6cd1ec35aa6305f4d437c55c0ce` | +| `docs/contributing/oracle-reviews/issue-2046-hydration-commit-deadlock.md` | `7954be179200f8c80da08488c45bb779fbc5e9dde8d58b8e88159ce27145a4a1` | +| `docs/contributing/oracle-coverage.md` | `e84ea6d6455f9d1c91590a5242754f84ee64a700218f9bd26061c80dcee3ba90` | + +## Source-by-source recovery ledger + +Pointers in this section refer to the frozen candidate or unchanged production +baseline, not moving line numbers after the edits. `P` is the core +`persisted-oracle.test.ts`; `B` is `hydration-commit.opfs.ts`; `R` is the initial +review; `C` is `src/persisted.ts` in persistence-core. `S` is +`shared-logical-scheduling-oracle.test.ts`; `F` is browser +`tests/shared-driver-fairness-oracle.ts`; `PC` is browser +`tests/per-collection-coordinator-oracle.test.ts`. + +These 23 source-specific recoveries preserve overlapping support. They are not +23 distinct bugs or an instruction to restore every possible cross-product. + +| ID | Supported material omitted from the frozen reduction | Support and loss point | Drop mechanism | +|---|---|---|---| +| S1-1 | Each receipt pending, not just an aggregate pending | ORC-005; guide 818–825; P19243–19250; R79–84. A pending preload/sibling masks early fulfillment. A standalone all-fulfilled Promise-shape control also read pending after the candidate's single microtask. | Observation compression and insufficient reaction drain | +| S1-2 | Immutable primary evidence survives all cleanup failures | ORC-010; B220–247 publishes after cleanup and retains a live errors array. Collection cleanup/dispose can erase the capture or skip database close. | Cleanup treated only as release; late/shallow capture | +| S1-3 | Review binds the actual executable revision | ORC-012; R6–8 identifies production HEAD, not new test content. | Baseline identity substituted for oracle identity | +| S1-4 | Existing direct replay stays direct | ORC-007; P121–126 guards existing suites, but P19121 uses unconditional describe. New red cases can contaminate another property's replay. | Local finite-test classification hid package integration | +| S1-5 | Expected records remain independent of mutable driver inputs | ORC-002; P197,19240,19272 reuse baseline/source objects to compute expectations. | Formula independence mistaken for record independence | +| S1-6 | Supplied adapter expires with its callback | ORC-001/005/013; R53–56 cites lifetime, but P19178–19197 uses a permanently callable bound store. | Two-sided scope law reduced to no public reentry | +| S2-1 | Acceptance/durability differ from visibility under optimistic work | C3943–3954,1412–1426,2744–2754; P19243–19250 aggregates receipts; existing causal replay P18613–18745 uses the same adapter for both routes. | Settlement observations collapsed | +| S2-2 | Correct current loan and per-Collection adapter identity | C438–445,880–884,978–999; P19178–19188 reuses one scoped object/store. | Identity/lifetime projection | +| S2-3 | Ordinary work remains scheduled, including during another Collection's hydrate | C2828–2834,446–449; B193–202 only starts an empty peer. Temporal overlap alone is not nested dependency. | Dependency relation replaced by scope-active flag | +| S2-4 | Metadata/truncate require durability; only a truly empty transaction does not | C2817–2823,2890–2939,4477–4486; P19144–19146 and19272–19281 only inspect distinct inserts/keys. | Zero row mutations equated with no durable obligation | +| S2-5 | Durability error envelope, prefix/suffix fencing, no implicit rollback | C898–901,2713–2719,2792–2805; errors.ts52–90; all scoped stores succeed. | Failure category compressed | +| S2-6 | Failed incremental reads may preserve independent source work; baseline failure is terminal | C2397–2423,2531–2533; P14283–14387 uses an unscheduled adapter. | Request-local and terminal failures merged | +| S2-7 | Abort timing and successor dependency determine the result | P90–96,3863–4047; C2769–2805; no signals/dependencies in the new matrix. | Abort/cleanup grouped into one future category | +| S2-8 | Cleanup invalidates queued work; old continuations cannot alter a replacement run | C2203–2288,2732–2733,2755,2852,4668–4726; P19286–19296 only cleans settled work. | History truncated before replacement | +| S3-1 | Independent receipt observations at held reads already exist | P8594–8666,806–856 versus19243–19253. | Existing observation granularity lost in composition | +| S3-2 | Same-key source precedence, deletion, truncate and FIFO | P67–77,11804–11811,12005–12014,12169–12430; disjoint baseline/source keys cannot witness stale overwrite or resurrection. | Input simplification/explicit operation exclusion | +| S3-3 | Metadata-only versus empty work, captured row-metadata ownership, sequence evidence | P2683–2716,3769,11629–11786; PC3766–3952. Candidate observes only rows and keys. | Result projection drops metadata and sequence | +| S3-4 | Startup/replay/durability failures differ from request-local failures; rejected hydrate releases peers | P750–856,9089–9160,9587–9706,4172–4246,4701–4859,14283–14482; S343–374. | Explicit exclusion compressed into generic failure | +| S3-5 | Independent/dependent and pre-/post-application aborts differ | P3705–3763,9192–9405,3863–3939,11097–11219,4880,5023. | Signal timing and dependency erased | +| S3-6 | Handler may await acceptance while its own optimistic publication is pending | P9995–10079,18614–18740; P18639 supplies no distinct scoped route. | Causal distinction hidden by “commit settles” | +| S3-7 | Scope non-interleaving, ordinary route after exit, K=1/FIFO fairness | S4–18,263–299,343–374; F4–18 and fairness tests256–295,574–619. B193–229 observes eventual peer readiness only. | Identity/order laws reduced to completion | +| S3-8 | Adapter also belongs to correct Collection/schema/elected owner | PC123–135,2437–2506,3645–3952; B158–200 uses same schema and only A writes. | Empty peer hides contamination and owner identity | +| S3-9 | Replacement fencing is different from successful harness teardown | P7206,7330,17739–17810,5177,5293; candidate never replaces a pending run. | Lifecycle distinctions collapsed | + +All scanners cautioned against turning recovery into an unrestricted Cartesian +product of the persistence portfolio. The guide does not require random +campaigns for this finite matrix or a second formulation without a named +shared-fault hypothesis. Existing open-transaction/hydration restrictions are +characterized limitations, not newly invented product laws. + +## Separate evaluation and implemented restoration + +The user separately requested improvements. The implementation restores the +following distinctions while reusing existing primary owners: + +- **Individual obligations:** Each receipt and each `whenSyncAccepted` promise + is observed independently at held-read and held-durability cuts. A task + boundary drains reactions while the gate remains causally held. A permanent + wrong-answer test proves a fulfilled receipt cannot hide behind its sibling. +- **Independent values and ordering:** An immutable input edit log recomputes + the last row/metadata edit. Inputs are cloned before the driver sees them. + Exact transaction data, FIFO turns, sequence/row-version numbers, durable + prefixes and final public/durable state are checked. `put` means a full + source update/upsert in this core driver; the OPFS driver separately performs + literal inserts, updates and deletes. +- **Scope lifetime:** Each controlled callback lends a fresh adapter identity. + Nested calls, ordinary same-run tail commits, a second on-demand hydration, + and reopen distinguish the current loan from public reentry and cached loans. +- **Transaction shapes:** Twelve history templates cover one/two distinct + upserts, same-key replacement/delete, metadata-plus-truncate/replacement, + row/collection metadata set/delete, metadata-only, truncate-only, empty, + atomic multi-row, independent pre-abort, and first/second durable failure. + Each runs for three phases, two scope modes and two baseline sizes: **144** + histories. A populated baseline shares the source key, making precedence + observable. Empty work must not allocate a durable sequence. +- **Failure classes:** Held first/second storage failures preserve exact cause, + code/path, shared terminal error, durable prefix, suffix rejection and later + same-run rejection. Publication rollback is not invented. Existing startup + read-failure and incremental request-local failure witnesses now lend a + distinct scoped adapter. The latter retains its successful overlapping retry. +- **Acceptance before visibility:** The existing causal replay witness now + lends a distinct adapter, holds the optimistic handler after acceptance, + checks the durable row and still-pending/absent public result, then releases + publication. A repair cannot wait on the handler that awaits acceptance. +- **Cleanup as an action:** Four additional histories cross startup/subscription + with scoped/unscheduled adapters, clean up while a source receipt is queued, + reject that receipt, release the old read, restart and reject late old-control + publication while fresh source work succeeds. The no-write claim is limited + to work not yet issued to storage. +- **Receiving host:** **24** actual Chromium OPFS histories retain the original + three phases, two coordinators and two baseline cases, adding single-insert + versus rich transaction histories. A=11, B=22 and C=33 are separate schema + adapters on the same database. A's nested work, B's cold hydration and C's + ordinary source write must finish without cross-contamination. No additional + SQL may enter the provider while the owning read is held. Exact metadata, + schemas, rows and reopen are compared. The exact scope containing the held + SELECT must exit; another scope's exit cannot satisfy that obligation. +- **Causal cycle detection:** Browser instrumentation matches the transaction + ID passed with a live scoped adapter to its erroneous public-adapter call. + Merely overlapping ordinary work cannot trigger the cycle observation. +- **Failure fidelity:** Browser observations are deep-copied before teardown. + Every cleanup failure is retained separately, coordinator disposal cannot skip + database close, and red cycles close the worker/context. A permanent browser + calibration injects first and last cleanup failures and checks both diagnostics + plus the intact primary observation. All setup after opening the database is + inside its cleanup guard. +- **Replay/provenance:** The finite matrix and strengthened fixed routing witnesses now use an explicit replay guard. + The versioned content hashes below bind this evidence to the changed files. + +The controlled probe forwards invalid routes so the harness can release its +gates and clean up. It therefore proves routing, not a simulated scheduler. +The OPFS receiver supplies the real non-preemptible scope and self-cycle premise. + +One attempted assertion was rejected during contract review: core truncate can +establish Collection readiness at publication (`collection/state.ts`, truncate +publication path). It cannot establish source durability. The retained test +checks acceptance/receipt pendingness at that cut without inventing a stronger +universal Collection-readiness law. An attempted second subset hydration in an +eager Collection was also an **unreached fixture path**; repeated scope coverage +now uses the legal on-demand path. Neither harness failure is product evidence. + +## Verification and hostile calibration + +The same narrow temporary scoped-adapter forwarding repair from the initial +review was used only to reach the successful comparison cuts. Every calibration +restored the original production file in `finally`. + +| Execution | Result | Classification | +|---|---|---| +| Complete core suite, unchanged production | 48 failed, 480 passed, 1 existing todo | Reached routing assertions: 44 initial nonempty scoped histories, 2 second-scope empty-history continuations, 2 strengthened existing causal/failure witnesses. No timeout/setup failures. | +| OPFS receiver, unchanged production | 8 failed, 17 passed | Reached same-transaction scheduler-cycle assertions in default-coordinator startup/subscription cases. Both workarounds and cleanup calibration pass. | +| Complete core suite, temporary forwarding repair | 528 passed, 1 existing todo | Same histories and comparisons. | +| OPFS receiver, temporary forwarding repair | 25 passed | Same real database, scheduler, peer/schema and cleanup paths. | +| Eight hostile repairs | All rejected by assertions | Counts/checkpoints below; production restored after each campaign. | +| Existing FIFO property's direct replay | 3 passed, 510 skipped, 1 existing todo | Requested replay plus existing global harness checks; no new routing cases execute. | +| Retained-demand replay special case | 361 passed, 151 skipped, 1 existing todo | Its legacy outer guard retains existing fixed tests; the new 149 finite checks and two strengthened routing witnesses are excluded. | +| TypeScript | Core package and focused browser oracle checks passed | Full browser package still needs unrelated Electric/pg dependencies unavailable in this partial install. | +| ESLint | No errors; 22 existing require-await warnings | No new warnings in the added oracle block/browser files. | +| Production/lockfile diff | Empty | No repair or dependency changes retained. | + +Eight additional wrong repairs were applied independently on top of that +calibration repair. All reached assertion failures; none timed out, failed +setup, remained unreached, survived, or was equivalent in the tested domain: + +| Wrong repair | Failing cases / 149 focused checks | Distinguishing checkpoint | +|---|---:|---| +| Resolve source receipt before durability | 88 | Individual receipt/acceptance at held durability | +| Cache the supplied scoped adapter as the registered adapter | 36 | Ordinary post-scope route / expired loan | +| Drop row and collection metadata | 48 | Durable prefix or final metadata | +| Ignore truncate | 24 | Exact admitted transaction truncate flag | +| Drop row mutations | 148 | Exact admitted transaction or fresh restart row | +| Reverse buffered FIFO | 48 | Exact transaction at durability admission | +| Swallow storage failure | 24 | No suffix admission after rejected durable turn | +| Replace classified storage error with generic error | 24 | Error type/code/path/cause and shared identity | + +A standalone Promise-shape probe exposed the frozen aggregate false green. The +retained early-receipt calibration exercises the replacement checker. The final +browser cleanup calibration supplies permanent ORC-010 evidence. Original main +is itself the routing hostile subject in both controlled and actual-host tests. + +The following historical session-only commands used cached tools and untracked +local-source aliases. No other checkout's production build supplied test +behavior. These commands are **not runnable from a fresh checkout** because +the cache configuration and temporary typecheck project were not committed. +Use the [fresh-checkout instructions below](#reproducing-the-retained-checks-from-a-fresh-checkout) +to run the retained checks with repository configurations. + +```sh +node node_modules/vitest/vitest.mjs run --config node_modules/.cache/issue-2046/vitest.config.ts --reporter=dot +node node_modules/vitest/vitest.mjs run --config node_modules/.cache/issue-2046/vitest.config.ts -t 'hydration source durability routing' --reporter=dot +TANSTACK_DB_ORACLE_PROPERTY=sqlite-persistence.source-fifo-order TANSTACK_DB_ORACLE_SEED=2046 TANSTACK_DB_ORACLE_PATH=0 node node_modules/vitest/vitest.mjs run --config node_modules/.cache/issue-2046/vitest.config.ts --reporter=dot +node node_modules/typescript/bin/tsc --noEmit -p packages/db-sqlite-persistence-core/tsconfig.json +node node_modules/typescript/bin/tsc --noEmit -p /private/tmp/issue-2046-audit/browser-types.json +``` + +From the browser package: + +```sh +PATH="$PWD/../../node_modules/.bin:$PATH" node node_modules/@playwright/test/cli.js test --config playwright.opfs.config.ts hydration-commit-oracle.opfs.spec.ts --reporter=line --timeout=15000 +``` + +Temporary logs, frozen files, calibration patch/scripts and mutation outcomes +are under `/private/tmp/issue-2046-loss-audit/`. The assertions, laws, finite +cases and verdict-critical results are retained here and in the executable +files; future readers need not trust the existence of those temporary logs. + +## Guide conformance and remaining boundaries + +| Requirement | Outcome and evidence | +|---|---| +| ORC-001 | Scope API, established persisted-history durable/failure laws, and core acceptance/publication authority are named. Limits below remain explicit. | +| ORC-002 | Immutable input edit-log recomputation; cloned inputs; no production queue, classifier or scheduler computes expected state. Browser literal snapshots give a different formulation. | +| ORC-003 | Opening laws/limits, model rationale, grammar, production driver, cuts and comparisons are adjacent in executable files. Model edits distinguish row and collection metadata across truncate. | +| ORC-004 | Bounded enumeration, not an important random property. Reconstruction retains reported startup/subscription/empty/populated traces and both controls. Each template/axis's contribution is stated below. Nearby invalid open transactions crossing another hydration are excluded; the grammar contains only committed independent transactions. No invalid history is silently treated as a successful transaction. | +| ORC-005 | Read-entry, exact transaction admission, each accepted/applied receipt, exact held-scope exit, durable rows/metadata and reopen are witnessed. Diagnostics include duplicates/order/value omissions. Real OPFS supplies the actual receiving path. | +| ORC-006 | Eight independently run hostile designs fail reached assertions; permanent receipt and cleanup controls; unchanged main fails the routing/cycle comparison. | +| ORC-007 | New bounded matrices do not trigger random-campaign requirements. Existing generated campaigns remain under the full suite. The explicit replay smoke check confirms the new matrix is skipped during another property's direct replay. | +| ORC-008 | Reference recomputes snapshots from an input log, not a new state machine. Driver scope tokens, gates and counters are instrumentation. Separate pending/fulfilled/rejected observations cannot be merged: gate release or failure distinguishes them. | +| ORC-009 | Glossary terms are retained: Collection versus subscription readiness, source acceptance versus applied receipt, cleanup/restart. Model-only `put` maps to source full update/upsert; edit-log filtering abstracts independently pre-aborted transactions. | +| ORC-010 | Gates always release; primary failure survives existing core cleanup diagnostics. Browser deep capture precedes cleanup; each failure is retained and database close is attempted. Permanent two-failure calibration passes. | +| ORC-011 | Named shared fault: insert-only source/baseline concatenation and a fake scope could agree on wrong precedence or scheduling. Real SQLite snapshots, independently written rich-history expectations and actual scheduled/ordinary peers distinguish that hypothesis. | +| ORC-012 | This versioned record lists every requirement, exact baseline/content bindings, calibration classifications and unresolved cells. No bug-class closure claimed while production remains red. | +| ORC-013 | Boundary witnesses include scoped versus ordinary, current versus expired loan, metadata-only/truncate-only versus empty, pending versus completed durable turns, first versus second failure, same-key versus disjoint writes, cleanup before storage versus next-run admission. Mutation kills demonstrate the intended distinctions. | +| ORC-014 | The browser receiver supplies the real shared-scope/read/source-commit premise and rich row/metadata operations. Controlled storage rejection, abort and cleanup timing do not claim actual-host I/O/cancellation coverage; owners below retain those handoffs. | + +Grammar ablations: removing startup misses eager loading; removing subscription +misses on-demand buffering with already-ready status; removing after-ready loses +the regular route control. Removing scope distinction loses the original bug; +removing default/browser coordinator loses its real workaround. Baseline zero +proves no stored rows are needed; baseline one challenges source precedence. +Distinct and repeated keys distinguish count/order from final membership; +delete and truncate distinguish absence from stale rows; row/collection metadata +and empty transactions distinguish zero mutations from zero obligations. Atomic +batches check transaction granularity. Failure ordinal distinguishes preserved +prefix from abandoned suffix; pre-abort distinguishes abandonment from failure +of independent work. Tail, second on-demand scope and reopen distinguish current +adapter loans, later ordinary use and durable storage. Bounds are 0/1 baseline, +1–3 transactions per core template, 0–3 edits per transaction, 0/1 independently +pre-aborted transaction, and storage failure at ordinal 0/1. Browser rich history +has six transactions plus its ordinary tail. No random-run confidence is used +to extend these bounds. + +The laws still have meaningful open compositions. They are retained with owners +in the coverage map, not hidden by the green calibration: + +- **Primary persisted-history owner:** dependent aborts, abort during/after + publication, buffered partial-update recovery, arbitrary queued reloads, + late already-issued storage success/failure across replacement, and metadata + inherited from different owning reads need distinct scoped-adapter witnesses. + Existing tests continue to own their unscoped semantic laws. The new cleanup + witness does not promise cancellation/rollback of an already-issued write. +- **Shared-driver fairness owner:** K=1 and FIFO under storms already have their + own models and OPFS receiver. This change proves finite peer progress and + ordinary-work exclusion at a held hydrate; it does not claim the nested-source + composition under arbitrary backlogs. Needed witness: nested source commit + inside a hydrate while both lanes retain a K-bound-distinguishing backlog. +- **Per-Collection coordinator owner:** current-host schema/data isolation is + now received in OPFS, but elected-owner transfer and cross-tab RPC while + nested durability is pending remain with its ownership ledger. +- **OPFS receiver:** physical storage failure, abort/cleanup races, other browser + engines and non-browser hosts need provider-supplied premises. Controlled + errors in the core suite do not discharge those receiving obligations. +- **Production/history:** the default-coordinator repair remains open; historical + package release attribution still requires a bisect. A known reachable red + counterexample prevents closure regardless of the stronger test coverage. + +## Reviewed executable content + +| File | SHA-256 | +|---|---| +| `packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts` | `08ba69cdb4bcad7fa1c683d080ac3c51e75642caccf137864c87ca13ac8f17fe` | +| `packages/browser-db-sqlite-persistence/e2e/hydration-commit.opfs.ts` | `200fd6fb5edd99a000acbb92b7e67b79b4949978953e0de8efbefd4efb2360bd` | +| `packages/browser-db-sqlite-persistence/e2e/hydration-commit-oracle.opfs.spec.ts` | `48a5b85740c4e7a282ba7fdda53b076c44ac36bda6d2b06e9820554440e8003d` | +| `packages/browser-db-sqlite-persistence/e2e/hydration-commit.opfs.html` | `28b99007a046019cc8466eb5d51561a55ab2c975d94bb98880e3f09b0f762058` | +| `packages/browser-db-sqlite-persistence/playwright.opfs.config.ts` | `d596da20036070bc4fded9829517aa8c2e69844061d7ac66c4b2233871c184cd` | + +Change weight against origin/main: **production 0 lines**; tests/fixtures/config **+1705 / −220 (net +1485)**; documentation **+559 / −0 (net +559)**. The larger test diff includes formatting around two existing witnesses; no production machinery was added. + + +## Retained repair and current-base verification — 2026-10-06 + +The user approved the call-local adapter repair and PR preparation after the +RED audit above. The unpublished branch advanced to current `origin/main`, +`a37e69ab6aa35fd6a2a72d727de2b5168d10b1a0`, before retaining the repair. That +base adds Temporal persistence coverage. Its new dependency required linking +an already cached `temporal-polyfill` into this worktree's ignored dependencies. +The initial missing-import run collected no tests and is setup evidence only. +No package manifest or lockfile change belongs to this repair. + +`SingleProcessCoordinator.requestApplyCommittedTx` now accepts the interface's +existing optional `scopedAdapter` and selects it for that call. Ordinary work +continues to use the registered adapter. The coordinator does not retain the +loan. This changes two production lines and removes one, for a net addition of +one line. Test and contract-documentation growth is separate from that budget. + +The current implementation preserves the existing hydration completion, +acceptance/publication, FIFO, failure and scheduler contracts. It does not add +recovery state, bypass unrelated scheduled work, or replace the default +coordinator. The earlier RED-stage statements remain historical evidence. + +| Initial repair execution on the advanced base | Result | Reached evidence | +|---|---|---| +| Original core implementation | 48 failed, 566 passed, 1 existing todo | Routing assertions, with no timeout or setup failures | +| Retained repair, complete persisted oracle | 614 passed, 1 existing todo | All existing and added comparisons | +| Original implementation, real Chromium OPFS | 8 failed, 17 passed | Same-transaction scheduled reentry/cycle assertions | +| Retained repair, real Chromium OPFS | 25 passed | All histories and cleanup-failure calibration | +| Eight independent hostile repairs | All rejected by assertions | Early receipt 88, cached scope 36, dropped metadata 48, ignored truncate 24, dropped rows 148, reverse FIFO 48, swallowed failure 24, generic error 24 | +| TypeScript | Core package and focused browser oracle passed | No type errors | +| ESLint | 0 errors, 22 existing warnings | Existing require-await warnings only | +| Formatting and whitespace | Passed | Changed executable files and diff | + +The hostile repairs ran separately on the retained repair, and the exact +repaired production file was restored in `finally`. None failed during setup, +timed out, remained unreached, or survived. The counts refer to the 149 focused +checks. Browser GREEN uses the same local source paths as RED. Cached tooling +from another checkout supplies dependencies only, never the production build. + +Executable SHA-256 bindings at the initial repair stage (superseded below): + +| File | SHA-256 | +|---|---| +| `packages/db-sqlite-persistence-core/src/persisted.ts` | `84ad4e2e5c8c071c44f72dcff007a220484d2a9530a5a0db033efd49b0c03bd4` | +| `packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts` | `7c8fe3aeb32afb66303392c2b79762cf005790cab91c455d64634ba82b9b7845` | +| `packages/browser-db-sqlite-persistence/e2e/hydration-commit.opfs.ts` | `200fd6fb5edd99a000acbb92b7e67b79b4949978953e0de8efbefd4efb2360bd` | +| `packages/browser-db-sqlite-persistence/e2e/hydration-commit-oracle.opfs.spec.ts` | `48a5b85740c4e7a282ba7fdda53b076c44ac36bda6d2b06e9820554440e8003d` | + +Current Vitest commands add `--pool=threads +--pool-options.threads.maxThreads=2 --pool-options.threads.minThreads=1` to the +commands above. Current browser verification uses `--timeout=15000`. Logs and +raw prep-pr reviews are under `/private/tmp/issue-2046-prep-pr/`; this record +preserves the verdict-critical results without requiring those temporary files. + +### Original issue ledger: disposition after the production repair + +This entry updates the original R1–R9 ledger without rewriting its historical +verdicts. R1, R2, R3, R4 and R9 are **fixed-now** within the declared source +hydration grammar and Chromium receiving path. R5, R6 and R7 retain the +approved existing behavior and executable controls: **accepted-design**. +R8 remains **confirmed-open** only for historical release attribution. The +causal source change and current repair are established, but the earliest +published affected version still needs a package bisect. Totals: nine raw +items = five fixed-now + three accepted-design + one confirmed-open. + +The scoped routing defect is repaired. The broader scoped lifecycle, fairness +backlog, elected-owner/host handoff and historical-version witnesses listed in +[the coverage map](../oracle-coverage.md#source-durability-inside-a-shared-hydration-scope) +remain separate open evidence. These bounded results do not establish universal +liveness or every persistence composition. + +### Initial prep-pr review and cleanup oracle receipt + +The simplifier returned no proposals. Five parallel review angles checked +repository instructions, obvious bugs, history, prior PR decisions and code +comments. Their raw reports preserve 25 source-specific candidates, including +rejected hypotheses and all nine historical review findings. No reviewer found +an introduced production defect. The parent evaluated every candidate without +a severity cap: one fixed-now, ten already-fixed, twelve refuted and two +duplicates. The task-local append-only evaluation ledger retains the full raw +claims, sources, judgments and independent cleanup score. No candidate was +silently filtered or deferred. The reviewers traced the existing contracts +and distinguished introduced defects from historical diagnostic limitations. + +Two independent reviewers identified the same preexisting diagnostic weakness +in the strengthened optimistic causal witness. Direct cleanup in `finally` +could replace its primary error. The parent injected two sentinel failures in +that actual witness: the original form reported only the cleanup error. The +existing `cleanupPersistedOracle` helper retained the primary error and logged +the separate cleanup error. Both calibration runs reached their intended error +assertions without timeout. The retained test uses the helper and contains no +sentinel injection. This is oracle diagnostic hardening, not another production +bug or proof of a native cleanup failure. The helper's permanent calibration +continues to exercise primary and cleanup diagnostics. + +After this seven-line test increase, the complete persisted oracle again passed +614 tests with one existing todo. Core TypeScript and lint passed with the same +22 warnings, and the file remains formatted. Its final SHA-256 is +`7156c39e8b657367aeea5491065b5836beecfeecc1f1855e344f5a4fcec36f15`. +The other executable hashes above remain unchanged. The eight hostile runs +preceded this diagnostic-only edit and exercised the unchanged 149-case suite. +Direct FIFO replay on the new base passed three checks, skipped 596, and kept +one existing todo. The repair and browser fixture remained unchanged for the +25-check browser GREEN receipt. + +Two review qualifications remain explicit. Exact owning-scope exit is observed +for the held startup/subscription histories. The after-ready control does not +establish a universal readiness-versus-scope-exit ordering. Browser reopen here +creates a new Collection over retained persistence, not a physical browser or +worker restart. Native failures and process restart keep their separate owners. + +## Reproducing the retained checks from a fresh checkout + +These commands use tracked package scripts and configurations. Run them from +the repository root with the repository's supported Node.js and pnpm versions. +Install dependencies and build the core package with its workspace dependencies: + +```sh +pnpm install --frozen-lockfile +pnpm --filter '@tanstack/db-sqlite-persistence-core...' build +``` + +Run the complete persisted oracle through its repository Vitest configuration, +with at most two workers. Coverage and integrated typechecking are disabled +for this focused runtime invocation, as in the historical source-alias run: + +```sh +pnpm --filter @tanstack/db-sqlite-persistence-core exec vitest run --config vite.config.ts tests/persisted-oracle.test.ts --coverage.enabled=false --typecheck.enabled=false --pool=threads --pool-options.threads.maxThreads=2 --pool-options.threads.minThreads=1 +pnpm --filter @tanstack/db-sqlite-persistence-core exec tsc --noEmit -p tsconfig.json +``` + +For only the finite routing matrix, add `-t 'hydration source durability routing'` +to the Vitest command. To replay the existing FIFO property, prefix that same +command with `TANSTACK_DB_ORACLE_PROPERTY=sqlite-persistence.source-fifo-order +TANSTACK_DB_ORACLE_SEED=2046 TANSTACK_DB_ORACLE_PATH=0`. + +Install Chromium and run the real OPFS receiver. The explicit browser channel +selects the installed Playwright Chromium instead of requiring local Chrome: + +```sh +pnpm --filter @tanstack/browser-db-sqlite-persistence exec playwright install --with-deps chromium +PLAYWRIGHT_CHANNEL=chromium pnpm --filter @tanstack/browser-db-sqlite-persistence exec playwright test --config playwright.opfs.config.ts hydration-commit-oracle.opfs.spec.ts --reporter=line --timeout=15000 +``` + +The browser Vite configuration aliases the workspace source packages. These +commands need no `node_modules/.cache/issue-2046` configuration or `/private/tmp` +project. The temporary paths elsewhere in this record identify historical +session artifacts only. The reported historical counts belong to their stated +source/dependency revisions, not to an unexecuted clean installation. The PR's +CI separately runs the tracked package and OPFS configurations. + +The documentation follow-up for CodeRabbit review `5431163123` distinguishes +these reproducible entry points from the original session commands and removes +nontechnical personnel commentary. No executable file, oracle law, assertion, +or recorded runtime result changed. + + +## High-effort review receipt + +This follow-up evaluates all ten items from a source-only review of +`90cbda1e5`, against the later documentation head `996292b31`. The review found +no introduced production bug. Its useful findings concern the coordinator +contract, historical documentation, and oracle integrity. The runtime repair +remains unchanged; this follow-up adds six lines of interface JSDoc, with no +new production state, branch, fallback, or coordinator replacement. + +The interface now states that wrappers must forward a supplied leader-local +adapter and must not retain or serialize it. A forwarding wrapper and a +loan-dropping wrapper reach the actual default coordinator and produce opposite +routing observations. An explicit-adapter control also shows that this call can +succeed without a registered fallback; a later ordinary call must reject before +an apply until that fallback is registered. Collection construction supplies +that registration. This records the existing optional-adapter design, rather +than adding a new configuration restriction. + +The controlled routing probe now tracks each callback's lifetime independently. +Three calibration histories cover nested callbacks and overlapping callbacks +with either exit order. Each distinguishes a live loan, an expired loan, +reentry through the public adapter, and ordinary work after all callbacks exit. +These histories describe the controlled adapter. They do not claim that the +real SQLite scheduler permits overlapping hydrate callbacks. + +The eight rejected-buffer histories, request-local failed-read/retry witness, +and optimistic causal-replay witness each run with no scope API, a same-adapter +scope, and a distinct loan. Their existing exact failure, durability, public +row, acceptance, and applied-receipt comparisons are preserved. Adding a scope +probe no longer substitutes for either original adapter path. + +The OPFS receiver now observes both contenders at their actual scheduling +entry points before taking the held-read snapshot. It continues observing +peer hydrate and regular callback entry, and C's commit SQL, until A's owning +callback exits. A positive count requires the C SQL observer to be reached +exactly once, so a changed query cannot silently disable that comparison. +Election stream-position reads deliberately bypass logical scheduling and are +outside this regular-work law; treating all peer SQL as forbidden would impose +a false contract. The default coordinator's tail must enter public apply; +the browser coordinator's tail must enter its regular scope. Rich subscription +histories join the follow-up hydrations triggered by truncate before issuing +that ordinary tail. Final rows alone cannot establish correct tail routing. + +Scope identity is now a callback invocation token, not a reusable adapter +object or singleton active value. The actual held SELECT requires exactly one +owning callback and publishes a diagnostic immediately if that premise is +missing. Sixteen startup/subscription histories assert owning-scope exit. +Eight after-ready controls report no held scope (`null`); their useful receipt, +row, schema and reopen checks remain. Those controls do not prove a universal +readiness-versus-scope-exit ordering. + +### Calibration and validation + +All runs used this worktree's source with the historical cached tooling +explained above. The fresh-checkout entry points remain the tracked commands in +the previous section. Temporary mutations were restored byte-for-byte. The +missing-owner injection below is a deliberate harness fault, not evidence of +reachable overlapping callbacks in the real host. + +| Check | Result and meaning | +|---|---| +| Original scope-dropping behavior with strengthened core oracle | 50 assertion failures, 590 passes, 1 existing todo; no setup or timeout failure | +| Retained repair, complete core oracle | 640 passes, 1 existing todo | +| Original scope-dropping behavior, strengthened Chromium receiver | 8 cycle assertion failures, 17 passes; no setup or timeout failure | +| Retained repair, complete Chromium receiver | 25 passes | +| Callback-lifetime calibration against old probe | 3 assertion failures; all 3 pass with independent lifetime tracking | +| Cached-loan mutation | Old browser receiver: 25 passes. Strengthened receiver: 8 tail-route assertion failures, 17 passes | +| Ordinary scheduling bypass mutation | Old receiver: all 4 selected held-read histories pass. Strengthened receiver: all 4 fail at whole-callback peer exclusion, without timeout | +| Missing no-scope failure identity | 4 restored rejection histories fail exact-reason assertions; 20 other rejection histories pass | +| Missing owning-callback identity injection | Old harness waits until its 15-second test timeout with no observation. New harness reaches an immediate missing-owner diagnostic and assertion failure; no timeout | +| Types and static checks | Core and focused browser TypeScript pass; ESLint has 0 errors and the same 22 existing warnings; executable formatting and diff whitespace pass | + +The original eight hostile-repair counts earlier in this record remain bound +to their earlier executable revision. The new runs establish sensitivity for +the newly strengthened boundaries; they do not retroactively relabel those +historical runs as executions of this revision. + +### Review accounting and limits + +All ten review items retain separate dispositions in the task-local lossless +ledger: eight fixed-now (contract prose, documentation clarity, scope probe, +adapter-mode coverage, tail routing, owner diagnostics, causal preemption checks, +and after-ready coverage credit), one already-fixed (personnel commentary, +removed in `996292b31`), and one accepted-design (explicit adapter versus +registered ordinary fallback). Some claims needed qualification: the historical +hashes were stage-specific and the final reviewed hashes matched; after-ready +histories were not wholly vacuous; and real public A callbacks are serialized, +so the hypothesized existing overlap timeout was not reproduced as product +behavior. The deliberate lost-owner fault established the diagnostic weakness. + +There is no deferred repair from these ten findings and no new runtime defect. +The scope remains finite: the primary routing grammar, the restored failure and +replay paths, and one Chromium OPFS host. Arbitrary custom coordinator wrappers +must obey the stated contract; this suite cannot enforce third-party code. +The separately recorded scoped lifecycle, fairness backlog, host/election +handoff, native failure, and historical release-attribution gaps remain in the +coverage map. A Collection reopen here is not a process or worker restart. + +Current executable SHA-256 bindings for this follow-up: + +| File | SHA-256 | +|---|---| +| `packages/db-sqlite-persistence-core/src/persisted.ts` | `d319f74a1020f6fd96db5cd5cb995c42a7809c105b423abdc6da25ba2d1fb070` | +| `packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts` | `f3939fc3cdc81580398b7ff8a07737a5745dfb0da458b85af02585eaacdce89a` | +| `packages/browser-db-sqlite-persistence/e2e/hydration-commit.opfs.ts` | `a0044df64bbe8281a53150b2698b97a25f796d3cfae707f7eb5220dfd682bb68` | +| `packages/browser-db-sqlite-persistence/e2e/hydration-commit-oracle.opfs.spec.ts` | `6e7936824e92fb4f0adb2f7da5650ff5aaf9d2d10ecb8c8833b0947529f8271e` | diff --git a/docs/contributing/oracle-reviews/issue-2056-cache-eviction-design.md b/docs/contributing/oracle-reviews/issue-2056-cache-eviction-design.md new file mode 100644 index 0000000000..2c66b7aa05 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2056-cache-eviction-design.md @@ -0,0 +1,356 @@ +# On-demand cache eviction after resume authority loss + +The agreed law applies to an on-demand Electric Collection whose persisted +resume evidence cannot authorize its cached rows. That Collection starts a +`changes_only` provider session, acquires only demanded source snapshots, and +retires the old durable cache. A valid on-demand resume and an eager Collection +retain their existing paths. Each later loss of resume authority retires the +generation claimed by that run. A run that still claims an older generation +gets private empty storage; it cannot retire a newer generation used by another +run. A cache eviction does not prove an empty source snapshot. + +The original wrapper only called core `truncate({ markReady: false })` in +`startScopedRecovery`. It removed old public source rows but left old SQLite +rows available to new sync runs. The revised wrapper claims a distinct storage +ID before reading resume metadata and rotates that ID during scoped recovery. +Old rows remain for a warm run with a valid claim; a new claim sees the empty +generation with incompatible key-set evidence. SQLite drops retired storage +after its last claim releases or expires. A held registration race has a +separate test: late DDL must remove any recreated empty table and metadata. + +An ordinary persisted truncate is not an eviction. SQLite treats +`PersistedTx.truncate` as a complete replacement: it clears rows and expected +keys, then sets `key_set_evidence_available = 1` and +`key_set_evidence_incompatible = 0`. That certifies an empty baseline which a +subset-only provider session has not established. The broadcast coordinator +also reports it as `tx:committed` with `requiresFullReload: true`. A warm tab +then reloads its active subset from the empty disk cache and loses a valid +source row. The hostile global-invalidation control detects that public-row +loss. It rules out simply persisting the existing truncate. + +A safe eviction needs distinct durable authority and receiving semantics: + +1. Atomically make the old cache unreadable to new runs and record that its + key set is uncertified. Later partial source writes and resume metadata + writes cannot turn that marker into a complete baseline. Only an actual + complete source replacement may certify it. +2. Keep authority local to each sync run. Advancing the current persisted + cache generation prevents new runs from claiming the old cache; it does not + invalidate a different run's provider session or public snapshot. An + existing run may keep reading its generation only while its own resume + evidence and claim authorize it. Its own loss of authority stops further + durable hydration and requires source snapshots for later demands. +3. Fence writes admitted under the retired cache authority. Otherwise a slow + write can recreate stale rows after eviction. The coordinator's ordered + transaction position alone does not identify the authority under which a + write began. +4. Preserve a pending optimistic mutation's public state and any separate + durable mutation obligation. A tab must not delete that obligation while + its handler waits for provider confirmation. A refusal or error must be + observable if the system cannot preserve it. + +The approved storage design keeps successive persisted cache generations for +one on-demand Collection. Several sync runs may share a generation. Each +generation has a distinct storage ID. A sync run claims the current generation +before reading resume evidence. Invalid evidence advances the current +generation; a warm run keeps its old claim and public source snapshot, while a +new run sees only the new empty, uncertified persisted cache. If the warm run +later loses its own resume authority, its new generation remains private and +the current head stays available to new runs. Source commits +carry the claim that admitted them, and SQLite checks it inside the write +transaction. An older library writer knows only the public Collection ID. The +first new-format generation therefore uses a distinct storage ID too: older +writes may continue in legacy storage but cannot contaminate the new-format +cache. Bumping the existing `collection_registry` `schema_version` alone would +not fence that writer, because its default `sync-present-reset` policy may +reset and continue. The legacy storage ID is isolated, but its physical rows +are not collected by the new generation collector while an old library version +may still write there. That cross-version cleanup policy remains open. + +“Retired” means unavailable to **new** claims. It does not itself revoke an +existing run's claim. A counterexample to global revocation is two live tabs +with different shape identities: the newer tab must discard its old resume +evidence, while the older tab still has a valid provider session for its +shape. A global persisted truncate or full-reload notification destroys that +older tab's valid public row. Conversely, an existing run that loses its *own* +resume authority must stop hydrating its old cache even if another claim to +the same generation remains. A hostile global-eviction control demonstrates +why the shared truncate law is wrong here. A separate two-run Browser receiving +history now checks that the warm run keeps its row while the recovering run +gets its own source owner and empty persisted cache. + +The user approved expiring a long-paused run's claim so storage from crashed +runs can be reclaimed. Expiry must revoke the claim before a resumed run reads +or writes retired storage; that run then reacquires the current head and +reloads its demanded subsets. A timer by itself is insufficient because a +paused tab can resume before its next timer tick. The implementation uses a +five-minute default claim duration and renews active claims. Demand, explicit +scan, source-commit, and coordinator-message admission check for expiry; +SQLite checks read and write claims inside their transactions. A receiving +oracle delivers a peer notification before the timer runs and requires a new +source snapshot. There is no host-specific resume hook or synchronous fence +on arbitrary `Collection.get` calls. + +The implementation carries the claim through subset, resume-snapshot, +row-scan, and collection-metadata reads. SQLite rejects unclaimed reads of a +generation and checks the claim again inside each read transaction. A held +registration lookup lets a second adapter release the claim after the first +check; removing the transaction check returns the stale row, while the fixed +path rejects. A second held-registration witness releases the final claim +before DDL finishes, then checks that neither a table nor its metadata is +resurrected. Coordinator stream-position, replay, and index operations now +carry the initiating claim for managed storage IDs. SQLite checks that exact +claim before registration and inside the transaction. A two-claim real-SQLite +oracle rejects the expired run while the warm peer can still use its index; +a public Collection index event checks that the expired run cannot remove the +warm peer's persisted index. + +A later scoped rotation can overlap a pending old subset acquisition. The +old coordinator release previously waited for that acquisition's load promise, +while rotation waited for release inside the apply mutex. Both coordinator +implementations now offer unload before that promise settles. A two-run Browser +witness holds the old load during rotation, checks prompt recovery and exact +old acquisition release, and checks that the new demand reaches its new source +owner. A separate wrapper witness rejects a queued old source commit after +rotation and accepts a new one. + +Only one generation is current for a public Collection ID. Two different shape +identities can therefore alternate the current generation across sequential +new runs. Existing claims remain isolated, but this design does not promise +indefinite reuse of both shape-specific caches. It may repeat demanded subset +work; it must never force a full-shape download because of that churn. + +The coordinator also keys election, the one remote subset owner, transaction +positions, subscriptions, and invalidations by Collection ID. Sync runs using +different persisted cache generations can have distinct source owners and +transaction positions at the same time. A single coordinator key cannot +represent both: one owner would replace the other, and a broadcast full reload +from one cache could remove the other run's valid source row. Routing by +persisted cache generation is consequently part of the coordinator routing +key, with the public Collection ID unchanged. The cross-tab receiving oracle +checks that a warm run keeps its source row and remote demand while a run using +the new generation starts with an empty cache and acquires its own subset. +An automatic approval review rejected the first routing attempt because it +could disrupt cross-tab sync. A later two-run Browser production driver +demonstrated the misroute with the original implementation: a recovering +demand settled through the warm run's source. With generation routing, that +driver reaches the recovering source and retains the warm public row. The +controlled transport still needs a native multi-tab receiving witness. + +A bounded TLA+ exercise checked the abstract claim rule and produced +counterexamples for global truncate, unfenced write, and unclaimed collection. +That exercise did not model Electric, SQLite, or mutation settlement, so +receiving oracles must establish those refinements. + +The claim and resume evidence read occur as separate calls. SQLite validates +the claim at the read transaction, so a rotation between those calls cannot +return unclaimed rows. The persisted oracle now holds a hydration read and a +sequence-gap replay across claim expiry and checks that neither old result is +published. A held public row scan also retries against new private storage; +the startup oracle discards an expired resume-metadata read before the source +starts. Native multi-tab and OPFS receiving and cross-version deletion of +legacy storage remain unproven. Timed expiry permits +reclamation after a crashed or indefinitely paused run; its later reads and +writes must revalidate the claim. A test holds an optimistic mutation through +expiry and confirms that recovery does not settle its local obligation or +deadlock on the accepted truncate. + +Oracle review then found an in-scope counterexample to the source boundary. +Offering unload no longer blocks rotation, but an old provider request may +ignore unload and later deliver its rows through the same source callback. +The wrapper originally recorded the cache generation when that callback +committed, so it stamped an old row with the new claim and published it. A +composed wrapper run reproduced both the public stale row and the durable +write into the new generation. The original demand could also stay pending +forever while the coordinator awaited its old load promise. + +The installed Electric client injects `requestSnapshot` rows into its shared +stream after the fetch completes, with no request identity or cancellation +signal. Its standalone `fetchSnapshot` does not update the stream's snapshot +tracker and offset, so substituting it would change concurrent-change +semantics. The chosen repair gives a source sync result an explicit optional +`restartAfterScopedRecovery` capability with two phases. The persisted wrapper +calls it before rotation with a promise that settles after the accepted cache +clear. The source must retire old callbacks synchronously before returning; +Electric unsubscribes and retires its old lifecycle at that point. The +replacement changes-only stream starts only after the promise fulfills, then +pending demands acquire fresh snapshots. If rotation fails, the promise +rejects. This order is necessary: an executed hostile delivery between +rotation and a later one-phase restart put an old row in both the public +Collection and the new durable cache. The wrapper also rejects source commit +admission while recovery is pending, so a callback queued behind the apply +mutex cannot acquire the new cache generation. + +The new Electric provider session does not reuse the old resume record or +certify the partial cache as a complete baseline. It forgets old txid and +snapshot acknowledgements, including those imported before initial scoped +recovery. Pending Electric txid/match waits reject with `StreamAbortedError`; +a new wait needs evidence from the replacement session. Cleanup aborts an +incomplete old stream transaction, releasing its reserved commit turn before +fresh demands run. The Electric oracle reached each of these checkpoints with +a RED witness before the repair. A managed custom source without the restart +capability is refused before rotation, and a managed persistence adapter must +supply claim, rotation, renewal, and release together. + +Coordinator-only SQLite work now requires at least one live claim for a +managed generation, checked again inside its transaction. The new witness +fails on the original claimless stream-position read and checks replay and +index operations too. A read admitted before expiry can still finish +afterward, but the wrapper revalidates before using that result. An expired run now rotates +privately even if it remembers the current head, preserving another run's +ability to claim that cache. The storage and Browser oracles check both sides +of this rule. + +An old-generation remote subset release cannot hold up the new generation. +The Browser oracle pauses the old leader's release message while delivering +all new-generation traffic; the new source demand must start first. Rotation +now requests old release without awaiting its acknowledgement. The broadcast +coordinator retains release debt and retries after transport failure. Its +timeout-and-retry behavior remains a direct receiving-oracle gap. Browser, +Node, Expo, and mobile factories now forward the public claim lifetime and +clock options to the core SQLite adapter. The runtime reads the same clock +before accepting a peer notification or scheduling claim renewal; a Browser +real-SQLite receiving history advances that clock and rejects an obsolete +peer row before reloading demanded source data. + +One valid expiry cut remains fail-stop under the existing durability law. +If a source transaction becomes public before its SQLite write and the claim +expires before SQLite accepts that write, the receipt rejects and the +Collection enters terminal error. A real-SQLite hostile probe observed the +public row with no durable row. Automatic private rotation covers expiry +detected before source publication; compensating an already-public write +would need a distinct recovery law and publication witness. + +The current sync-present wrapper does not expose the local-only +`acceptMutations` utility. Its accepted source writes can nevertheless be +held from publication beneath a persisting optimistic transaction. This +distinction must be preserved in the mutation witness; treating a local-only +Collection as an Electric sync-present tab would exercise the wrong path. + +The final oracle review found four more cuts. An acquisition aborted while +the replacement Electric provider session waits for cache rotation must reject +promptly, even when another demand remains active. The descriptor oracle held +rotation and observed the old demand pending before the repair; it now observes +`AbortError`, then a fresh row for the sibling demand. A peer full-reload +notification can enter with a valid claim and finish its SQLite read after +expiry. The persisted oracle checks both a stale row returned by that read and +SQLite's second claim check rejecting it. Both outcomes discard old cache +evidence and reload demanded source data. A still-live claim with an unrelated +read failure remains terminal; expiry recovery must not hide that error. + +Cleanup can release the original claim while SQLite rotation is held. If the +rotation then creates a private generation, its returned claim belongs to the +abandoned sync run and must be released as well. A claim-ledger oracle was RED +with the orphan claim and GREEN after the release guard. A separate real-SQLite +oracle exposed a valid eager Collection ID beginning with the internal physical +ID prefix; prefix matching alone wrongly treated it as managed storage. SQLite +now checks catalog membership for claimless access. The same review held the +interval between a managed read's first claim check and collection of its +generation. Passing the claim into registration prevents late DDL from +recreating the collected table. Direct claimless adapter access to a physical +ID after its catalog row has been collected is structurally indistinguishable +from an arbitrary public ID; managed runtime calls always carry a claim. + +The full sqlite3 CLI suite exposed another host boundary: its subprocess API +cannot accept a NUL byte in a SQL argument. New physical IDs now use a +CLI-safe prefix. A CLI oracle writes and reads a claimed row through that +driver; the existing subset-failure cases still check their original public +and durable rows after seeding the claimed physical storage. A real-SQLite +control confirms that an eager Collection whose public ID starts with either +prefix remains usable. The internal prefix is only an allocation hint; catalog +membership and a claim decide whether storage is managed. + +Two further held-await histories sharpen the lifecycle law. A subset demand +aborted while initial scoped rotation holds the wrapper's apply mutex must +reject promptly without canceling a sibling demand. Electric provider-session +restart has its own abort cut, and cleanup during a held rotation must settle +quietly after rotation returns and release any late claim. The composed +Electric oracle reached the cleanup failure before the repair. It does not +claim prompt cleanup while an uncancelable adapter rotation remains held. + +Resume certification also reads SQLite under a claim. A read accepted before +expiry can return a consistent old key set afterward, or SQLite's second claim +check can reject it. The persisted oracle now requires private recovery in +both cases and retains terminal behavior for an unrelated live-claim I/O +failure. A real-SQLite temporary probe confirmed the late consistent result +was possible before the repair. Electric's installed SDK was separately driven +through a managed restart with a held old HTTP snapshot: the late old response +did not enter the new public or durable cache. That witness uses controlled +HTTP and Map storage; native multi-tab and real SQLite delivery remain outside +its scope. + +The independent review then found a cold-restart counterexample to the reset +marker rule. Claim-expiry rotation passed no Electric reset metadata to SQLite. +After one demanded subset wrote a partial row, that generation had incompatible +key-set evidence but no resume record. A later cold Collection started a +changes-only stream and hydrated that row before its source snapshot; an empty +authoritative subset snapshot still left the stale row public. A real SQLite +and installed-SDK receiving witness was RED at the pre-response public-row +checkpoint and GREEN after Electric treated an incompatible managed cache as +fresh-source evidence even without a resume record. The new run therefore +rotates and keeps the stale partial row private. An untouched generation with +unknown evidence retains its normal startup path. + +A second cold-restart challenge supplied an explicit Electric offset or handle. +Those options bypassed the original fresh-source predicate even though SQLite +reported an incompatible key set, and the stale row became public before the +source replied. The same real-SQLite and installed-SDK oracle now varies absent +and explicit source cursors. Both explicit cases were RED at the pre-response +public-row checkpoint and pass after the managed-cache incompatibility check +was made independent of source cursor selection. An explicit cursor still +controls the Electric stream; it cannot certify rows in an old cache. + +The final composed review also exposed an abort-settlement error. The wrapper +raced every returned subset load against its signal, including an uncancelable +SQLite hydration already in progress. It could report `AbortError` before the +late cached rows became visible, allowing core to release an overlapping +replay barrier too early. The persisted Electric interleaving oracle now holds +the cache read, aborts the demand, and records both settlement and public rows +before releasing the read. Fixed and random campaigns were RED because the +load rejected before publication. The repaired wrapper races abort promptly +while startup, cache rotation, or mutex admission can still skip hydration. +Once the adapter starts the baseline read, it waits for its rows and applied +receipts, then reports `AbortError`. A composed follow-up probe also held an +Electric source request after local hydration and confirmed that its own +cooperative abort remains prompt. + +The primary persisted owner is +`packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts`. Its +global-invalidation trace is a hostile control; the real-SQLite owner checks +generation rotation, physical collection, claim expiry, and uncertified +key-set evidence after a partial write. The Browser owner checks a recovering +demand beside an active warm tab and a peer notification delivered to an +expired receiver. The Electric descriptor owner checks restart, and the +installed-SDK owner checks subset-only network work. The remaining limits +above are recorded in the coverage map rather than inferred from green tests. + +## Follow-up: persisted demand coverage + +PR #2263 implements item 1 of #2102 in a narrower form than the issue +proposed. The issue asked for certified predicate and window evidence that +overlapping subsets could combine. The PR instead records persisted demand +coverage: evidence, in one persisted cache generation, that a load of one +exact demand reached applied settlement with its rows durably committed. Its +law is in `2026-10-20-on-demand-network-first-law.md`, and its primary owner is +`packages/db-sqlite-persistence-core/tests/persisted-demand-coverage-oracle.test.ts`. + +Coverage does not change this design's authority rules: + +- It is not a key-set certificate. An uncertified generation stays + uncertified. Coverage only lets a later load of the same demand settle from + rows that the same generation already holds. +- Coverage belongs to its generation. Scoped recovery and claim-expiry + rotation give the next load an empty generation with no coverage, and a + record that memory still holds from the retired generation does not count. +- An ordinary persisted truncate deletes every coverage record in its own + transaction, and a schema reset deletes collection metadata with the rows. +- A row deleted after the covered load settled ends coverage for that demand. + An ordered window whose rows change asks for a tie-boundary key that no load + covered. +- Overlapping demands never combine. A demand covers only its own exact key, + as #1797 requires for request deduplication. + +Shape-identity churn across new runs, described above, can alternate the +current generation. Coverage written under one shape's generation is not +reused under the other's. + diff --git a/docs/contributing/oracle-reviews/issue-2056-cache-generation-audit-7f1df5c82.md b/docs/contributing/oracle-reviews/issue-2056-cache-generation-audit-7f1df5c82.md new file mode 100644 index 0000000000..692ee90d51 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2056-cache-generation-audit-7f1df5c82.md @@ -0,0 +1,139 @@ +# On-demand cache-generation oracle review + +Reviewed code head: `7f1df5c82f7544148fe316ccbc115030ead8b0a7` on +`codex/evaluate-2056`. This record is a docs-only follow-up to that code +commit. The [design record](issue-2056-cache-eviction-design.md) explains the +chosen lifecycle and its counterexamples; the +[coverage map](../oracle-coverage.md#electric-recovery-demand-and-scoped-persisted-restore) +names each executable owner and its remaining receiving cuts. + +## Judgment and observed boundaries + +When persisted evidence cannot authorize an on-demand Electric cache, the run +must retire its claimed cache generation and reload only active subset demands. +Another run with a valid claim keeps its own public rows and storage. An expired +claim cannot read, write, or republish retired rows. The replacement Electric +provider session excludes late callbacks and acknowledgements from its retired +session. A subset load that has begun uncancelable local hydration settles only +after the rows it accepted become visible, even when its caller aborts; it then +reports `AbortError`. An explicit source offset or handle does not certify an +incompatible persisted key set. These laws come from the approved design, the +Collection subset signal and applied-receipt contracts in `packages/db/src/types.ts`, +and the Electric guide. They are bounded by the observations below, not a claim +that every crash, native host, or provider schedule has been explored. + +The primary model and production drivers live in +`packages/db-sqlite-persistence-core/tests/persisted-oracle.test.ts`, +`packages/db-sqlite-persistence-core/tests/sqlite-resume-snapshot-oracle.test.ts`, +`packages/browser-db-sqlite-persistence/tests/per-collection-coordinator-oracle.test.ts`, +`packages/electric-db-collection/tests/electric-descriptor-isolation-oracle.test.ts`, +`packages/electric-db-collection/tests/electric-oracle.property.test.ts`, and +`packages/electric-db-collection/tests/electric-sdk-delivery-oracle.property.test.ts`. +Their opening prose assigns narrower authority and names model, history, +production path, and checkpoint. The SQLite owner uses real `node:sqlite` and +an independently computed generation/claim ledger. The Electric installed-SDK +owner supplies controlled HTTP to the real client, while the Browser owner +exercises its coordinator and host factory. + +Three independent oracle reviewers challenged the integrated code after the +initial implementation. One reproduced early `AbortError` settlement while a +held cache read later published a row. Both fixed and random persisted Electric +interleaving campaigns were RED at the pre-release settlement comparison and +GREEN after the wrapper waited for the admitted hydration. Its temporary +composed follow-up passed five adjacent cuts, including prompt abort before +hydration, cleanup during rotation, old receipt fencing, and post-hydration +Electric request abort. Another reviewer used real SQLite and the installed +Electric SDK to show that explicit offset and handle each exposed a stale row +before source response; the no-cursor control did not. The expanded permanent +oracle was RED for both explicit cases at that public-row checkpoint and GREEN +after the trust predicate changed. The third reviewer executed five SQLite +claim/rotation cases and three Browser receiving cases without a new defect. +Earlier hostile controls rejected global truncate, stale provider callbacks, +unclaimed writes, retained old acknowledgements, and a false-green insert +fixture at their named checkpoints. These outcomes are assertion failures; +no timeout or setup failure is counted as a successful mutant kill. +The interleaving shrink reported seed `18530601`, path `0:0:0:0:0`. Direct +replay with `TANSTACK_DB_ORACLE_PROPERTY=electric.persistence-interleaving` +failed after one test at that same held-hydration comparison under an +in-memory early-abort mutant; direct replay on the reviewed code passed. The +mutant was applied by a temporary Vitest transform outside the PR worktree. + +Validation on the reviewed code head: Electric 1,074 passed, 1 skipped; +persistence core 1,247 passed, 2 TODO; Browser 405 passed; Node 231 passed; +Expo 76 passed; React Native 251 passed. The changed packages built, Vitest +reported no type errors, changed-file ESLint had no errors, Prettier passed, +and `git diff --check` passed. A core lifecycle test timed out during a +concurrent multi-package run; it passed alone and in the later serial full +core run. Native multi-tab/OPFS receiving and live Electric service behavior +were not run. + +## ORC-012 requirement audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Applicable. The approved cache-generation and subset contracts above are the authority. Each owner states its narrower path and limits. Full native-host and live-service behavior remain outside the result. | +| ORC-002 | Applicable. Expected rows come from ordered source changes or independent map/claim ledgers, rather than the production cache classifier or Electric tag index. The explicit-cursor and held-abort laws use public pre-response and settlement observations. | +| ORC-003 | Applicable. The owner files place contract prose beside their model, legal histories, production driver, observations, and comparison cuts. The interleaving owner now explains why abort does not undo already admitted cached rows. | +| ORC-004 | Applicable to generated campaigns. The persisted interleaving grammar reconstructs both schedule kinds and rejects missing, foreign, and out-of-range values. Other owners record their own axes and controls. Native host scheduling and arbitrary service frames are excluded. | +| ORC-005 | Applicable. Drivers call real Collection, persistence, coordinator, and Electric entry points. Checks compare public rows, durable rows, claims, request counts, load settlement, and acknowledgements at held or final cuts. Real SQLite plus installed SDK receives the cold-restart premise. | +| ORC-006 | Applicable. Wrong designs were executed: global truncate, stale callback admission, unclaimed writes, early abort settlement, explicit-cursor cache trust, and merge-on-insert fixture behavior. Each named outcome was an assertion failure at the intended comparison. | +| ORC-007 | Applicable to important generated properties. Their fixed and unseeded campaigns use the same property and budget, with seed/path replay interfaces. Direct replay of the persisted interleaving RED seed `18530601` and path `0:0:0:0:0` failed under the early-abort mutant at the intended checkpoint and passed on the reviewed code. Fixed real-SQLite/SDK examples do not trigger ORC-007. | +| ORC-008 | Applicable where a claim ledger or retained-demand state was added to a reference model. Distinct current, retired-but-claimed, and expired states are retained because claim, read, and rotation next actions distinguish them. The source-row model stores only rows and authored order; cache generations are not inferred from public rows. | +| ORC-009 | Applicable. Model claim and generation map to the glossary's persisted cache claim and persisted cache generation. The model's controlled HTTP response is provider input, not a Collection publication. Electric provider session remains separate from Collection sync run. | +| ORC-010 | Applicable. New held-cut drivers release gates and clean up in `finally`, and the Electric lifecycle helper retains primary failures with cleanup diagnostics. Not every older fixed witness has been audited for cleanup-failure fidelity; no universal ORC-010 claim is made for the whole pre-existing suite. | +| ORC-011 | Applicable. A controlled ShapeStream mock could share the wrong assumption that a full-mode subset request works. The installed SDK/controlled-HTTP owner supplies a second formulation and rejects that assumption. Real SQLite cold restart distinguishes a mock-only cache. Native receiving remains separate. | +| ORC-013 | Applicable. Valid and expired claims distinguish generation retention from wrong global eviction; absent and explicit source cursors distinguish cache trust from cursor selection; abort before and after hydration entry distinguishes prompt cancellation from premature settlement. These are bounded histories, not all possible thresholds. | +| ORC-014 | Applicable. Controlled Electric delivery and coordinator messages supply material premises. Installed-SDK and real-SQLite witnesses receive the cold-restart premise. Native multi-tab/OPFS receiving, live service framing, and timed-out old-leader release retry lack matching receiving witnesses and remain assigned in the coverage map. | + +## Open cells + +A later test-only follow-up, `e5095c0d6`, strengthened the Electric canceled +subset reacquisition witness after CodeRabbit observed that `second !== true` +also accepts a cached rejected promise. The oracle now awaits the identical +later demand and requires a second SDK snapshot invocation in both late +resolve and late reject histories. A rejected-promise wrong result passes the +old predicate and fails the new one; the focused tests and typecheck pass. +This extends the caller-settlement and provider-invocation comparisons without +changing the production law or the reviewed implementation commit above. + +The next CI run found a receiving gap in Electron IPC and stale logical-ID +assumptions in two Browser OPFS fixtures. Electron's renderer did not advertise +or forward managed-cache claim operations, so the shared SQLite adapter +contract could not exercise cache rotation through IPC. Protocol v3 now +forwards claim, rotate, renew, release, and claim contexts. The renderer sends +the logical Collection ID for main-process adapter selection while cache reads +use the claimed physical storage ID. An in-process receiving witness exercises +claim, read, index operations, renew, rotate, read again, and release against +real SQLite; both v1 and v2 requests are rejected before adapter work, and +their responses are rejected before result parsing. The shared Electron +contract and package suite pass (163 tests, no Vitest type errors), and the +package build and changed-file lint pass. +The Browser fixtures now observe the claimed storage ID at the adapter or +coordinator boundary. The full real-Chromium OPFS suite passes (29 tests), +including the on-demand hydration and two-tab routes that failed before the +fixture correction. The installed local Electron package lacks its executable, +so actual separate main/renderer process receiving remains unproved; the +in-process IPC path and real SQLite adapter are the present evidence. + +The next CI run reached the live Electric/OPFS hydration-straddle witness and +found one more stale fixture observation. Its public row was present, but it +read `durableRows=[]` from the old logical ID before the controlled hold began; +the same ID also made its hold target and leader check unreachable. A focused +local run reproduced that RED first checkpoint in 59.7 seconds. The fixture now +observes the claimed storage ID and claim at coordinator registration, reads +durability under that claim, and targets the same ID for hold and leadership. +PostgreSQL remains the independent source of the expected row. The focused +history is GREEN, and the full live Electric/PostgreSQL/Chromium OPFS suite +passes all 15 tests, including handoff and reopen. This restores the host +witness; it does not change production behavior. + +The coverage map keeps the specific in-scope cells open: claim expiry after +public source publication but before SQLite accepts the write is fail-stop; +legacy pre-upgrade on-demand tables remain isolated but uncollected while old +tabs can still write; prompt cleanup during a permanently held adapter rotation, +native multi-tab/OPFS receiving, old-leader release timeout retry, full retired +metadata collection, and direct claimless access after catalog collection are +not established. Browser receiving also lacks one combined history with two +runs sharing a generation, expiry of only one claim before its timer, and exact +warm-peer row and lease retention. This review does not declare the entire +cache-eviction bug class closed. diff --git a/docs/contributing/oracle-reviews/issue-2056-full-mode-recovery.md b/docs/contributing/oracle-reviews/issue-2056-full-mode-recovery.md new file mode 100644 index 0000000000..de34a533cb --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2056-full-mode-recovery.md @@ -0,0 +1,286 @@ +# Issue 2056: full-mode recovery and subset demand + +Review record, revision 1 — 2026-10-06. Reviewed head: `f43a16522c990134ae993235a312d2d5e433dc8a`. + +Source: [issue #2056](https://github.com/TanStack/db/issues/2056), including its workaround, test offer, and #811 reference. The issue has no comments. This is an issue evaluation, not a PR review. The user authorized oracle changes, reproduction, and comparison of fixes; production implementation remains for their decision. + +Branch: `codex/evaluate-2056`, based on fetched `origin/main`. Dependencies installed from the lockfile offline with install scripts disabled. No branch or commit was pushed. Production source is restored byte-for-byte to the reviewed head. The changed tests intentionally fail on that head. + +## Verdict and reviewer assessment + +The reported capability mismatch is real and merits P1 treatment: a valid persisted restart rejects subset demand and live-query preload. The installed Electric SDK 1.5.15 reproduces it through the real Electric adapter and persistence wrapper. Tagged and untagged two-launch cases provide the distinguishing control. + +The report overstates one causal step on current main. `subscribe()` already starts the stream. The receiving witness observes the full HTTP request before acquiring demand, then observes a ready source Collection with the correct replacement row alongside the rejected demand. Permanent `loading` and the necessity of deleting local data are not established here. The original Expo reproduction may have additional scheduling or version conditions. + +Accuracy and signal are high; the report identifies the key mismatch, gives a useful restart sequence, and separates its workaround from a durable optimization. Analysis of settlement is incomplete. Readiness alone cannot discharge an applied-receipt obligation, and a one-time gate cannot cover later reset cycles. + +## Finding ledger + +The original append-only ledger and raw JSON are in `review-evidence/issue-2056/`. IDs remain in source order, including the final footnote. + +| ID | Original claim / proposed fix | Evidence and technical verdict | Disposition / PR action | Durable value and destination | +| --- | --- | --- | --- | --- | +| R1 | Tagged on-demand persistence works once, then fails on a new launch; Collection stays loading and every live query fails until data is cleared. | Core failure confirmed on main/adapter 0.5.5 with SDK 1.5.15. Both direct demand and public live-query preload reject. The source can still become ready with correct rows. The universal loading/deletion claim is not established. | `confirmed-open`, P1; select a repair. | Real-SDK two-launch receiver and the tag-history oracle. Exact Expo/version-specific hang remains under R6. | +| R2 | Loss of in-memory tags plus `requiresTagState: true` selects a full stream at initial offset. | Confirmed: the first launch durably writes the flag; a new descriptor and Collection request `log=full&offset=-1`. Untagged compatible resume requests changes-only at `2_0`. | `duplicate` of R1's mechanism; preserve the state distinction. | Resume evidence must account for tag membership. The coverage map now names the primary tag owner explicitly. | +| R3 | `loadSubset` calls `requestSnapshot`, which rejects in full mode before stream startup. | Illegal call and SDK rejection confirmed. Merely enforcing the SDK restriction makes three existing tests fail. “Before stream startup” is refuted for the tested current-main path: a full HTTP request already exists, and delivery can produce readiness. | `confirmed-open`, P1, for capability dispatch; retain the refuted causal clause separately. | Capability-aware mock and installed-SDK receiver. Final rows alone cannot establish successful acquisition. | +| R4 | Wait for first `markReady`, reject initial error, resolve collection abort. | A representative readiness-only prototype fixes the reported case but fails both held-application cuts. An applied-only startup gate fails a later same-run reset. Reporter supplied no patch, so these are evaluations of the described strategy, not their exact code. | `design-decision`: recommend a current-replacement applied gate. | Applied-settlement, reset-continuation, exact initial-error, and concurrent-cleanup witnesses in the Electric history owner. | +| R5 | Every tagged launch downloads the full shape; persist tag state with the resume state instead. | Current cold tagged recovery does use the full log. A durable tag ledger could avoid it, but storing the latest row headers is insufficient: silent moves can change selected membership and active conditions without a row write. | `design-decision`: separate optimization; it does not replace the capability repair for reset or uncertified baselines. | A future tag certificate must include complete selected membership/active conditions and be atomic with rows, offset, handle, shape identity, and key-set evidence. | +| R6 | Offered two-launch `node:test` witness uses Expo persistence over node:sqlite and a fake tagged server; fails 0.5.4 and passes their patch. | The offered fixture was not attached. Our SDK receiver uses the real persistence wrapper over the existing controlled durable-storage seam. It confirms the adapter problem but does not verify Expo, SQLite, a separate OS process, or the published 0.5.4 package. | `confirmed-open` evidence gap, within a declared external boundary. | Receiving owner: Expo persisted-collection E2E; obtain the reporter's fixture or reproduce the same schedule there. No need to delay the demonstrated adapter correction for that host claim. | +| R7 | #811 reports the same error in progressive mode. | Historical comparison is accurate. #811 is closed; its resolution identifies #852. Current progressive tests remain green with the strict provider mock. | `already-fixed` for the earlier progressive path; no duplicate progressive repair. | Preserve the broader lesson: dispatch from actual provider capability, not only configured sync mode. | + +## What the report exposes about the laws + +### L1. Acquisition must use the current provider session's capabilities + +Authority: the installed SDK's `requestSnapshot` contract permits changes-only mode and rejects full mode. The adapter's full-recovery decision is necessary when persisted resume evidence is insufficient. The obligation is conditional on the chosen provider session; it does not require future designs to always choose full recovery if they can supply a valid complete certificate. + +Primary owner: `electric-descriptor-isolation-oracle.test.ts`, specifically `runTagHistory`. It already generated eager/on-demand/progressive modes, warm/cold restart, reset/compatible resume, tagged/untagged/legacy evidence, and interrupted recovery. It already acquired a subset after restart. This was not chiefly a missing random input. Its permissive mock accepted an SDK-forbidden operation and returned success before the replacement existed. Tightening just that mock turned 3 of its original 30 tests red. + +The repaired driver starts demand without requiring impossible early completion, allows persisted hydration to become visible while demand is pending, and asserts successful acquisition after full replacement. It retains partial-replacement, callback, durable-row, move-out, interrupted-restart, and post-completion demand observations. Bounded cases independently force cold tagged, cold legacy, untagged compatible, and explicit reset histories. + +Receiving witness: `electric-sdk-delivery-oracle.property.test.ts` now reconstructs both launches through real `ShapeStream`, with direct demand and public live-query preload. It verifies the saved flag, actual HTTP mode/offset, correct source row, source readiness, and acquisition outcome. The tagged cases fail while the untagged cases pass. Authored HTTP messages and controlled durable storage remain explicit premises; a live service/native host is not inferred. + +Encoding and enforcement: present for the named bounded and generated histories. The original implementation is rejected at subset settlement, not by a timeout or an unrelated setup error. This proves the mock restriction is material. It does not prove arbitrary native restart schedules. + +### L2. Readiness, application, and replacement authority are different facts + +Authority: `LoadSubsetFn` in `packages/db/src/types.ts` requires successful loads to await every receipt establishing their result. `SyncAppliedReceipt` describes visibility. `whenSyncAccepted` in `packages/db/src/sync-receipt.ts` deliberately lets Collection readiness precede application to avoid optimistic-transaction deadlock. Existing `publishes readiness once a held receipt is accepted` protects this distinction. + +The main Electric oracle's opening prose incorrectly said readiness waited for applied receipts. It now matches the existing contract: acceptance before Collection readiness, application before subset success. Changing that prose does not change product policy. + +The added law checker holds a real optimistic transaction, receives a complete source snapshot, observes Collection readiness while the source row is absent, and demands that subset settlement remain pending. Acquisitions before and after readiness matter: the latter must still account for a receipt created before its own commit cursor. Releasing the transaction must reveal the exact source row and allow success. Both cuts reject the readiness-only prototype at the explicit pending assertion. + +A continuation starts a new demand after a same-run `must-refetch`. The previous completed snapshot cannot satisfy the new private replacement. The one-time applied gate fails this pending assertion. The candidate that renews its obligation on reset passes it. No change to global Collection readiness is necessary. + +Encoding and enforcement: complete for those named cuts, including the distinguishing late acquisition and reset continuation. Empty full replacements, multiple resets overlapping held application, and independently cancelled sibling acquisitions still need dedicated witnesses before claiming closure across all such histories. + +### L3. Failure and cleanup must settle retained waiters with the right authority + +Authority: existing initial-error and cleanup laws in the Electric and Collection lifecycle owners. Two concurrent full-recovery demands reject with the exact initial stream error. Cleanup settles both and makes subsequent callbacks obsolete. Cleanup outcome is intentionally not constrained to one rejection shape; invalidation and quiescence are the relevant obligations here. + +The checker distinguishes a provider error from cleanup. During development, an overstrong draft treated any error callback as invalidating all later provider callbacks. That is not the existing retry contract; the final test checks error settlement and reserves stale-callback assertions for cleanup. + +Encoding and enforcement: named initial-error and cleanup schedules are enforced. General same-run recovery after errors, request-local abort timing, and native shutdown scheduling are outside these tests, not silently counted as green. + +## Candidate fixes + +All patches are evaluation artifacts under `review-evidence/issue-2056/`; none is applied to production in the final worktree. + +| Option | Result | Assessment | +| --- | --- | --- | +| Wait only for first readiness | Reported restart and SDK receiver pass; both held-application cases fail. | Reject as a complete repair. Successful readiness is weaker than successful subset application. | +| Wait for initial full snapshot's applied receipt | Initial restart and both held-application cuts pass; another same-run reset exposes early success. | Better, but incomplete. | +| Wait for the current full replacement's applied receipt; renew the obligation after reset | All 238 runtime tests in the four affected suites pass, including public live-query consumers. Temporary prototype adds 31 production lines. | **Recommended direction.** Reuse the existing stream lifecycle and applied receipt; do not replace the lifecycle or change global readiness. Remaining scope is listed below. | +| Persist a complete tag certificate | Not implemented or experimentally validated. | Potential bandwidth optimization. Requires a separate storage/visibility design and migration coverage; full-mode demand handling is still needed for reset/uncertified evidence. | + +Forcing changes-only mode or simply returning success removes the symptom by abandoning established obligations. Neither is an acceptable substitute for authoritative recovery or applied settlement. + +The recommended prototype is deliberately reviewable, not declared ready to merge. It still needs the omitted boundary witnesses and review of per-request abort behavior, naming, and failure ordering. Its 31-line cost should be compared with simplifying existing completion machinery before final implementation. The user requested choices before a production repair, so these are open design/implementation items, not approved deferrals. + +## Changes and verification + +Changed production code: **zero final lines**. Three oracle files were strengthened, and their existing 81-line persistence fixture was moved into a shared 87-line support module. The support module owns no expected-result computation and is not a new oracle. Test net growth is 529 lines. This review record and the coverage-map entry are separate documentation weight. + +- Original tag oracle: **30 passed**. +- Add only the SDK capability restriction: **3 failed, 27 passed**. Failures are the forbidden snapshot operation, not timeout/setup failures. +- Original other three suites: all runtime assertions passed (195 runtime tests), but the combined default run exited nonzero because fresh-worktree typechecking lacked the unbuilt package's self declaration. This is not reported as a clean baseline. +- Final oracles with unchanged main: **11 failed, 227 passed**. Six tag-history tests, three completion-law tests, and two real-SDK tagged restart tests fail at their promised behavioral comparisons. +- Readiness-only prototype: **2 failed, 6 passed** in the selected eight-case comparison; both failures are premature subset fulfillment under held application. +- One-time applied prototype: the same-run reset witness fails at `prior readiness is not replacement coverage`. The initial receipt checks pass. +- Reset-aware applied prototype: **238 passed** across descriptor isolation, SDK delivery, Electric histories, and recovery histories. +- Direct replay: seed `42712`, path `4:1:1:1`, property `electric.persisted-tag-history` reproduces the original violation with **1 failed, 30 skipped**. No fixed/random campaign precedes it. +- Focused TypeScript check with a temporary source alias for the unbuilt package self import: passes. Changed-test ESLint and formatting checks pass. The package lockfile was unchanged. + +Runtime comparison command, from `packages/electric-db-collection`: + +```sh +pnpm exec vitest run tests/electric-descriptor-isolation-oracle.test.ts tests/electric-sdk-delivery-oracle.property.test.ts tests/electric-oracle.property.test.ts tests/electric-recovery-oracle.test.ts --coverage.enabled=false --typecheck.enabled=false +``` + +Direct replay: + +```sh +TANSTACK_DB_ORACLE_PROPERTY=electric.persisted-tag-history TANSTACK_DB_ORACLE_SEED=42712 TANSTACK_DB_ORACLE_PATH=4:1:1:1 pnpm exec vitest run tests/electric-descriptor-isolation-oracle.test.ts --coverage.enabled=false --typecheck.enabled=false +``` + +## Oracle-guide audit + +| Requirement | Evidence / limit | +| --- | --- | +| ORC-001 authority | SDK mode restriction, LoadSubsetFn applied settlement, existing acceptance/readiness contract, and restart invalidation. Native and infinite-history limits are explicit. | +| ORC-002 independence | Existing row Maps/tag sets, exact source replacement rows, and controlled application obligations compute expected behavior. Production state-machine helpers are not copied into the model. | +| ORC-003 literate responsibilities | Opening/local prose names capability, settlement, history, real driver, and checkpoints. The shared persistence fixture remains driver support. | +| ORC-004 grammar | Existing 0–6 edits, keys 1–3, three tag names, full removal permutations, three sync modes, and four warm/cold × reset/resume paths remain. Tagged/legacy bits control recovery evidence; interruption controls retirement; edits vary payload and membership. Fixed cases reconstruct the report and reset/legacy neighbors. Updates target existing modeled keys; foreign/malformed moves are excluded. Longer histories and DNF restart are not claimed. | +| ORC-005 observations | Public and durable rows, actual SDK HTTP mode, raw promise outcomes, application hold, late demand, reset cut, error identity, and stale callback after cleanup. Query comparison retains payload values and cardinality; virtual metadata is outside this law. | +| ORC-006 calibration | Original SDK failure, readiness-only early success, and one-time gate early success at reset are rejected at explicit comparisons. Earlier wire-encoding/projection setup mistakes were corrected and are not counted as product RED evidence. | +| ORC-007 campaigns/replay | Tag fixed/random campaigns now have the same budget (10), generator, driver, and checker. Replay runs only the selected property; saved seed/path was executed. No new generated property was added. Other historical campaigns are not claimed comprehensively audited. | +| ORC-008 minimality | Existing tag model state is unchanged. Added promise outcome is an observation, not a replica state-machine copy. Before/after readiness and before/after applied receipt are legally distinguishable cuts. | +| ORC-009 vocabulary | Uses Collection readiness, acquisition, applied receipt, cleanup, restart, and truncate replay distinctly. Fresh descriptor/Collection stands in for loss of process-local state, not actual process restart. | +| ORC-010 fidelity/cleanup | SDK and receipt witnesses reuse `withElectricCleanup`/`atCheckpoint`. Primary tag owner retains existing finally cleanup. Replay reaches the same settlement mismatch. Injected cleanup-failure fidelity for the changed tag driver remains unverified. | +| ORC-011 alternate formulation | The real-SDK receiver challenges the permissive-mock hypothesis through actual HTTP dispatch. The held-application witness independently challenges readiness-as-application. No second full replica model is needed. | +| ORC-012 review evidence | This head-pinned record, raw report, append-only ledger, candidate patches, and exact run logs retain judgments and remaining boundaries. No bug-class closure is claimed. | +| ORC-013 distinguishing boundaries | Tagged versus untagged cold restart; explicit reset without tags; legacy unknown tag evidence; demand before versus after accepted readiness; same-run reset after completed recovery. Each rejects a named wrong design. | +| ORC-014 handoff | Mock capability restriction is received by installed SDK 1.5.15. Live-server subquery tag generation and Expo/native persistence have no new receiver here and remain explicitly open. | + +## Remaining owners and loss audit + +The coverage map records these witnesses as open: native Expo/SQLite and actual process relaunch; live Electric subquery/tag emission; missing/unknown key-set evidence with full-mode demand; empty full replacement; overlapping resets and held application; independent request aborts; persisted DNF active-condition reconstruction if tag certification is selected. Existing receiving owners are named there; no individual maintainer has been assigned. + +Raw items were reread after the experiments. Accounting: **7 = 3 confirmed-open + 1 duplicate + 2 design-decision + 1 already-fixed**. No original item, workaround, test offer, or related-issue footnote was dropped. There are no `fixed-now` or `deferred` items. The subclaim about permanent loading is not conflated with the confirmed acquisition rejection. + +Authorized reproduction, law audit, and candidate comparison are complete within the stated boundary. Production repair and whole-class closure are **not** complete; the user asked to review potential fixes first. Main still fails the strengthened tests, and the saved patches do not change that fact. + +## Continuation: scoped on-demand recovery + +Implementation worktree revision — 2026-10-07. Base head: +`0dfade1f600bd409019111cd89bf278d65a82d21` (#2059). The preceding +review verdict remains tied to its original head; this section records the +subsequent proposed contract and implementation. The user selected +bounded on-demand recovery after considering the full-shape cost. The Electric +guide now states the proposed change: when persisted resume evidence is +uncertified, the current SQLite wrapper keeps durable rows as cache but removes +them from the public source Collection, starts changes-only at `now`, and loads +only demanded subsets. This intentionally revises the former promise that all +cached rows remain visible through a cold full-shape replacement. Eager and +progressive recovery keep that promise and the full snapshot. The on-demand +full-stream fallback remains for a wrapper without scoped recovery support. + +The implementation and its executable evidence are committed at +`7c2e6a4f640bbbef64c1045362b22346a8c46e21`. This audit reference is a +documentation follow-up, so the cited code revision remains immutable. + +The first new receiver prediction was RED on the merged base: tagged direct +demand and live-query preload expected `log=changes_only` but observed `full` +(two failures, two compatible-resume controls passing). With the implementation, +the SDK receiver checks tagged, changed-shape, and malformed-state restart. It +observes a scoped HTTP request, no full baseline-row read, no local subset read, +applied demand settlement, stale B retained on disk but absent publicly, an +empty first or later B snapshot, and a third launch with no active demand. The +primary tag owner retains its generated restart grammar and distinguishes +durable cache from public rows when scoped recovery is selected. A separate +receiver checks that two concurrent demands invoke the installed SDK's +`requestSnapshot` one at a time; cursor tie/from legs also run in order. + +Calibration: removing only the persistence hydration gate made all three +uncertified-cache receiver cases fail at the intended local-read assertion +before provider response. The original full-log dispatch failed at the HTTP +mode assertion. A controlled installed-SDK probe also showed that overlapping +responses can regress a shared row and offset when the later response arrives +first. A temporary production mutant that removed serialization was rejected by +automatic approval review, so this record does **not** claim an executed mutant +kill for the ordering witness. Its named invocation-order assertion and SDK +probe are narrower evidence. + +Verification on this implementation revision: 367/367 tests pass across the five +affected Electric suites; 640 pass and one pre-existing TODO remains in the +SQLite persistence oracle; 6/6 sync-persistence capability tests pass. The +saved tag-history seed `42712`, path `4:1:1:1`, passes by direct replay. Electric +and SQLite persistence TypeScript checks pass; the Electric check uses a +temporary source alias for this fresh worktree's missing built self-declaration. +Changed source and oracle files pass ESLint and formatting checks. Production +weight is 119 added and 27 removed lines across Electric, persistence, and the +internal capability. The added state separates cache-authority from source +authority; a later demand legally distinguishes those states. The true full +stream's replacement gate and the shared SDK cursor queue are separate +obligations, not substitutes for quarantine. + +| Guide requirement | Continuation evidence and limit | +| --- | --- | +| ORC-001 authority | The revised Electric guide is the proposed product authority in this branch; SDK capability, scoped source-snapshot meaning, and applied settlement remain established contracts. This is not yet a merged public guarantee. | +| ORC-002 independence | The A/B source Map predicts rows independently of Electric tag machinery and the persistence wrapper; the controlled adapter and HTTP server drive production only. | +| ORC-003 literate roles | The tag owner's opening/local prose and the SDK receiver's opening/local prose name law, model, histories, driver, public observations, and checkpoints. | +| ORC-004 grammar | The tag history's fixed/random generator, bounds, exclusions, and replay remain as recorded above. New lost-tag, changed-shape, malformed, empty-first/later, and idle cases are fixed receiving histories, not a new generated campaign. | +| ORC-005 path/observation | Real Collection, wrapper, and installed SDK see controlled HTTP; URLs, SQLite-adapter reads, durable rows, public rows, metadata, and acquisition outcomes are compared at named cuts. Native SQLite and live-service behavior are not inferred. | +| ORC-006 calibration | Original full-mode dispatch and the stale-hydration mutant fail at intended comparisons. The serialization mutant was not run; its claim remains bounded to the SDK probe and named order witness. | +| ORC-007 campaigns/replay | Tag fixed/random campaigns pass, and the saved seed/path was replayed directly after the repair. The new receiving examples do not claim generated-history coverage. | +| ORC-008 model state | The provider Map and durable cache are distinct because a later empty demand distinguishes them. The tag driver's scoped-recovery marker only selects the expected public/durable comparison under its generated history. | +| ORC-009 vocabulary | Collection, source snapshot, persisted restore, demand, applied receipt, public snapshot, and truncate replay retain glossary meanings. Local-only Collection clearing is not called an Electric source truncate. | +| ORC-010 failure fidelity | Existing lifecycle helpers preserve cleanup diagnostics. The stale-hydration mutant fails at the local-read assertion, not timeout or setup. | +| ORC-011 second formulation | The real SDK/HTTP path checks the controlled tag mock's capability assumption and the separate A/B cache model challenges a transport-only repair. Live-provider emission remains unverified. | +| ORC-012 review evidence | This continuation records new authority, predictions, RED, mutant outcome, GREEN, weight, and unresolved boundaries; the original ledger and earlier head-pinned findings remain intact. | +| ORC-013 boundary witnesses | Compatible untagged resume contrasts lost tags; changed shape and malformed evidence enter the same scoped path; empty-first/later and zero-demand cuts reject stale readmission and eager work. | +| ORC-014 handoff | Installed SDK receives the controlled provider frames. Live Electric tag emission, native Expo/SQLite, cross-tab invalidation during quarantine, and partial updates before a scoped baseline remain assigned in the coverage map. | + +This evidence covers the controlled two- and three-launch on-demand paths, +bounded tag histories, full-mode fallback, and named public/durable observations. +It does not close arbitrary provider schedules, live service behavior, native +storage, or cross-tab recovery. No reachable in-scope counterexample is known +within the executed histories; the coverage map retains the other cells. + +## Prep-PR review: coordinator authority during scoped recovery + +Review continuation — 2026-10-07. Reviewed implementation head: `6f3149bf6`. +The review repair and its witnesses are committed at `5579d3005`. +The prep-PR review found a P1 counterexample to the scoped-source contract: +after an applied Electric subset snapshot, a coordinator notification could +reload an active paginated subset from empty SQLite cache and remove the public +source row. The old oracle omitted this coordinator cut. The review also found +one valid simplification: compute the on-demand changes-only decision once and +use it for both stream mode and the default offset. Two other simplification +ideas lacked a lifecycle witness and were not applied. The complete source-order +ledger is retained in the task's prep-PR review evidence. + +The persistence oracle now crosses targeted, paginated, full-reload, +sequence-gap, and durable-reset notifications after applying a source row. It +fences coordinator processing and compares the public row with the source +model. The controlled installed-SDK receiver adds the paginated coordinator +cut after its scoped A snapshot. On the reviewed implementation, the targeted +and paginated cases failed at the post-invalidation public-row assertion. With +the repair, all five persistence cases and the installed-SDK receiver pass. +Removing each of the three recovery guards separately made a corresponding +controlled history fail; removing them together made all three scoped restart +cases fail at the SDK receiver's coordinator checkpoint. These are finite +mutant kills at named observations, not a claim about all coordinator schedules. + +The repair keeps coordinator durable-cache notifications from hydrating, +truncating, or replacing public source rows while scoped recovery is active. +Electric source changes and demanded snapshots retain public authority in that +interval. Outside scoped recovery, coordinator invalidation follows its +existing path. Unknown and missing key-set evidence were also checked with a +held Node SQLite metadata read: startup avoids a full baseline read, leaves +cached rows unpublished, and applies only a demanded source snapshot. + +Validation before merging the newer main branch: 647 Electric package tests +passed; the persistence oracle passed 645 tests with one existing TODO; the +persistence TypeScript check and changed-file ESLint passed with existing +warnings. The changed files passed formatting after the final oracle edit. +The coverage map names remaining native SQLite, real cross-tab transport, +live-service, and wider scheduling witnesses. This review closes the observed +controlled coordinator counterexample, not the whole scoped-recovery class. + +## PR review: scoped-clear readiness + +CodeRabbit review 5445467738 examined PR #2069 at `8a1f590e0`. It identified +an initial-readiness error: the scoped cache clear called core `truncate()` +without an option, and core marks a truncated Collection ready by default. +Clearing uncertified cache rows supplies no Electric source evidence. The +wrapper now passes `markReady: false` through its internal sync control, so the +Collection remains loading until the source marks it ready. + +The new persistence owner reaches that wrapper call before source readiness. +On the reviewed implementation, the public status assertion received `ready` +instead of `loading`. With the repair, it observes loading after the clear and +ready after the source signal. The installed-SDK restart receiver adds the same +pre-response observation for lost tags, changed shape, and malformed resume +state. A temporary old-call mutant failed all three at that status assertion. +The core truncate-readiness owner separately crosses loading, ready, error, +and held optimistic state with explicit readiness intent. These observations +enforce the initial scoped-clear law in the controlled wrapper and SDK paths. +Native Expo and a live Electric service remain outside those paths. + +The review also proposed waiting only for acceptance of the scoped truncate. +Its suggested deadlock requires core to hold that truncate behind a persisting +optimistic transaction. Core instead applies committed truncates immediately, +including their preceding committed work. A held-handler wrapper cut confirmed +that `startScopedRecovery()` fulfilled before the handler released on the +reviewed implementation. The proposed cycle was not reproduced, so this PR +keeps the existing applied wait. Subset success still waits for applied rows. +The review's two documentation findings were fixed: the historical audit no +longer exposes a local path or a personal hiring judgment, and the raw-issue +ledger now cites its committed filename. + +After this review repair, the persistence oracle passed 647 tests with one +existing TODO. The Electric package passed 647 tests. The core truncate-readiness +and persistence-capability tests passed 69 tests. The review's four actionable +items are accounted for as three fixes and one refuted deadlock claim. These +finite checks do not establish arbitrary provider or native-host scheduling. diff --git a/docs/contributing/oracle-reviews/issue-2058-paced-serialization.md b/docs/contributing/oracle-reviews/issue-2058-paced-serialization.md new file mode 100644 index 0000000000..c559e38bfa --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2058-paced-serialization.md @@ -0,0 +1,79 @@ +# Paced persistence serialization review + +Reviewed product commit: `632e46944241e64d91f46fd874de90d70d226cf8`. +Base: `9e8ed997885fb46ac98ec85906f4ca4f662e7cce`. The review record and +changeset follow that product commit. The primary executable owner is +`packages/db/tests/paced-mutations-oracle.test.ts`. + +## Contract and result + +The paced mutations guide promises at most one pending and one persisting +transaction for debounce and throttle. The queue strategy promises serial +persistence. Issue #2058 demonstrated two concurrent backend handlers for +debounce and throttle. A manual rollback exposed the same overlap in queue: +the public receipt rejected before its active handler returned, so the next +handler started early. + +The repair separates admission from persistence. A rejected paced call owns an +isolated transaction, while admitted calls can merge into the one pending +transaction. Debounce and throttle reserve a leading edge before `onMutate` +can reenter. A shared serial pacer waits for actual `Transaction.commit()` +completion, rather than receipt settlement, before it starts another handler. +Queue uses that same completion boundary. A manual rollback still rejects its +public receipt immediately. + +## Law, histories, and observation cuts + +The independent two-call rule starts the successor only after both its timer +edge and the first handler's release. The oracle enumerates 24 successful +hold/release histories across debounce and throttle. It crosses leading, +non-leading, and omitted-leading options, with release before, at, and after +the edge. The model computes `max(eligibleAt, releaseAt)` without importing +the production scheduler. The immediate handoff is the chosen pacing policy; +the guide's serialization promise alone supplies the lower bound. + +The production driver uses real Collections, strategies, and transactions. +It controls handler promises and a virtual clock. At admission, the timer +edge, handler release, and final drain, it checks every handler start, active +handler count, transaction state, receipt outcome and identity, and optimistic +rows. Adjacent histories distinguish a renewed debounce quiet period, throttle +spacing from actual starts, and dropped-call admission isolation. The +settlement grammar crosses both factories, leading modes, same/distinct keys, +first-handler success or rejection, and pending cancellation. Manual rollback +crosses debounce, throttle, and queue. Nested `onMutate` checks one merged +leading write, no stale timer, and a later usable admission. + +Original production fails 18 of the 24 held-edge histories at the concurrency +assertion. The six release-before-edge controls pass. Before the final repair, +manual rollback fails at the active-handler assertion for each of the three +strategies. Nested admission fails for debounce and throttle. These are +assertion kills at the promised intermediate cuts, not timeouts. The repaired +owner and ordinary pacing tests pass 136/136 on the reviewed base. Focused +TypeScript, ESLint, Prettier, and the DB package build pass. The build reports +non-fatal missing Expo example configuration warnings. + +## Oracle guide audit + +| Requirement | Result for this owner | +| --- | --- | +| ORC-001 | The guide supplies serialization and timing authority. The opening oracle prose and coverage map state finite limits. | +| ORC-002 | Expected times come from timer eligibility and handler release, not a production pacer or classifier. | +| ORC-003 | Opening prose states the contract; nearby model, action grammar, production driver, and assertions remain distinct. | +| ORC-004 | The 24-cell matrix is bounded enumeration with explicit release offsets. It does not claim random reach. | +| ORC-005 | Real production entry points reach held handlers. The recorder retains ordered starts and intermediate receipts. | +| ORC-006 | Original overlap, early release after rollback, and duplicate leading starts fail their intended assertions. | +| ORC-007 | No important random property is introduced. Named finite tests provide direct replay. | +| ORC-008 | The two-call expected rule is a prerequisite calculation, not a copied production state machine. | +| ORC-009 | Model `pendingIds` combines optimistic mutations. Handler completion and public receipt settlement stay separate. | +| ORC-010 | Cleanup releases held promises and preserves a primary assertion when cleanup also fails. | +| ORC-011 | A second equivalent formulation is unnecessary for the max-prerequisite rule. The issue's direct two-call probe provides a separate public concurrency observation. | +| ORC-012 | This record states the bounded closure, kills, checks, limits, and each guide outcome for the reviewed product commit. | +| ORC-013 | Edge-before/at/after releases, renewed quiet, delayed actual-start spacing, and nested admission reject nearby wrong designs. | +| ORC-014 | The claim is limited to controlled DB source execution. No real-server, framework, or npm handoff is claimed. | + +The repaired source paths have no known reachable counterexample in the named +grammar. The coverage map owns provider echo, cleanup during a held handler, +longer arbitrary bursts, held default options, and multiple-manager histories. +Those gaps limit the evidence; a passing finite suite does not prove every +possible history. Server ordering, framework scheduling, and published npm +behavior require separate receiving witnesses. diff --git a/docs/contributing/oracle-reviews/issue-2069-cache-claim-gap-audit-575fb9473.md b/docs/contributing/oracle-reviews/issue-2069-cache-claim-gap-audit-575fb9473.md new file mode 100644 index 0000000000..ec6b0de718 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2069-cache-claim-gap-audit-575fb9473.md @@ -0,0 +1,141 @@ +# Cache-claim oracle gap audit + +Reviewed code commit: `575fb9473dd7d8eccbe469d0831dc9f71cffddd4`. +This review covers the oracle and coverage-map changes in that commit. No +production code changed. Three independent oracle reviewers challenged the +new receiving histories. Their false-green findings about exact expiry, +retained tombstones, demand settlement, request predicates, release identity, +transient publication, and timer-driven recovery were repaired before this +reviewed commit. The final reviewers found no further issue within the bounded +histories below. This is not a claim that every cache-eviction interleaving is +closed. + +The [glossary](../glossary.md) defines a persisted cache claim as one sync +run's expiring authority over one persisted cache generation. The approved +[on-demand recovery contract](../../collections/electric-collection.md) +requires an expired run to reload demanded subsets from the source while a +later live claimant retains its current cache. Retired storage becomes +collectible after its last claim releases or expires; collection occurs on a +cache-claim operation, not on clock passage alone. + +## Histories and observations + +The SQLite startup witness pauses one run **before dispatching** a real resume +read. A later peer obtains a live claim on the current generation. The first +run's claim then expires. Its real SQLite read rejects, and the persisted +wrapper binds private storage before it completes a demand from a fresh source +row. The peer keeps its public row, durable row, resume metadata, and current +head. The separate managed-claim-read property owns the claim check **inside** +the SQLite transaction; the composed witness does not claim that later cut. + +The retired-storage grammar has two independently controlled retired claims: +`release`, `expire`, or `renew`. It varies zero or two rows and collection at +`1170`, `1190`, `1209`, `1210`, `1229`, or `1230` on a clock whose renewed +expiries are `1210` and `1230`. Its independent rule retains a retired +generation exactly while its claim is live; the current head remains retained. +At the collection checkpoint it compares claim IDs and physical IDs, seeded +rows, tombstones, resume metadata, expected keys, physical row and tombstone +tables, native indexes, and all generation-scoped catalog entries. Two +generations with different expiry times are necessary: a single retained bit +would incorrectly conflate them when one expires and the other remains live. + +The persisted-wrapper notice grammar varies an absent, commit, or reset old +notice; old and new row versions on either side of one another; and an absent +or present new notice. A controlled coordinator holds an explicit constrained +demand and the new source receipt behind a persisting optimistic transaction. +It compares the exact physical storage ID and predicate of the source request, +the pending demand before application, the absence of any transient +notice-only row in the public publication trace, the fresh row after +application, and release of the exact `(storage ID, options)` acquisition only +after the source receipt applies. Old notices cannot authorize new-generation +work, regardless of their row version. + +The Browser receiver supplies the controlled notice premise on one real-host +schedule. Two Chromium contexts share OPFS, Web Locks, and BroadcastChannel. +A real source commit causes the Browser coordinator to emit a physical-ID +notice. The first context holds the delivered callback, advances its host +clock beyond its own claim expiry but within the warm peer's claim, and then +releases the callback. It must reload privately. The warm peer keeps its +public and durable rows and current head. An explicit demand remains pending +until a fresh source receipt applies. The real renewal timer is outside this +fixed test window, and the test verifies that refetch has not started before +the held callback is released. + +## Grammar controls and calibration + +The retired-storage fixed corpus reconstructs both live generations, release +of either, expiry of both, zero and two rows, and the immediately-before and +at-expiry cuts for both renewed claims. Removing either claim action axis loses +independent reachability; removing row count loses empty versus populated +storage; removing the clock axis loses the half-open threshold. The bounded +grammar excludes concurrent writes and a fourth rotation. It does not silently +discard a generated history. The notice property's fixed corpus reconstructs +queued commit and reset callbacks with old versions both below and above the +new version. Its absent-old and absent-new generated cases are work-count +controls. A callback delivered only after its old subscription is removed is +outside this grammar. + +Both important generated properties use the same driver, recorder, and check +in equal-budget fixed and unseeded campaigns: `sqlite-resume.retired-storage` +uses seed `2069` and 24 runs; `persistence.generation-notice` uses seed `2069` +and 24 runs. Neither uses `fc.commands`. The registry accepts a seed and shrink +path for direct replay. With the temporary inclusive-expiry mutant, the +retired-storage property shrank to seed `2069`, path `3:1:1`, with old claim +renewed, middle claim released, zero rows, and collection at exact expiry +`1210`. Direct replay ran only the requested property, failed after one test +at the claim-identity comparison, and passed after production was restored. +The prior [audit](issue-2069-cache-generation-oracle-audit-53ded62d8.md) +records direct replay of the notice property at seed `2069`, path `1:1:1`. + +Temporary wrong designs were restored after each check. These were assertion +failures at their intended comparison, not setup failures or timeouts: + +| Wrong design | Rejected observation | +| --- | --- | +| Treat claim expiry as inclusive (`<` deletion instead of `<=`) | Extra claim survived at `1210`; direct replay failed at exact claim identities. The fixed boundary also rejects the retained physical table. | +| Rotate the current head for an expired run | Warm peer's current-head ID changed. | +| Omit retired metadata collection | Generation-scoped catalog comparison retained an obsolete entry. | +| Release a scoped refresh before its receipt applies | Pending-demand and exact release-timing comparisons failed. | +| Release refresh work against the logical ID | Exact `(physical storage ID, options)` release comparison failed. | +| Return from a demand before awaiting its coordinator request | Demand-settlement comparison failed while the source receipt was held. | +| Drop the native OPFS notice callback | Browser receiver never entered refetch; `refetchEntered` assertion failed. | + +The notice fixture originally matched the `where` expression by object +identity. A legal cloned predicate would have timed out before the intended +comparison, so that trigger now matches the request shape and the separate +deep-value assertion checks its predicate. This is a fixture repair, not a +product-law change. + +## ORC-001–014 review + +| Requirement | Outcome at the reviewed code commit | +| --- | --- | +| ORC-001 authority and limits | Applicable. The glossary and approved on-demand recovery contract define claim authority, private reload, and warm-peer retention. Each owner states its bounded checkpoint. | +| ORC-002 independent judgment | Applicable. The retired-storage model computes half-open reachability from authored times and actions. The notice model derives source and demand obligations from physical identity and receipt order, without importing the production classifier. | +| ORC-003 literate responsibilities | Applicable. Each changed owner states the law before mechanics and explains its model, legal histories, production entry, public or durable observations, and refinement checkpoint beside the code. | +| ORC-004 grammar controls | Applicable to the two generated properties. Reconstruction, ablation, range, and exclusion are recorded above. The composed startup and native OPFS histories are fixed receiving cuts, not claims of generated coverage. | +| ORC-005 production path and observation | Applicable. The real SQLite adapter and persisted wrapper execute the startup and collection histories. The wrapper handles notices. The OPFS test uses the real Browser coordinator and two native contexts. Their specified observations were reached in passing runs. | +| ORC-006 checker calibration | Applicable. The hostile designs above were rejected at the named comparisons. The inclusive mutant's seed/path was replayed directly. | +| ORC-007 fixed/random/replay | Applicable to both important properties. Equal-budget fixed and unseeded campaigns ran. Direct replay was checked for the new retired-storage property here and for the notice property in the prior audit. | +| ORC-008 stateful-model minimality | Applicable to the new reachability model. Its two retired-claim states remain separate because expiry of one can change the next collection result while the other stays live; rows and catalogs are observed contents, not extra authority states. No production state machine was copied. | +| ORC-009 vocabulary mapping | Applicable. Claim, generation, sync run, demand, source request, and scoped recovery use glossary terms. Model clock cuts represent the adapter's controlled host clock; the controlled coordinator is a provider of source receipt ordering. | +| ORC-010 failure fidelity and cleanup | Applicable. SQLite cleanup preserves the original assertion and attaches cleanup failures. The notice driver preserves its primary mismatch through bounded teardown. Browser cleanup reports secondary failures without erasing the primary failure. | +| ORC-011 independent second formulation | Applicable. The controlled wrapper fixture alone cannot establish physical SQLite isolation or native notice delivery. The composed SQLite and Browser OPFS receivers use real physical storage; OPFS supplies the host notice premise. A live Electric provider on that same schedule remains unproved. | +| ORC-012 review evidence | Applicable. This versioned record identifies the exact reviewed code commit, outcomes, hostile controls, and unresolved cells. It makes no universal bug-class closure claim. | +| ORC-013 boundary witness | Applicable. The `1209/1210` and `1229/1230` histories distinguish half-open expiry from inclusive expiry, with a killed mutant. The notice history distinguishes exact physical identity and receipt settlement from plausible wrong release and early-demand designs. | +| ORC-014 controlled-premise handoff | Applicable. The Browser OPFS receiver gets a matching physical-ID notice from the real coordinator over native BroadcastChannel and checks the fresh receipt before demand settlement. Arbitrary native scheduling and Electric delivery in the same history remain assigned below. | + +Validation on the reviewed code: both SQLite oracle owners passed (809 active +tests, one existing todo); the OPFS two-context history passed four runs, +including the final post-review run. Both affected package typechecks passed. +Changed-file ESLint reported zero errors; Prettier and `git diff --check` +passed. The pre-commit hook's dependency install had previously received a +private-registry 403, so the verified code commit bypassed that hook. + +The [coverage map](../oracle-coverage.md) retains these unproved cells with +owners: arbitrary native notice scheduling and a live Electric provider in +the same two-context history belong to the Browser and Electric receiving +owners; release retry after a timed-out old-leader RPC belongs to the Browser +coordinator owner; claimless direct-adapter access to a collected physical ID +belongs to the SQLite adapter owner. The fixed receivers do not imply those +histories have passed. diff --git a/docs/contributing/oracle-reviews/issue-2069-cache-generation-oracle-audit-53ded62d8.md b/docs/contributing/oracle-reviews/issue-2069-cache-generation-oracle-audit-53ded62d8.md new file mode 100644 index 0000000000..bf1516219a --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2069-cache-generation-oracle-audit-53ded62d8.md @@ -0,0 +1,120 @@ +# Cache-generation oracle audit + +This is a self-review of the oracle expansion. +Reviewed code commit: `53ded62d8ef70ccd63b75d0e6dbc0a964f14b17c`. +This record covers the oracle and coverage-map changes in that commit. It does +not claim that the whole cache-eviction bug class is closed. No production code +changed in the reviewed commit. + +The [glossary](../glossary.md) defines a persisted cache claim as one sync +run's expiring authority over one persisted cache generation. The approved +on-demand recovery contract in the [Electric collection guide](../../collections/electric-collection.md) +requires a run that loses that authority to reload its demanded subsets. The +SQLite owner checks transaction-time admission; the persisted owner checks +physical-ID notice routing; the Browser owner checks a warm peer and an expired +run through a real SQLite database and a controlled coordinator wire. + +## Generated-history controls + +The managed-claim read grammar fixes an initial claim at clock 1000 with a +100 ms lifetime. A peer claims the same physical ID 10–80 ms later. The held +read uses clock cuts -9, -1, 0, +1, and +9 ms around the first claim's expiry. +The peer remains live at every generated read cut. The model uses half-open +claim intervals and computes both expiries from these authored inputs, rather +than from the adapter's returned expiry. It varies `includeRows` and zero to +three seeded rows. Six fixed histories reconstruct the reported second-check +race and the exact-expiry boundary with both row-inclusion options. + +- **Ablation:** Removing the clock cut loses expiry and equality; removing + `includeRows` loses the position-only no-row-read promise; removing row count + loses empty and multiple-row membership; removing peer delay loses the + independent later claim interval. +- **Range:** The adjacent clock cuts are -1, 0, and +1 ms; 0 and 3 rows bound + the generated row count. A peer acquired after the held read, or expired by + its own read, is excluded from this grammar rather than silently skipped. +- **Checkpoint:** The real `loadResumeSnapshot` transaction is held before its + claim query. An expired read rejects with no generation-specific SQL beyond + claim validation. A live read returns the modeled rows and metadata, while + the peer reads the same exact durable rows and metadata. A harmless global + catalog query before validation is allowed. + +The generation-notice grammar fixes rotation from one physical ID to another. +It varies an absent, commit, or reset old notice; old row versions 1 and 10; +new row versions 1 and 3; and an absent or present new notice. The four fixed +histories reconstruct queued commit/reset delivery with the old version below +or above the new version. An old version has no semantic effect when no old +commit exists; a new version has no effect when the new notice is absent. + +- **Ablation:** Omitting old notices removes the queued-callback case. Omitting + the absent-old control loses the rotation-only work baseline. Omitting the + absent-new control permits unexplained later work. Omitting either version + order permits comparison by row version instead of physical ID. Omitting + commit or reset loses one callback path. +- **Range and exclusion:** Versions 1/10 and 1/3 cover the two orderings. + The grammar queues any old notice before rotation binds; delivery to an old + subscription created only after unsubscribe is outside its legal histories. +- **Checkpoint:** After the queued callback crosses the wrapper's apply mutex, + the active subset has exactly its one rotation request and no retired public + row. A new notice requests additional source work against the new ID. This + stub records requests, not source evidence or a completed snapshot. + +Both grammars run the same driver and comparison with a fixed seed and a +random seed. The fixed seeds are 2056 (30 runs) and 2069 (24 runs). The package +oracle registry accepts direct seed-and-shrink-path replay for each property. + +## Hostile checks and replay + +Temporary source mutants were restored after each run. These outcomes are +assertion failures at the intended comparison, not setup errors or timeouts: + +| Wrong design | Observed result | +| --- | --- | +| Accept a claim at its exact expiry (`>=`) | Both fixed equality histories rejected the resolved read. Generated history shrank to `elapsedMs=0`, `includeRows=false`, `peerDelayMs=10`, `rowCount=0`. | +| Read collection metadata before transaction claim validation | Four expired fixed histories failed the generation-specific statement check. | +| Read key-set evidence before transaction claim validation | All six fixed histories failed the generation-specific statement-order check. | +| Scan rows for a live `includeRows=false` read and discard them | The live position-only fixed history failed its row-query count. | +| Process a queued retired commit after rotation without checking its physical ID | Both fixed commit histories failed because the old notice added source requests. Generated history shrank to old commit/version 1, new version 1, and no new notice. | + +A temporary harmless `sqlite_master` read before claim validation passed all +six fixed histories. It is outside the claimed generation and does not weaken +the first generation-specific statement check. The Browser receiving witness +also rejected an unconditional-current-head rotation mutant at its exact +head-ID comparison during the initial review campaign. + +Direct replay of the expiry mutant used property +`sqlite-resume.managed-claim-read`, seed `2056`, path `0:1`. It selected only +the random-or-replayed property and failed after one test with the same +counterexample. Direct replay of the retired-commit mutant used property +`persistence.generation-notice`, seed `2069`, path `1:1:1`. It likewise selected +only that property and failed after one test with the same counterexample. + +## ORC-001–014 audit + +| Requirement | Outcome at the reviewed code commit | +| --- | --- | +| ORC-001 authority and limits | Applicable. The glossary and approved on-demand recovery contract provide the claim and private-recovery law. Opening prose and the coverage map bound each owner. | +| ORC-002 independent judgment | Applicable. The SQLite model computes half-open intervals from authored clock/TTL inputs. The notice model expects work by physical identity and presence of a new notice; it does not call the production classifier. | +| ORC-003 literate responsibilities | Applicable. Each owner states the law before mechanics and places grammar, controlled driver, observations, and checkpoint beside the code. The notice owner claims request isolation, not source settlement. | +| ORC-004 grammar controls | Applicable to both new generated properties. Reconstruction, ablation, range, and exclusion are recorded above. Conditional axes are identified explicitly. | +| ORC-005 production path and observation | Applicable. Real SQLite executes the held resume transaction. The real persisted wrapper processes queued notices. Browser wa-sqlite compares two claims, both public snapshots, both claimed durable stores, and a distinct warm source demand after the expired run rotates. | +| ORC-006 checker calibration | Applicable. The table above records killed mutants and the accepted harmless-catalog variation. The old notice's extra source request fails at its own checkpoint. | +| ORC-007 fixed/random/replay | Applicable. Both generated properties have equal-budget fixed and random campaigns. Captured seed/path replays reproduced each selected mutant failure directly. Neither property uses `fc.commands`. | +| ORC-008 stateful-model minimality | Not triggered: these additions use small stateless predictions over bounded phase histories. The separate TLA+ authority model owns general state exploration. | +| ORC-009 vocabulary mapping | Applicable. `persisted cache claim`, `persisted cache generation`, `sync run`, `demand`, and `source request` follow the glossary. Model clock values stand for the adapter's controlled host clock; notice work counts stand for coordinator request calls. | +| ORC-010 failure fidelity and cleanup | Applicable. The SQLite driver preserves the primary error through database close. The generated notice driver bounds recovery/cleanup through `cleanupPersistedOracle`, which preserves an earlier assertion failure. The Browser receiver uses failure-preserving cleanup. | +| ORC-011 independent second formulation | Applicable. The controlled notice fixture shares one row Map across physical IDs, so it cannot prove exact durable isolation. The Browser receiver supplies real SQLite physical stores and a warm-peer observation. Native coordinator emission remains open below. | +| ORC-012 review evidence | Applicable. This versioned record names every requirement and is tied to the exact reviewed code commit above. The class remains open at the boundaries below. | +| ORC-013 boundary witness | Applicable. -1/0/+1 ms fixed cuts distinguish expiry from an inclusive threshold; the inclusive mutant failed. Old versions below and above new versions distinguish physical-ID routing from version-order routing. | +| ORC-014 controlled-premise handoff | Applicable. The Browser receiver commits the peer write to real SQLite before manually delivering a matching targeted notice. It proves receiver admission, not native coordinator emission. The missing host handoff is assigned below. | + +Validation on the reviewed code: 798 active SQLite oracle tests passed, with +one existing todo; all 30 Browser coordinator-oracle tests passed. Both +affected packages typechecked. ESLint reported no errors, and Prettier and +`git diff --check` passed. The pre-commit hook's dependency install received a +private-registry 403; the verified code commit bypassed that hook. + +Open cells remain in the [coverage map](../oracle-coverage.md): a composed +real-SQLite-to-wrapper startup rejection, native Browser coordinator emission +and multi-tab/OPFS scheduling, arbitrary notice interleavings, and retired +physical-cache reclamation. The SQLite resume owner, Browser coordinator +owner, and cache-generation lifecycle owner respectively retain those cells. diff --git a/docs/contributing/oracle-reviews/issue-2069-native-ddl-receiver-b4d57ced6.md b/docs/contributing/oracle-reviews/issue-2069-native-ddl-receiver-b4d57ced6.md new file mode 100644 index 0000000000..0066f95d48 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2069-native-ddl-receiver-b4d57ced6.md @@ -0,0 +1,82 @@ +# PR #2069 native SQLite index receiving audit + +Oracle code and coverage-map commit: `b4d57ced63d513dd0dcb794b8e8bb2a840577758`. +This record audits two fixed histories added to +`packages/db-sqlite-persistence-core/tests/sqlite-resume-snapshot-oracle.test.ts`. +It does not claim arbitrary host scheduling or a new product behavior. + +**Subsequent review correction:** At this recorded code commit, the durable +assertion polled after recovery settlement and checked the index name without +checking its table. Its cleanup path also discarded secondary failures. Those +claims below were too broad for this exact code commit. The +[follow-up audit](issue-2069-native-ddl-review-followup-b42b94b73.md) records +the repaired checkpoint, target-table assertion, and failure handling at +`b42b94b73ddba79a6a87fa93e71787dd126ea1bd`. + +## Law and receiving boundary + +The Collection's current index declaration determines whether a successful +index operation on its claimed persisted cache should leave a physical index. +The Collection index registry and the persisted wrapper's index lifecycle are +the authority. The controlled owner in `persisted-oracle.test.ts` already held +new-generation bootstrap ensure behind removal and held reconciliation removal +behind re-add. Its Map did not establish how SQLite's registry and physical DDL +behave in those orders. + +The new receiver uses the real persisted wrapper and `node:sqlite` adapter. It +holds the first new-generation `ensureIndex` before native DDL, lets removal +settle, then releases ensure. One history ends with no declaration; after +rotation the registry row must be removed and its named index absent from +`sqlite_master`. The neighboring history holds the reconciliation removal, +re-adds the same signature, lets the replacement ensure finish, then releases +that removal. After recovery, the registry row must be active and the named +physical index present. Both compare the Collection's public index metadata +at the same final checkpoint. The fixture serializes transactions on its one +`DatabaseSync` handle; the holds control the order of adapter calls, while +SQLite executes the actual registry writes, `CREATE INDEX`, and `DROP INDEX`. + +The two histories are independent of the wrapper's reconciliation branch in +their expected result: absence or presence comes solely from the final public +declaration. The observed registry state and `sqlite_master` are distinct +durable facts. The test checks that rotation actually changed the storage ID. + +## Hostile calibration + +Two temporary production mutants were run and then removed: + +- Returning early from reconciliation when a declaration was removed left the + late native index active. The no-declaration history failed its registry + assertion (`removed` was `0`, expected `1`). +- Returning success without re-ensuring a re-added declaration let the held + native removal win. The re-add history failed its registry assertion + (`removed` was `1`, expected `0`). + +Both were assertion failures at the intended durable checkpoint, not timeouts +or setup failures. The restored production code passed both histories and the +full SQLite persistence suite. + +## Oracle-guide audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | The Collection index declaration is the law's authority; these two orders and Node SQLite are the limit. | +| ORC-002 | Expected presence is derived from public index metadata, not the wrapper's reconciliation result or SQLite registry. | +| ORC-003 | The existing owner's opening law and local receiving comment name the contract, two legal orders, real production path, public/durable observations, and final comparison. | +| ORC-004 | Not applicable: the addition is two fixed histories, not a generated-history claim. | +| ORC-005 | The persisted wrapper reaches real SQLite DDL; storage-ID change, public declaration, registry row, and physical index are observed after recovery. | +| ORC-006 | Both temporary wrong designs failed at the registry comparison as recorded above. | +| ORC-007 | Not applicable: no important generated property was added or changed. The owner's existing generated campaigns remain in the package suite. | +| ORC-008 | Not applicable: the independent reference rule adds no stateful model state. | +| ORC-009 | The history uses Collection declaration, persisted cache generation, claim, and recovery in the glossary's senses. The transaction queue is fixture scheduling, not a product state. | +| ORC-010 | Every hold is released in `finally`; cleanup preserves the primary assertion failure if it also fails. No shrink or replay mechanism was added. | +| ORC-011 | Real `node:sqlite` registry and physical DDL provide a second formulation beside the controlled Map; other hosts remain unproved. | +| ORC-012 | This versioned record ties the audit and mutant outcomes to the exact code commit. | +| ORC-013 | The absent and re-added declarations are neighboring legal outcomes. Each rejects a plausible wrong final state at the durable checkpoint. | +| ORC-014 | The controlled call-order premise is reproduced at the wrapper-to-real-SQLite boundary. Native Browser, Electron, and other host scheduling are still separate receiving cuts. | + +Verification on the restored code: the focused owner passed 89 tests; the full +SQLite persistence package passed 965 tests with one existing todo. Package +TypeScript, changed-file ESLint, Prettier, and `git diff --check` passed. The +change added tests and contract documentation only; production code changed by +zero lines. The coverage map retains host-specific DDL scheduling as an open +limit rather than claiming exhaustive concurrency coverage. diff --git a/docs/contributing/oracle-reviews/issue-2069-native-ddl-review-followup-b42b94b73.md b/docs/contributing/oracle-reviews/issue-2069-native-ddl-review-followup-b42b94b73.md new file mode 100644 index 0000000000..c977386215 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2069-native-ddl-review-followup-b42b94b73.md @@ -0,0 +1,45 @@ +# PR #2069 native SQLite oracle review follow-up + +Reviewed oracle code: `b42b94b73ddba79a6a87fa93e71787dd126ea1bd`. +Two independent oracle reviewers read the preceding `b4d57ced6` receiver and +found three enforcement gaps, not production defects. This append-only record +corrects the earlier [native DDL audit](issue-2069-native-ddl-receiver-b4d57ced6.md). + +| Finding | Why the prior check could pass wrongly | Correction and observation | +| --- | --- | --- | +| Recovery-settlement cut | `vi.waitFor` could observe a late DDL repair after recovery returned. | The registry and physical index are read synchronously after the recovery promise settles. A later repair cannot satisfy this checkpoint. | +| Claimed physical table | Looking up `sqlite_master` by index name alone allowed the expected name on a different table. | The present-index branch now requires both that exact name and `tbl_name = createPersistedTableName(current physical ID, 'c')`. The absent branch requires no index with that name. | +| Failure fidelity | Construction and signature validation preceded the cleanup guard; `Promise.allSettled` and a broad cleanup catch discarded secondary failures. | Construction and validation now occur inside the guard. Cleanup releases both holds, observes recovery rejection with a bounded checkpoint, preserves distinct secondary failures, closes SQLite, and retains the original failure. | + +The reference relation remains: at recovery settlement, the final public +Collection index declaration predicts whether SQLite has an active index on +the current claimed row table. The two legal schedules still exercise removal +before late ensure and re-add before a held reconciliation removal. The first +mutant skipped removal after late ensure; the second skipped re-ensure after +re-add. Both failed the durable registry comparison at the earlier code commit. +The stronger current comparison additionally rejects a wrong target table or +a repair that occurs only after recovery settles by construction. A separate +wrong-table production mutant was not run; this remains a calibration limit. + +| Requirement | Outcome at the follow-up code commit | +| --- | --- | +| ORC-001 | Collection index declaration is authoritative for these two Node SQLite schedules; other hosts and arbitrary timing remain outside the claim. | +| ORC-002 | Expected presence and table identity come from the public declaration and current claimed storage ID, not the reconciliation branch or SQLite registry state. | +| ORC-003 | Opening and local prose identify the law, legal schedules, real adapter path, public/durable observations, and exact recovery-settlement comparison. | +| ORC-004 | Not applicable: two fixed histories, no generated grammar claim. | +| ORC-005 | The real wrapper and `node:sqlite` perform DDL; the final check sees the correct registry state and physical table at recovery settlement. | +| ORC-006 | The two reconciliation mutants failed at the intended durable assertion; no wrong-table mutant was run. | +| ORC-007 | Not applicable: no important generated property changed. | +| ORC-008 | Not applicable: no stateful reference-model state changed. | +| ORC-009 | Collection declaration, claim, persisted cache generation, and recovery follow the glossary; the transaction queue is test scheduling only. | +| ORC-010 | The guard covers construction through teardown, releases held calls, and preserves secondary recovery/cleanup failures beside the primary mismatch. | +| ORC-011 | Real SQLite DDL is the second formulation for the controlled Map schedule, with host-specific receiving work still open. | +| ORC-012 | This record names the exact corrected code commit, the findings, and outcomes for every numbered requirement. | +| ORC-013 | Absent and re-added declarations distinguish the two legal final states. Each reconciliation mutant fails its neighboring history. | +| ORC-014 | The controlled order is reproduced at the wrapper-to-SQLite boundary. Browser, Electron, and other host scheduling remain unproved. | + +The corrected owner passed all 89 tests. TypeScript, changed-file ESLint, and +Prettier passed. The full SQLite persistence package passed 965 tests and one +existing todo before this test-only correction; the package was not rerun +afterward because the corrected owner, containing every changed test, passed. +Production code remains unchanged. diff --git a/docs/contributing/oracle-reviews/issue-2069-receiving-gap-audit-87aead1be.md b/docs/contributing/oracle-reviews/issue-2069-receiving-gap-audit-87aead1be.md new file mode 100644 index 0000000000..387c1561f9 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2069-receiving-gap-audit-87aead1be.md @@ -0,0 +1,96 @@ +# Cache-rotation receiving-gap audit + +Reviewed code commit: `87aead1be5730481152a10849008287755c0ff16`. +This commit adds oracle histories and a Browser receiving path; it changes no +production code. The accepted cache-claim and subset-demand laws come from the +[glossary](../glossary.md), the [Electric on-demand recovery contract](../../collections/electric-collection.md), and the Query Collection's +[shared-cache contract](../../collections/query-collection.md). The fixtures +judge those laws at named boundaries. They do not establish every cache-eviction +interleaving or turn an unsupported direct-adapter call into a product promise. + +## Histories and observations + +The Browser coordinator owner accepts one remote subset acquisition, then drops +its old leader's release response **after** the owner unloads. Leadership +transfers while the caller's actual RPC timer remains pending. After the timer +expires, production retries and the public release settles. At that checkpoint +the old owner has unloaded once, the new owner has loaded and unloaded zero +times, and no outbound acquisition remains. The model is one exact acquisition +lease with one release obligation; a response lost in transport cannot revive +the retired demand. Web Locks and BroadcastChannel are deterministic seams, +so this is a coordinator result rather than proof of native scheduling. + +The Query owner gives two on-demand Collections one QueryClient with distinct +exact keys and overlapping prefixes. The first claim starts at 1000 and expires +at 11000. The second starts at 5000 and remains live at the 11001 checkpoint. +A disjoint demand makes the first Collection rotate and fetch fresh rows. The +independent two-claim rule predicts no change to the warm Collection. The driver +uses real QueryClient and Node SQLite, then compares the first public rows and +the second public row, Query cache entry, durable row, current storage ID, and +fetch count. This history does not redefine the documented effect of explicit +cleanup on another consumer of the *same exact* Query key. + +The Browser OPFS owner reuses its native two-context notice history with an +installed Electric SDK in the expired tab. Controlled HTTP supplies one initial +up-to-date message, the old subset row, a held old subset request, and a fresh +subset response held after private rotation. The warm tab's deterministic +source commits a durable peer row; the real coordinator emits and native +BroadcastChannel delivers the physical-ID notice. At the held notice cut, the +expired claim has passed its half-open expiry and the warm claim remains live. +Before delivery, no refetch starts. After delivery but before the fresh response, +the explicit demand remains pending, the expired public and private durable +cache are empty, and the warm peer retains its public and durable rows under +the unchanged head. After the response, the demand fulfills and only the fresh +row appears in the expired public and durable cache. This receives the installed +SDK and native host premises together, with controlled HTTP rather than a live +Electric service. + +## Checker calibration + +These temporary mutants were restored after each run. Every killed mutant +reached the stated assertion, not setup failure: + +| Wrong design | Result | +| --- | --- | +| Release does not set `releaseRequested` before old-leader timeout | The new owner unexpectedly loaded the retired demand. | +| Rotation removes every Query entry matching its base prefix | The warm Collection's distinct Query cache entry was absent. | +| The held native notice callback is dropped | The Electric/OPFS history failed at the refetch-entry poll. | + +A first Browser fixture mutant changed the *ordinary* callback path while the +history held and later invoked a different callback. It survived. That outcome +was an unreached-path calibration result, not evidence that the notice was +unnecessary. Mutating the held callback itself produced the failure above. + +## ORC-001–014 review + +| Requirement | Outcome | +| --- | --- | +| ORC-001 authority and limits | Applicable. The three existing contracts above authorize the expected results. Each owner states its bounded path. Claimless direct-adapter access and live Electric service delivery remain open. | +| ORC-002 independent judgment | Applicable. One lease's release, two independently timed claims, and source-row replacement predict the results without importing the production retry, Query ownership, or cache-rotation classifiers. | +| ORC-003 literate responsibilities | Applicable. Each edited owner explains law, small independent rule, legal history, production path, observation, and checkpoint beside the test. The Browser fixture names its controlled provider. | +| ORC-004 grammar controls | Not triggered. All three additions are fixed receiving histories; no generated grammar changed. | +| ORC-005 production path and observation | Applicable. The coordinator executes its real RPC timer; Query uses real QueryClient and SQLite; Playwright runs two Chromium contexts over native OPFS and BroadcastChannel with the installed SDK. Call counts, public rows, cache entries, durable rows, and demand settlement are compared at the named cuts. | +| ORC-006 checker calibration | Applicable. The table records three assertion failures and the separate unreached-path survivor. | +| ORC-007 fixed/random/replay | Not triggered. No important generated property was introduced or changed. Existing generated owners remain separate. | +| ORC-008 stateful-model minimality | Not triggered. The fixed histories use existing lease and cache-claim concepts; no reference-model state was introduced or collapsed. | +| ORC-009 vocabulary mapping | Applicable. Persisted cache claim and generation, acquisition lease, demand, sync run, provider session, applied receipt, and public snapshot follow the glossary. The controlled HTTP response is provider input, not publication. | +| ORC-010 failure fidelity and cleanup | Applicable. The OPFS receiving test retains a primary mismatch separately from page cleanup failures. The existing coordinator and Query owners release their test resources in teardown. No shrinking or failure serialization was added. | +| ORC-011 independent second formulation | Applicable. Native OPFS and installed SDK receive premises previously supplied separately by controlled coordinator and Node SQLite tests. The controlled HTTP service is still a narrower formulation than a live Electric service. | +| ORC-012 review evidence | Applicable. This record is tied to the reviewed code commit and lists every requirement, mutant outcome, and remaining boundary. It makes no universal bug-class closure claim. | +| ORC-013 distinguishing boundary witness | Applicable. The Query history puts one claim past expiry and one later claim before expiry at the same clock cut; the broad-eviction mutant fails. Existing SQLite expiry grammar owns exact-at-expiry calibration. The coordinator history reaches actual RPC timeout and rejects demand replay. | +| ORC-014 controlled-premise handoff | Applicable. The Browser receiver gets a physical-ID notice from real native delivery and an installed SDK source snapshot. The remaining live-service and arbitrary-schedule handoffs are explicit below. | + +The Browser coordinator oracle passed all 166 tests, and the Query ownership +oracle passed all 173 tests. Both OPFS receiving variants passed. Both affected +packages typechecked. Changed-file ESLint had no errors (19 pre-existing +warnings); Prettier and `git diff --check` passed. The branch's pre-commit +hook had previously tried a private-registry install and received 403, so the +verified code commit bypassed that hook. + +The [coverage map](../oracle-coverage.md) retains the remaining cuts: arbitrary +native notice schedules, a live Electric service in this same two-tab history, +custom Query provider restarts and arbitrary retention schedules, and direct +claimless access after a collected physical ID loses its catalog identity. +The latter has a scratch reproduction of orphan registry and row recreation; +rejecting that path while allowing arbitrary eager Collection IDs needs a +durable identity distinction and therefore a separate design decision. diff --git a/docs/contributing/oracle-reviews/issue-2069-review-51563046.md b/docs/contributing/oracle-reviews/issue-2069-review-51563046.md new file mode 100644 index 0000000000..87d63086f9 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2069-review-51563046.md @@ -0,0 +1,86 @@ +# PR #2069 review at 51563046 + +Reviewed code: 51563046ccac1e739250601580a795fa4400d841. +Repair code: f2bf482b37508302a8df851ed16dabecaff4c82e. +The external xhigh review supplied 15 numbered findings and three separate +test-gap claims. Its 15th finding contained three independent cleanup claims. +The task-local source-order ledger preserves all 20 resulting items and the +review's original wording. This record states the executable law audit for the +repair commit. It does not claim that all cache-generation design work is done. + +## Laws and decisive observations + +| Law and authority | Original failure and missing oracle cut | Repair and enforcing observation | Limit | +| --- | --- | --- | --- | +| An expired persisted cache claim cannot authorize a read, write, or public row; an active on-demand demand reacquires from its provider after private rotation. The glossary defines claims and generations, and the cache-eviction design authorizes rotation. | A real QueryClient with Node SQLite entered terminal error without a restart hook. A separate startup schedule rotated before Query sync entry yet published old QueryClient data with zero source fetches. The prior Query owner covered demand ownership but omitted the persisted-startup boundary. | Query now retires callbacks, gates old fetch results, restarts active acquisitions, and reads a startup-rotation capability. The Query owner checks warm reuse with zero fetches, private startup rotation with a fresh fetch, old peer fetch success and error, active reacquisition, overlapping rotations, public rows, caller settlement, and the tested run's claimed SQLite rows before and after a stale fetch finishes. Original schedules were RED; repaired schedules are GREEN. | Fixed real QueryClient/Node SQLite schedules. Two Collections sharing one QueryClient, arbitrary peer timing, and two successive private startup rotations have no composed Query receiver. Custom sources still need an explicit restart capability. | +| A cache-claim read that expires during an awaited SQLite operation must be discarded before publication; unrelated live storage errors remain failures. The claim law and persistence adapter transaction boundary authorize this distinction. | Hydration and startup second-check rejections became terminal. The persistence oracle lacked the after-read expiry transition and a second successive startup expiry. | Persistence retries private claim recovery before source entry or reload, then admits only the replacement generation's rows. Controlled second-check histories were RED then GREEN; a real SQLite owner rejects expired claimed reads. | The exact startup second-check rejection schedule is controlled; a real SQLite receiving test for that schedule remains open. A durability rejection after core publication remains fail-stop. | +| A provider-session restart must settle demands and retire old callbacks before a rotated cache accepts new work. The glossary separates provider session, sync run, demand, and applied receipt. | A failed Electric restart left later demands in a microtask loop. Two rotations shared the wrong gate and admitted a middle-session callback. The earlier Electric oracle checked only gate timing. | Electric retains restart failure, rejects later demand while the run remains active, gives each rotation its own gate, and rejects retired callbacks. Descriptor and installed-SDK tests check settlement, final fresh demand, and old callback exclusion. The failed-restart and overlap schedules were RED then GREEN. | No one test composes two rotations, installed SDK, real SQLite, and public/durable rows. A stalled physical SDK snapshot can still block later requests. | +| A declared Collection index must govern successful late index work on the same claimed storage, including remove-before-ensure and remove-then-readd. The Collection index registry is the authority. | A controlled reverse-order schedule completed removal before held bootstrap ensure; the durable index reappeared while the Collection had no declaration. The earlier oracle released ensure first, so it was false green for this order. | Bootstrap, coordinator, and dynamic index paths recheck the current declaration after awaited work. The reverse order and readd-during-removal tests check both declaration and durable adapter state; the reverse order was RED then GREEN. | The durable fixture is a controlled adapter Map. Native SQLite DDL under the same reverse completion order remains an unproved receiving cut. Failed index I/O remains best-effort. | +| Claim renewal preserves a still-valid claim without writing on every foreground operation; transient timer errors do not independently prove claim loss. The claim expiry and adapter admission checks define the safety boundary. | A deterministic six-commit/six-load history increased renewals from 2 to 20 while expiry was distant. A transient timer error also made the Collection terminal. | Renewal is due only near expiry; the timer retries a transient error and each durable operation still checks its claim. Call-count and timer histories were RED then GREEN, with near-expiry renewal as the neighboring control. | Source-commit durability errors still reject their receipt; this is not a promise to swallow SQLite failures. | + +## Lossless disposition + +| ID | Verdict and action | Durable value | +| --- | --- | --- | +| F01 | Confirmed for Query and fixed there; combined item remains confirmed-open for custom managed sources and legacy logical-table collection. | Custom sources need a restart contract; pre-generation storage needs a cross-version reclamation policy. | +| F02 | fixed-now | Persistence second-check and startup claim-expiry histories. | +| F03 | fixed-now | Failed Electric restart followed by another demand must reject finitely. | +| F04 | fixed-now | Unmanaged Electric adapters take full-log replacement; managed scoped recovery requires a claim. | +| F05 | fixed-now for transient timer failure; the review overstated source-commit recovery. | Distinguish expiry from live I/O failure at each durable boundary. | +| F06 | refuted for the stated public StrictMode release path: core aborts before unload, and the held-claim probe started no source load after release. | Direct signal-less internal calls are a separate scope limit. | +| F07 | fixed-now at the wrapper boundary | Reverse-order and readd index oracles; native DDL receiving remains open. | +| F08 | fixed-now | Capacitor, Tauri, and Cloudflare factories forward clock and claim lifetime, with targeted tests. | +| F09 | accepted-design | Cleanup reports adapter teardown failure; expiry is not proof that cleanup succeeded. | +| F10 | refuted | Unload before a pending load is necessary when unload lets that load settle; a source-specific late callback still needs fencing. | +| F11 | confirmed-open | Uncertified Electric subsets rotate on later cold starts, so offline reuse and multi-tab sharing need a subset-authority design. | +| F12 | fixed-now | Deterministic renewal-write count and near-expiry control. | +| F13 | design-decision | Installed Electric SDK shares a mutable snapshot cursor; removing serialization regresses cursor state. Parallelism needs a separate cursor/session or SDK protocol change. | +| F14 | fixed-now at the descriptor and controlled delivery boundaries | Composed real-SQLite/installed-SDK overlap remains open. | +| F15a | design-decision | Claim-table and old cache-ID migrations only support PR intermediate databases; retain until the supported preview-upgrade policy is explicit. Older applied-transaction migrations remain necessary. | +| F15b | fixed-now | Removed unused claim-assertion API and implementation. | +| F15c | fixed-now | One lifecycle-guarded helper replaces four identical scheduled recovery blocks; the transaction-path recovery remains separate. | +| T01 | stale as a blanket claim: real SQLite already rejected expired claimed reads; a startup second-check gap was real and fixed in the controlled owner. | Same-schedule real SQLite receiver remains open. | +| T02 | fixed-now for Query | Real QueryClient plus managed Node SQLite is now a receiving witness; custom sources remain F01. | +| T03 | fixed-now | Failed Electric restart followed by a new demand is now observed before cleanup. | + +These 20 source-order items account for 12 fixed-now, 2 confirmed-open, +1 stale, 2 refuted, 1 accepted-design, and 2 design-decision. The reviewer +identified several high-value lifecycle counterexamples but did not run them. +Its proposed parallel Electric snapshots and wait-before-unload remedies were +not sound on the installed SDK and legal owner schedules, respectively. + +## Oracle-guide audit for the changed witnesses + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | The laws above derive from the glossary, cache-eviction design, Collection index registry, and established Query/Electric demand contracts. The limits are explicit. | +| ORC-002 | Query expected rows come from separately controlled provider values and claim authority, not the Query fetch classifier. Index expectations come from the Collection declaration, not the persistence reconciliation loop. | +| ORC-003 | The primary Query, persistence, and Electric oracle files place law, model or independent relation, legal schedule, production entry, and observation cut beside the executable histories. | +| ORC-004 | New Query startup and index races are fixed controlled histories, not new generated grammar claims. Existing generated persistence and Electric campaigns retain their documented bounds. | +| ORC-005 | Drivers enter the real QueryClient/Collection/Node SQLite path, the persisted wrapper with controlled index I/O, and the Electric adapter/SDK paths. They assert public rows, caller settlement, fetch counts, and claimed durable rows at named cuts. | +| ORC-006 | Original code failed the Query stale-startup and held-peer success/error cuts, the index reverse-order cut, and the other RED schedules named above. A first added durable scan failed because it read another valid generation; that was a driver error, not a product mutant, and was corrected. | +| ORC-007 | No new important generated property was introduced. Existing fixed/random campaigns ran in the full package suites; direct replay procedures remain with their owners. | +| ORC-008 | No independent stateful model was added or merged. The fixed histories retain claim ownership because an expired run and a warm peer can observe different generations. | +| ORC-009 | Tests distinguish sync run, provider session, persisted cache generation, claim, demand, source fetch, applied receipt, public row, and durable row per the glossary. | +| ORC-010 | Fixed histories use explicit holds and cleanup; no shrink claim is made. Their original assertion failures were recorded before cleanup. | +| ORC-011 | A real QueryClient plus real Node SQLite is a distinct receiving formulation beside the controlled persistence and Electric models. It does not establish native multi-tab or Electric service behavior. | +| ORC-012 | This commit-tied record states outcomes and open cuts for each applicable requirement. | +| ORC-013 | Warm versus rotated startup, old peer success versus error, near-expiry versus distant renewal, and remove versus readd are distinguishing neighboring legal cases. | +| ORC-014 | The Query startup claim and durable row observations use Node SQLite. The index reverse order and several Electric delivery schedules use controlled collaborators; their native or service receiving cuts remain open. | + +## Verification and open work + +At repair commit f2bf482b3, the full Query package passed 1,028 tests, +Electric passed 1,077 with one skip, and SQLite persistence passed 1,276 with +two existing todos. The core capability test passed seven tests. All affected +package typechecks reported no errors. Changed-file ESLint reported zero +errors; Prettier and Git diff checks passed. Two independent final oracle +reviewers found no further confirmed Query or index correctness bug; one +durable Query observation gap they found was added and checked. + +The next design work is subset-specific Electric cache certification for safe +offline reuse, a reclamation policy for legacy logical storage, a contract for +custom managed on-demand providers, and an SDK cursor design for parallel +snapshots. The coverage map names remaining real-provider and native-host +receiving cuts. Green fixed histories do not prove arbitrary timing or storage +growth bounds. diff --git a/docs/contributing/oracle-reviews/issue-2071-refused-insert.md b/docs/contributing/oracle-reviews/issue-2071-refused-insert.md new file mode 100644 index 0000000000..b0b241781f --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2071-refused-insert.md @@ -0,0 +1,116 @@ +# Issue #2071: refused insert in a persisted Collection + +Source: [TanStack/db issue #2071](https://github.com/TanStack/db/issues/2071), including its closure comment, reread 2026-10-07. The report names `@tanstack/db` 0.12.1 and `@tanstack/db-sqlite-persistence-core` 0.4.5. The release probe used `3825ac3cb1db1fb145cafcc202dc05424b74dc56`, whose package versions match. This review began from `origin/main` at `ea51b67b06f2ef86d3a891a577dcc74e558454d7`, then merged `origin/main` at `2c98b4992`. Its executable tests and contract corrections are commits `14fc66d99c852dff9e0046c3ed32e92c4da6f51f` and `3e33fec8d073811e0c4c08e10cfd5db14268e596`. At those commits, the checkout had no production behavior change. The later [PR #2078 review](pr-2078-origin-attribution.md) records subsequent oracle and production changes. + +The reporter's bridge test and callback/write sequence are unavailable. The direct probes below reproduce the stated public setup, but they cannot establish that the private bridge reaches the same scheduling path. The issue is closed; the author's comment says it is probably a real problem, and does not retract the technical claim. + +## Reviewer assessment + +The report identifies the wrapper, real SQLite adapter, rejected outbound insert, transaction state, durable-versus-live divergence, and same-key source delete. Those are useful signals and a plausible high-impact failure if reproduced. It gives no minimal bridge fixture or exact sync callback and mutation-handler order, so its root-cause theory and proposed repairs cannot be verified. The scope is focused and the signal-to-noise ratio is good; the proposed overlay/reconciliation alternatives are outcomes to test, not approved mechanisms. **Hire recommendation:** insufficient evidence to recommend hiring as a reviewer from this single report. + +## Lossless finding ledger + +The issue supplied no file/line or severity labels. “High if true” below reflects the live/durable divergence, not a confirmed defect. Dispositions apply to the reviewed commit. + +| ID | Original claim or proposal | Technical verdict and evidence | Disposition / PR action | Durable value and destination | +| --- | --- | --- | --- | --- | +| R01 | A failed local insert remains visible until restart. | Unverified on the private bridge; no ghost at refusal settlement or fresh-adapter reopen in the controlled current and release probes. High if true. | **confirmed-open**; obtain the bridge schedule before a production fix. | Refusal and reopen law in the persisted wrapper and real SQLite owners; bridge gap in the coverage map. | +| R02 | In sync-present persistence, outbound insert rejects and the transaction fails, while `applyCommittedTx` never stores the failed row and reopen omits it. | The focused real SQLite receiver observes exactly this in its refusal history. An independently accepted source insert is a separate durable write. | **accepted-design**; retain the durability and rollback assertions. | Fresh Collection, adapter, and driver witness in `persisted-real-adapter-lifecycle.test.ts`. | +| R03 | Version 0.12 folds the failed optimistic insert into `syncedData`. | No such promotion in four controlled histories on either commit. A hostile promotion mutant fails the new exposed-base comparison at refusal settlement. The private path is unknown. High if true. | **confirmed-open**; do not alter core state from an unverified causal theory. | Core optimistic owner and persisted wrapper owner; exact bridge history needed. | +| R04 | Same-key post-failure sync writes are silently ignored in memory although a delete reaches `applyCommittedTx`. | No omission in controlled insert/delete orders or the real SQLite continuation. Omitting source-delete forwarding fails two controlled row comparisons and one real receiver. The private path remains unknown. High if true. | **confirmed-open**; request the exact source transaction sequence. | Persisted wrapper source-order law and real adapter receiving witness. | +| R05 | Without the wrapper, core rolls the failed optimistic row back. | Confirmed by the core optimistic-history model and its failure path; no conflicting controlled result. | **accepted-design**; no production change. | `optimistic-history-oracle.ts` and outcome/publication drivers. | +| R06 | Supported APIs could not repair the bridge; completed overlays survived persistence in 0.9, while the current bridge acknowledges success through sync. | The 0.9 behavior is the reporter's historical account, not retested; the current settlement-drop contract does not retain completed overlays. Whether supported APIs leave a current bridge gap cannot be judged without its sequence. | **confirmed-open** for the current API/path question; retain the retired 0.9 detail as context, not a restoration request. | Current core contract and the bridge evidence gap in `oracle-coverage.md`. | +| R07 | A failed mutation must leave live and stored rows consistent; keep it overlay-only until acknowledgement or reconcile through later source writes. | The outcome law is accepted. Both controlled and real adapter witnesses satisfy it. The two implementation suggestions are unproved alternatives. | **accepted-design** for the outcome; no speculative state machine or retry. | Refusal matrix, durable adapter comparison, and reopen receiver. | +| R08 | The reporter closed the issue with an apology while saying it was probably real. | Closure and comment confirmed. They add no execution evidence and do not technically retract R01/R03/R04. | **stale** as an issue-status signal; no closure-based refutation. | This record preserves the unresolved claims and exact evidence request. | + +Totals: 8 raw items = 4 confirmed-open + 3 accepted-design + 1 stale. There are no fixed-now behavioral bugs, refutations, duplicates, deferrals, or design-decision items. Documentation was corrected separately under the attribution law below. + +## Laws, histories, paths, and enforcement + +### L1. A rejected local mutation cannot become authoritative + +Authority: the optimistic-state and accepted-sync-transaction contracts in `docs/contributing/glossary.md` and `optimistic-history-oracle.ts`. A local insert overlays the applied source rows while its handler runs. On rejection its optimistic state drops. Only an accepted source transaction can change the applied base or durable adapter. This is an outcome law; it does not choose the reporter's proposed repair mechanism. It covers R01–R03, R05, and R07. + +The old portfolio exercised core rollback but did not cross a rejecting `onInsert` through the sync-present persisted wrapper with source work on the same key, nor receive that controlled event with a fresh real SQLite adapter. The new independent single-row source reference state starts empty. Four legal histories cross first source `insert`/`delete` with acceptance before/after refusal, then apply the opposite turn. A source insert before refusal is an accepted independent source row, so it may survive; a refused local intent alone may not. The production path is `collection.insert` → wrapped `onInsert` → `SyncConfig` → real Collection and recording adapter. The driver compares the live row, exposed `collection.base`, durable adapter row, and origin while optimistic, after source acceptance, at `isPersisted` rejection, and after each source receipt. A held receipt must remain pending until the mutation settles. + +The real receiving path uses `createSQLiteCorePersistenceAdapter` with a SQLite CLI driver. It observes the absent durable row, accepted same-key source delete, live/base absence after rejection, later insert/delete/replacement, and reopening the same SQLite file with a new Collection, adapter, and driver. It does not restart an OS process. Its two histories cover reopen before later writes and reopen after the continuation. They receive the controlled delete-before-refusal premise, but do not receive every insert/timing cell. + +Encoding and enforcement are complete **for these bounded histories and checkpoints**: the independent source reference state predicts the row, and the production comparison reaches each named cut. They are not complete for the reporter's bridge, other mutation kinds, on-demand sync, other SQLite hosts, or a process restart. A reachable bridge-specific counterexample would keep the product class open; the current evidence does not establish its absence. + +### L2. Accepted source writes apply in order and receipts wait for visibility + +Authority: the accepted-sync-transaction glossary entry and the persisted wrapper contract. An accepted source transaction becomes durable and then visible in order. A same-key delete can remove an absent source key and still must reach core. If publication waits behind a persisting mutation, its commit receipt waits too. This covers R04 and the source portion of R02/R07. + +The refusal matrix checks source insert/delete in both orders and at settlement and each receipt. The real receiver confirms that a delete reaches the adapter and core and that subsequent source writes replace the row in live, base, storage, and reopened Collection. The original gap was the absence of a refused-insert wrapper history with a direct same-key source continuation. The model can represent absent keys, later replacement, and independent source writes; the recorder does not collapse them to a final snapshot. The exact private bridge sequencing remains uncovered. + +### L3. `$origin` is key-and-timing attribution, not causal authorship + +The old public API text said a `local` source row originated from this client. `SyncConfig.write` carries no source-client identity. Two temporary RED probes disproved that wording on the reviewed core: an independent peer write accepted on the same key during a successful local mutation was labeled `local`, and a second queued same-key source transaction was labeled `remote` even though it too was accepted before settlement. We chose the observed attribution contract, as directed in this review, and corrected API comments, guide, generated reference pages, production comments, glossary, and oracle prose. No production branch was changed. + +For one local mutation without truncate, a successful settlement grants local attribution to the first queued same-key source transaction. Every same-key write inside that atomic transaction keeps it; an absent-key delete consumes it without leaving a row. Later queued same-key transactions are remote without another local owner. A rejected mutation grants those queued writes none. A truncate is a distinct legal cut: it can publish a same-key source row while the mutation still persists, leaving that row labeled local after the mutation later fails. Local-only Collections always label rows local. These are attribution rules, not evidence of the writer's identity. + +The core `HistoryModel` independently tracks applied rows, local mutations, and accepted source batches. Its existing driver compares virtual properties, live rows, event replicas, downstream rows, request outcomes, and publication cuts after each step. New fixed witnesses reach one and two queued transactions, two different same-key writes inside the first atomic transaction, an absent-key delete followed by insert, and truncate before failed settlement. The persisted wrapper receives the one/two queued pair. The new truncate witness also prevents the revised public prose from overclaiming that every failed mutation leaves remote origin. This law is encoded and asserted for these histories. At `3e33fec8d`, the source-batch grammar wrote rows before deletes, so it could not express delete then reinsert within one batch. The later PR #2078 review added ordered operations and a witness for that history. Overlapping same-key mutations and all truncate/source combinations are not claimed as exhausted; causal authorship would require a new source signal, model, and real-provider receiver. + +The prep-PR review exposed a model flaw, not a production defect. The old model deleted a key's attribution after its first write inside a batch. Adding a legal first batch with two different same-key writes made the oracle fail at `isPersisted` settlement: it expected the final row's `$origin` to be `remote`, while production returned `local`. `HistoryModel.drain()` now snapshots attributed keys once per atomic batch, then consumes them for later batches. The extra batch-local set is derived from existing state; it preserves a distinction that the next legal same-key write inside the batch can observe. The adjacent two-transaction witness ensures attribution does not persist into the next batch. A temporary production mutant that discarded within-batch attribution failed the new settlement assertion; restored production passed. + +## Hostile controls and GREEN results + +All mutants were temporary and restored before commit. Each failure below reached the intended production path; none was committed. + +| Control | Wrong design | Result at the relevant checkpoint | +| --- | --- | --- | +| M1 | Omit clearing pending local changes. | Two refused-insert origin assertions failed. | +| M2 | Drop durable source-delete forwarding to core. | Two controlled row assertions and one real SQLite receiver failed. | +| M3 | Settle a wrapped receipt at durability. | Two held-receipt pending assertions failed. | +| M4 | Keep local attribution after the first same-key source transaction. | The two-transaction origin assertion failed. | +| M5 | Ignore the first same-key local attribution. | The one-transaction origin assertion failed. | +| M6 | Keep attribution after an absent-key source delete. | The delete-then-insert origin assertion failed. | +| M7 | Clear the pending-local snapshot in truncate replacement. | The truncate history failed on rows visible when `isPersisted` settled. | +| M8 | Keep a failed mutation in the optimistic overlay. | All four refused-insert histories failed at the refusal-settlement live-row comparison. | +| M9 | Promote a failed local insert into applied synced data. | The two histories with no prior source write failed at the refusal-settlement live/base comparison. | +| M10 | Clear local attribution after the first same-key write inside one atomic source transaction. | The new two-write history failed at the `isPersisted` settlement origin comparison. | + +A narrower attempt that removed only the direct `pendingLocalChanges` check survived the truncate witness because the retained truncate snapshot enforced the same rule. That survival does not weaken M7's separate assertion failure and is not counted as a kill. The old model's false RED and M10's production assertion failure are distinct controls. M8 and M9 directly challenge the reported ghost-row class; M2 and M3 challenge the neighboring source and timing laws. + +On executable commit `3e33fec8d`, the two core optimistic-history suites passed **37/37**, with no type errors. The affected persistence suites passed **869**, with **2 existing todos** and no type errors. Prettier and `git diff --check` passed. The docs link check still exits 1 on two links in unchanged `docs/contributing/oracle-rubric.md` that point outside the docs tree; it reports no new link from this review. The `@tanstack/db` build passed earlier in this review, before the final test and documentation edits. The reported release's scratch controlled matrix passed five selected probes, and its fresh-adapter SQLite probe passed three selected tests. The release probes are uncommitted diagnostic files in a separate checkout; only the current-head witnesses are in the executable commits. + +## Oracle guide audit (ORC-001–014) + +| Rule | Outcome | +| --- | --- | +| ORC-001 authority/limits | L1/L2 use the existing settlement and accepted-source contracts; L3 was corrected to the observed, approved timing contract. The bridge and host limits are explicit. | +| ORC-002 independent judgment | The persisted expected row comes from a single-row source reference state, not wrapper caches. The core HistoryModel remains structurally separate from Collection state. | +| ORC-003 literate responsibilities | Opening prose states each law and limit; nearby comments identify the fixed history grammar, model rule, real driver, observations, and settlement/receipt/reopen cuts. | +| ORC-004 generated grammar | No generator changed. The new fixed matrix reconstructs all four declared timing/write cells; each axis changes a reachable cut or expected row. The core owner's existing generated grammar and exclusions are not revalidated by these fixed cases. The fixed two-write source batch reaches repeated same-key rows; at `3e33fec8d`, delete then reinsert within one batch remained unrepresented. PR #2078 later added an ordered-batch witness. | +| ORC-005 production/observations | `collection.insert`, wrapped sync, Collection, real adapter, and fresh reopen all run. Live/base/durable rows, origin, receipt state, and publication cuts are asserted. | +| ORC-006 calibration | M1–M10 classify wrong answers at intended assertions; the redundant-check survivor and old model's false RED are reported separately. | +| ORC-007 fixed/random parity | Not triggered by these fixed witnesses; no generated property or replay interface was edited. Existing campaigns ran as part of the persistence suite, not as proof of the private bridge. | +| ORC-008 state minimality | The core model added only a derived batch-local attribution set. A second same-key write in the same batch distinguishes retaining it from consuming attribution per write; a later batch distinguishes it from retaining attribution indefinitely. The persisted reference state has one expected row and source-order turns. | +| ORC-009 vocabulary mapping | “optimistic state,” “accepted sync transaction,” “applied base,” and “held optimistic row” use the glossary. `base`/`queue` model fields are mapped in the core owner opening. | +| ORC-010 failure fidelity/cleanup | Controlled and real witnesses preserve primary assertion errors, await/reject outstanding work, and report secondary cleanup failures before releasing collections and SQLite files. | +| ORC-011 second formulation | The single-row source reference state and core event-history model differ from wrapper state; real SQLite supplies a separate durability receiver. A causal-origin formulation cannot be derived without a client-ID signal, so causal authorship is not claimed. | +| ORC-012 review evidence | This record is tied to exact executable commits `14fc66d99` and `3e33fec8d` and names the open contract × history × path × observation cells. No bug-class closure is claimed. | +| ORC-013 distinguishing boundary | Two same-key writes in one transaction versus two queued transactions distinguish per-batch attribution from per-write consumption or indefinite retention at settlement. Delete-then-insert and truncate-before-failure distinguish other plausible wrong origin rules. M4–M7 and M10 reach their premises. | +| ORC-014 controlled handoff | The real SQLite receiver supplies the controlled delete-before-refusal premise and same-key continuation. Other controlled cells and the private bridge have no real-provider receiver and remain bounded. | + +## Refutations, stale context, and remaining work + +There is **no refutation** of the reported bridge failure. The negative probes reach the public setup but not the exact private callback schedule. The reporter's 0.9 completed-overlay account was not retested and does not describe the current settlement-drop contract. Issue closure is not a technical answer. The former causal `$origin` wording was disproved by controlled counterexamples and corrected as documentation and contract prose, not by changing runtime attribution. + +No authorized repair is deferred: no reproducible current product defect was found to fix. R01/R03/R04/R06 remain **confirmed-open** evidence gaps. The persisted wrapper owner in `oracle-coverage.md` needs the reporter's smallest bridge callback and source-write sequence, including which sync transaction commits before the refused handler settles. If supplied, replay it first against `3e33fec8d` and the reported release, extend the primary oracle with nearby legal histories, require a hostile control at the violated checkpoint, and only then change production. At `3e33fec8d`, remaining in-scope paths included other mutation kinds, on-demand sync, host-specific SQLite behavior, overlapping same-key mutations for attribution, delete then reinsert within one atomic source transaction, and an actual OS process restart. PR #2078 later added an atomic delete/reinsert witness and bounded same-key overlap histories. The other paths remain unestablished. + +Final accounting: **8 = 4 confirmed-open + 3 accepted-design + 1 stale**. The bounded oracle and documentation work requested for this restart is complete; the issue's private bridge claim remains unresolved. + +## Prep-PR review reconciliation + +The code reviewer found one test-integrity flaw and no other actionable finding. The claim was accurate, reached a legal history, and identified a narrow model fix, so its technical depth and signal-to-noise were strong. P2 is proportionate because the oracle falsely rejected correct behavior, while no production defect was shown. This single review supports a recommendation for focused oracle review; it does not establish broader reviewer performance. The simplifier made no edits and proposed three useful cleanups, plus preservation of existing distinct witnesses and API text. + +| ID | Original review item | Evidence and verdict | Disposition and durable result | +| --- | --- | --- | --- | +| P01 | The model consumes `$origin` attribution per write, while production retains it through an atomic source transaction; preserve it through the batch and add a witness. | Old model RED at `isPersisted` settlement for two same-key writes; corrected model GREEN; M10 fails at that cut. | **fixed-now** in `3e33fec8d`; primary oracle model, witness, glossary, and coverage map. Mixed delete/reinsert was outside the grammar at that commit; PR #2078 later added it. | +| S01 | Consolidate duplicated SQLite reopen setup while preserving both checkpoints. | Both branches repeated the same Collection, adapter, and driver construction; the shared tail now reopens after each branch's distinct source history. Both real SQLite histories pass. | **fixed-now** in `3e33fec8d`; real adapter lifecycle test. | +| S02 | Describe scalar `expectedSource` as independent reference state, rather than an “edit log.” | The model holds one expected row after each source turn, so “edit log” implied a history it does not store. | **fixed-now** in this record; L1 and ORC-002/008/011 descriptions. | +| S03 | Separate the two #2071 coverage entries from the earlier three-domain list. | The prior layout included those entries under an unrelated count. | **fixed-now** in `3e33fec8d`; acceptance map. | +| S04 | Retain distinct oracle histories, checkpoints, and full descriptions on both generated API reference pages. | The histories compare different publication cuts, and both public pages describe their own API surface. No deletion was proposed or made. | **already-fixed**; preserve the witnesses and API text. | + +Prep-PR loss audit: **5 raw items = 4 fixed-now + 1 already-fixed**. The final executable diff matches each disposition. P01's per-batch law is encoded and enforced for repeated row writes in the first batch and distinguished from a later batch. At `3e33fec8d`, the grammar could not express delete then reinsert within one batch; PR #2078 added that witness. The private bridge is still unavailable, so this record does not claim the reported issue is closed. diff --git a/docs/contributing/oracle-reviews/issue-2072-alias-shadowing-ad5e0108.md b/docs/contributing/oracle-reviews/issue-2072-alias-shadowing-ad5e0108.md new file mode 100644 index 0000000000..b7421c9c32 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2072-alias-shadowing-ad5e0108.md @@ -0,0 +1,148 @@ +# PR #2079: alias scope review at ad5e0108b + +This audit evaluates the ten source-order claims in the external review of +`ad5e0108be85d221d4061a303da8fac47ce2d312`. The repair is a follow-up +diff on that head. The authority is `packages/db/src/query/live/ARCHITECTURE.md` +§Identity and law 1: aliases are lexical, captured references retain their +source, and compilation addresses Collection inputs by `SourceId`. +The primary executable owner is +`packages/db/tests/query/includes-alias-shadowing-oracle.test.ts`; the +generated scope campaign is `includes-oracle.property.test.ts`. + +## Law, experiment, inference + +| Law and authority | What the owner previously proved | Decisive experiment | Inference and repair | +| --- | --- | --- | --- | +| A captured source and a child source remain distinct under ancestor shadowing (§Identity, law 1; items 01–05). | Existing direct, QueryRef, union, grouping, and spread cases covered selected placements, but did not combine an inner FROM alias with a later outer join, merge a joined QueryRef's inherited parent context beside a same-named local child, combine two same-name spreads, spread a grandparent's whole row, or select a captured alias directly. | At the public live Collection row after preload, the original implementation returned zero rows for an eager self-join, lost the child's title in a joined include, dropped parent spread fields, and read a child's row for direct parent selection. Renamed controls distinguished name collisions from ordinary joins. An adjacent unmatched LEFT join read the parent ID as the missing joined ID. | The missing dimensions were sibling source placement, matched and unmatched joined sides, simultaneous spread keys, whole-alias depth, and direct alias selection. The repair resolves the lazy source by ID, retains the local row when joining inherited context and clears a missing joined alias, makes temporary spread keys unique, collects binding-bearing whole-row refs, and lowers direct alias selection to its expression. | +| A keyed lazy demand reaches the exact lexical Collection source (§Identity and law 12; item 06). | The prior finite demand cases covered direct and wrapped includes, but not the outer join's self-join target with an inner source of the same alias and Collection. | The eager self-join was RED at the first public row. Replacing exact source resolution with the old recursive alias search in a temporary hostile mutant made that same assertion fail; the renamed control passed. | Alias text selected the inner source before the outer joined source. The target now follows a binding-aware ref to a `SourceId`. Other lazy join topologies remain an owner gap. | +| An include cannot reuse one builder's source declaration across ancestor and descendant scopes (§Identity, law 1; item 10). | Existing direct, QueryRef, union, and nested-include rejection cells exercise this rule; sibling reuse remains legal. | The reported `new Query().from({ x: base })` still embeds `base` and its ancestor source declaration, so it must reject. | The guard is correct under the approved API rule. Error 233 now says to start the child from the Collection rather than pass the ancestor builder. | + +The new finite model joins plain source rows by role and applies ordinary +object spread order. It does not use binding IDs, alias maps, compiler +metadata, or the optimizer to predict rows. The driver builds public `Query` +plans, preloads live Collections, and compares values and multiplicity at +initial publication and the named source writes. The self-join crosses eager +and controlled on-demand sync; the joined child and spread cases have a +renamed control. The direct alias case crosses shadowed and renamed children. +These are bounded witnesses, not a claim that every alias-scope path is closed. + +The original implementation's public-row failures were captured before the +production edits in `/private/tmp/pr2079-selfjoin.log`, +`/private/tmp/pr2079-join-red2.log`, and +`/private/tmp/pr2079-red2.log`. The first merge repair still failed the +adjacent unmatched LEFT join's initial public-row comparison: `joinedId` +was 1 instead of undefined. Clearing the absent joined alias passed initial, +insertion, and deletion checkpoints. The source-ID hostile mutant failed the +eager self-join public-row assertion in +`/private/tmp/pr2079-alias-mutant.log`. The review's particular on-demand +self-join failure did not reproduce with the finite source adapter: the +on-demand cell passed before and after the repair. This does not refute a +different provider schedule. + +## Lossless review ledger + +The source review labels two items “7”; IDs 07 and 08 below preserve their +source order. + +| ID | Claim and technical verdict | PR action | Durable value and destination | +| --- | --- | --- | --- | +| 01 | Inner FROM/outer JOIN alias reuse returns empty rows. **Confirmed** for the eager public path; the claimed on-demand failure was not reproduced with this controlled adapter. The flat alias maps are a plausible mechanism, but the demonstrated wrong target was recursive alias lookup. | **Fixed-now** for the reproduced topology using exact source resolution. No general alias-scope closure claim. | Self-join and renamed controls, eager/on-demand cells, source write: primary alias oracle. Other join-subquery and provider schedules: coverage map. | +| 02 | Joined parent context overwrites a same-name local child. **Confirmed** at the initial row; a renamed child passed. An unmatched LEFT join exposed the inverse error: the parent ID appeared as the absent joined ID. | **Fixed-now** by preserving local main-row aliases, adding the actual joined source, and clearing it when absent. | Child title after preload and update; missing/present/missing joined side: primary alias oracle. | +| 03 | Captured and local spread sentinels collide. **Confirmed** at the first child row; a renamed child passed. | **Fixed-now** using unique temporary keys and deterministic select-local rekeying. | Both fields after parent/child writes and repeated-build identity: primary alias oracle. | +| 04 | Grandchild whole-row spread loses a grandparent. **Confirmed** at the first public row. | **Fixed-now** by collecting binding-bearing single-segment external refs. | Grandparent row after preload and update: primary alias oracle. | +| 05 | Direct `.select(() => capturedParent)` reads the child. **Confirmed** under shadowing; renamed child returned missing data. | **Fixed-now** by retaining the captured expression under the spread sentinel. | Shadowed/renamed initial and post-write rows: primary alias oracle. | +| 06 | Lazy target search can choose the nested source by alias text. **Confirmed** as the wrong target behind 01's eager trace. The broad claim about all subscription shapes is unproved. | **Fixed-now** for this target path with binding-aware traversal and exact `SourceId`. | Primary alias oracle and lazy-target unit; other lazy topologies: coverage map. | +| 07 | A user-declared pure wrapper is retained and costs another stage. **Structurally true.** | **Accepted-design** for now: flattening its distinct binding would violate the approved identity rule; no binding-preserving flattening design is established. | The wrapper identity cell remains in the primary oracle. A future optimizer design must prove both identity and work. | +| 08 | Local refs perform parent-context metadata checks per row. **Confirmed** by the compiled closure and the earlier two-row/two-ref spy (four lookups); no elapsed-time or throughput conclusion follows. | **Design-decision:** whether a compile-time local/parent split earns its code and compatibility cost needs a work target. No correctness law is violated. | Performance opportunity in this record; `compiler/evaluators.ts` is the implementation owner. | +| 09 | Full-tree cache comparison and function-form misses. **Confirmed**: a temporary same-source work probe recorded two compiled entries for a shared structured child and three for the same child with `fnWhere` (including the outer query). The existing cache comparison test shows function-form plans return false. | **Design-decision:** whether to add an identity-safe function-form equivalence and how to bound comparison work. Reusing plans by path alone previously conflated lexical bindings. | Cache tests and `compiler/query-equivalence.ts`; preserve this work count before changing equivalence. | +| 10 | A fresh wrapper around an ancestor builder is rejected. **Refuted as a product bug** by the explicit ancestor/descendant source-declaration rule. The old error wording was misleading. | **Accepted-design** for the guard; **fixed-now** for error 233's instruction. | Existing rejection oracle and updated error fixture/docs. | + +Items 07–09 are work observations. No elapsed-time contract was inferred from +them, and none justifies weakening source identity or cache equivalence. + +## Oracle guide audit + +- **ORC-001:** The architecture's lexical-binding and source-ID rules authorize + the expected rows and demand target. The owner comments and this record bound + the finite histories and excluded paths. +- **ORC-002:** Expected rows come from plain source-role joins and JavaScript + spread order, independent of the compiler's alias and binding machinery. +- **ORC-003:** The executable owner states the law before mechanics and places + the model, legal axes, public driver, and checkpoint explanations beside the + tests. The new lazy-target unit is supporting code, not a second oracle. +- **ORC-004:** The new cells are a finite matrix, not a new generated property. + The eager/on-demand and shadowed/renamed controls ablate source mode and + alias collision. The source IDs, two-level and three-level nesting, and + initial/post-write cuts bound range. Same-scope duplicate aliases remain + invalid in the existing validation owner. The unchanged generated campaign + retains its own reconstruction, ablation, range, and exclusion audit. +- **ORC-005:** `createLiveQueryCollection` and public `toArray` reach the + claimed paths. Row comparisons preserve missing values and multiplicity; + the lazy demand owner also compares provider requests. +- **ORC-006:** The original implementation failed the new first-row + comparisons for 01–05. The alias-only hostile mutant failed 01's public-row + assertion, not a setup or timeout gate. No mutant is claimed for every + additional topology. +- **ORC-007:** The new cells are finite, so fixed/random campaign and replay + obligations do not trigger for them. The existing generated owner still runs + its fixed and random scope campaign. +- **ORC-008:** The new models recompute from plain Maps or fixed rows; they do + not add a stateful reference machine. +- **ORC-009:** “Parent,” “child,” “manager,” and “joined source” denote source + roles. Model rows do not encode production binding IDs or `SourceId`; those + identify the production source implementing each role. +- **ORC-010:** `withHistoryCleanup` retains the primary mismatch and cleans + live queries and Collections. The RED evidence is assertion failure at the + stated row cut. The tests do not shrink histories. +- **ORC-011:** No named model/production shared semantic classifier remains: + the role model never resolves aliases. Renamed-query controls add a separate + metamorphic comparison, but do not replace the plain row model. +- **ORC-012:** This exact-head record provides each applicable outcome and the + non-applicable trigger reasons. It claims bounded repairs, not closure of the + entire alias-scope class. +- **ORC-013:** Shadowed and renamed controls distinguish lexical role from + alias text; the alias-only mutant proves the public comparison detects the + wrong boundary on the eager self-join. The on-demand schedule claim remains + bounded to the controlled fixture. +- **ORC-014:** Controlled on-demand sources establish this adapter's preload + and write cuts only. A real-provider receiving witness for the review's + on-demand schedule remains with the alias-scope coverage owner. + +## Remaining boundary + +The covered contract × history × path × observation is lexical source identity +for the finite self-join, joined include, two-spread, grandchild spread, and +direct-alias selection histories at their public-row cuts, plus exact source-ID +lazy targeting for the eager self-join. The coverage map retains arbitrary +join-subquery topologies, deeper captured refs, RIGHT/FULL joins, temporal +demand, and real-provider scheduling. Flat alias-keyed metadata remains in the +compiler; the tested lazy path no longer relies on its alias-only target search. +This record does not claim that every remaining metadata consumer is safe. + +The reviewer found five real public-row errors and a related source-target +error, with small distinguishing examples. That is high technical signal. +The broad attribution to flat alias maps was incomplete, the on-demand claim +was not reproduced in this fixture, and item 10's proposed guard removal +conflicts with the approved contract. The performance items correctly identify +work but do not establish a performance budget or safe replacement. + +For reviewer quality, this is a **hire** recommendation: the review found +several distinct public failures that the existing suite missed and supplied +useful renamed controls. The reviewer should check approved API rules before +proposing guard removal, and use work counts rather than timing claims for +performance findings. + +## Verification of the repair diff + +- The bounded alias and lazy-target suite passed after the unmatched-join + addition. The complete query runtime run passed 108 files and 4,490 tests + with Vitest's integrated type check disabled; the separate TypeScript + project check passed. The earlier integrated run passed 4,717 test + assertions but could not write Vitest's temporary type-build file through a + read-only dependency symlink. This is a tool-location failure, not an + assertion failure. +- The production error-message oracle passed 19 tests. The generated + error-doc check, ESLint, Prettier, and `git diff --check` passed. +- The final source simplification and unmatched-join fix were followed by the + full query run above. The temporary cache work probe was removed; it is + evidence of work, not a permanent performance law. diff --git a/docs/contributing/oracle-reviews/issue-2072-alias-shadowing-followup.md b/docs/contributing/oracle-reviews/issue-2072-alias-shadowing-followup.md new file mode 100644 index 0000000000..9b48b20ca2 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2072-alias-shadowing-followup.md @@ -0,0 +1,273 @@ +# Issue #2072 alias scope: review follow-up for PR #2079 + +The external review checked the alias-shadowing change at `c79d8a8c4`. The +evaluation ran against `0d4b883cc2`, which merged newer `main` error-message +work but kept the relevant binding and compiler logic. The contract is +`packages/db/src/query/live/ARCHITECTURE.md` §Identity and law 1. The primary +executable owner is `includes-alias-shadowing-oracle.test.ts`, with the broader +scope grammar in `includes-oracle.property.test.ts`. + +## Proved failures and repairs + +- Reusing one `q.from({ n })` builder as both the parent and a direct include + child gives both placements the same binding ID. A child + `eq(child.parentId, parent.id)` is then classified as two child references + and construction throws the misleading “must have a WHERE ... eq()” error. + This is still open. The existing helper-reuse oracle reused a child twice, + which did not exercise one builder across ancestor and descendant scopes. +- The declaration collector also descended through a child `QueryRef`. A + captured parent ref was classified as child-local when the parent builder + was reused inside that `QueryRef`. A public query failed at include + construction before its first row comparison. The collector now includes + only declarations in the queried lexical scope. The primary oracle checks + the same form at preload and after a source row moves between parents. The + old collector fails before publication; the repaired collector passes. +- A spread sentinel carried its path but not its binding. Whole-row and + nested-profile spreads of a captured parent read child data under a shadowed + alias, and read `undefined` under a renamed child alias. The sentinel now + carries a `PropRef` with its binding. Eight primary-oracle cells cross row + and profile spreads, shadowed and renamed aliases, and eager and on-demand + child sources. They compare parent fields and child ID after preload, + parent update, and child update. All eight failed on the old lowering at the + public-row assertion and pass after the repair. +- `queriesMatchForCaching` equated identical `PropRef` paths whose hidden + binding IDs named different scopes. A focused cache test failed on the old + comparison and passes after an additional semantic-identity check. A + function-form query has no stable identity, so the cache comparison now + conservatively returns false for that valid form instead of throwing. + Public consequences of the broader rewrite-loss claim still need a witness. +- Single-row and `$selected` proxies returned a nested Proxy for + `__bindingId`; alias proxies did not report that key through `in` or + `getOwnPropertyDescriptor`. Focused tests failed on the old traps and pass + after all three proxy forms return or expose the intended metadata. + +## Remaining evidence and boundaries + +- Flat alias-keyed compiler metadata can overwrite a same-named source from + another scope. A public eager-root/on-demand-joined-`QueryRef` witness with + an inner alias shadowing the root loaded the correct joined row and issued + a joined-source request. The asserted wrong-target outcome needs a smaller + failing query or an additional lazy topology. The coverage map already owns + lazy join loading through merged `aliasRemapping` as an open cell. +- In a no-includes query, a deterministic spy observed four parent-context + metadata lookups for two rows and two selected refs. This confirms extra + work. It does not establish a throughput regression or that a guard is + cheaper than the current property lookup. No performance change was made. +- The claim that a captured parent `orderBy` or `groupBy` reads the child row + did not reproduce in a public order/limit probe or the existing grouping + oracle. Several `new PropRef` sites deliberately turn a namespaced ref into + a single-row ref; a blanket preserve-ID rewrite would be wrong. The cache + comparison defect above is the demonstrated structural-equality part. +- `removeRedundantFromClause` can collapse an outer `QueryRef` to its inner + `CollectionRef` while outer refs retain the vanished binding ID. A wrapped + and direct query returned equal public rows in the tested correlated form, + but their optimized plans had different identities. Predicate pushdown loss + and a public row failure remain unproven. A repair must preserve `SourceId` + and lexical binding roles together, or retain the wrapper. +- `DuplicateAliasInSubqueryError` remains exported for import compatibility + but has no throw site. Its source documentation marks the changed behavior, + and a minor changeset records the newly supported nested alias scope. +- `_getCurrentAliases()` has a `unionAll()` output wildcard `*` that + `_getCurrentBindings()` intentionally lacks. Deriving the alias list only + from binding-map keys would lose that output namespace. + +This follow-up does not close the alias-scope bug class. The direct reused +builder trace is an in-scope counterexample to the approved lexical law. +Broader lazy targets, captured ordering/grouping, optimizer rewrites, and +temporal demand retain the owners and limits recorded in the coverage map. + +## Follow-up: ancestor/descendant builder reuse + +The API now requires a fresh `new Query().from()` when a source is declared in a +descendant of a query that already uses that source binding. The same builder +may still be placed in sibling include fields. The primary oracle's direct and +`QueryRef` rejection cells were RED at `5bd355a7`: the direct form gave the +misleading missing-correlation error and the `QueryRef` form constructed +silently. Two more cells require the same error through a union branch and a +nested include. The rejection checks source bindings through those placements +before correlation extraction. The former positive +parent/inner `QueryRef` cell now uses a fresh declaration of the same +Collection, preserving its row and source-update assertions. The sibling +reuse cell remains unchanged. This closes the known direct reuse trace under +the stated API rule; the other evidence gaps above remain open. + +At `1f588f53f`, the 51-cell alias-shadowing oracle, 268-cell generated scope +oracle, alias validation tests, and production error-message oracle all passed +(346 tests total, no type errors). Lint and whitespace checks passed too. + +## Bounded group, identity, and lazy-demand follow-up + +This audit starts from PR head `eda70b96cba65be5e843fbd0a73db4f500f719c0`. +The established laws are ARCHITECTURE.md §Identity and laws 1 and 12: a +captured reference keeps its lexical source, explicit projected results are +invariant under legal alias renaming, and an applicable index excludes +irrelevant source rows from physical work. These are consequences of the +approved alias-scope design; the oracle does not introduce a new API rule. + +Three bounded cells were added to the existing alias-scope owner: + +- **Same-path group keys.** A child shadows `parent` and both have `rank`. + The independent model counts plain child rows by rank for each parent. + Four cases cross shadowed/renamed aliases with both group-key orders. The + public-row comparison runs after preload, a child-rank change, and a + parent-rank change. Removing the binding-ID comparison from group expression + equality makes both shadowed cases fail at the first public-row assertion: + `childRank` reads the parent's value. Renamed controls pass. +- **Optimized identity.** Two pure wrappers differ only in the lexical name + of their source. A plain Map predicts identical explicitly projected rows + after preload and after a source change. Their raw identities agree, but the + old optimizer collapsed a distinct outer binding and left its refs dangling; + the optimized identities then differed by alias spelling. Restoring that + collapse makes the new identity assertion fail after the public-row checks. + The repair retains a user-declared wrapper when its binding differs from the + inner source. The older optimizer fixture now gives its synthetic internal + wrappers the same binding ID, so it still proves that safe wrappers collapse. +- **Lazy joined QueryRef.** A finite on-demand user source is joined to an + anchor, while an include inside that joined QueryRef shadows or renames the + user's alias. Direct and wrapped include sources cross both names. A plain + Map predicts public rows, and a separate finite interpreter evaluates each + provider WHERE over both user IDs. The checks run after preload, an anchor + move, and a user update. Restoring the child's `aliasToCollectionId` merge + makes the shadowed direct case ask for `[1, 2]` when the model permits only + `[1]`, at the first provider-work assertion. A child compilation result + already owns its aliases, so the repair does not merge child alias or + remapping records into the enclosing scope. Source-keyed WHERE clauses + still propagate. The wrapped cases challenge a child remapping as well. + +Oracle guide audit for these cells: ORC-001 names the architecture authority +and explicit limits in each opening comment. ORC-002 uses plain Map/count +models, a metamorphic alpha-renaming relation, and a small provider-predicate +interpreter rather than compiler decisions. ORC-003 keeps law, model, bounded +history, public production driver, observation, and checkpoint beside their +code. ORC-004 and ORC-007 do not apply: these are finite enumerations, not new +generated properties; the existing broader scope campaigns remain unchanged. +ORC-005 observes complete selected group values and counts, public wrapper +rows, and both public rows and provider requests for the lazy case. ORC-006 is +demonstrated by the three intended-checkpoint failures above; none is a setup +or timeout failure. ORC-008 applies only to the lazy model's covered-ID set: +the anchor's later move distinguishes an already covered user from a newly +reached user. ORC-009 maps model parent, child, anchor, and user roles to +Collection sources; lexical aliases and binding IDs are production concepts, +not model state. ORC-010 uses `withHistoryCleanup` to retain the primary +failure while releasing the live queries and sources. ORC-011 has no named +shared semantic fault requiring a second formulation; public-row models and +alias-renamed controls also constrain the lazy interpreter. This record and +the updated coverage map provide ORC-012 evidence. ORC-013 is witnessed by +the shadowed/renamed cases and by `[1]` versus an unrestricted `[1, 2]` +request with an applicable index. ORC-014 makes no real-provider claim: the +request witness uses a controlled finite adapter. + +At repair commit `d1126e7c4`, all 98 DB query suites pass (4,079 tests), +including the generated scope oracle's fixed and random campaigns. The DB +TypeScript, ESLint, and Prettier checks pass with the worktree's pinned +dependencies linked. +This is bounded evidence, not alias-scope closure. Other aggregate and +ordering expressions, other optimizer rewrites, RIGHT/FULL joins, broader +lazy-target paths, cancellation, and real-provider request handling retain +the owners and limits in the coverage map. The no-includes metadata lookup +count remains an unquantified performance observation; no throughput law or +repair is claimed for it. + +## Outer filter ownership and captured projection pushdown + +The semantic repair head is `8c34efa2c20de1cc521bb6f0ed643771dbdbe9a3`. +ARCHITECTURE.md §Identity and law 1 supplies both expectations: a filter +belongs to its lexical source, and a captured parent field keeps that binding +through a child QueryRef. The primary alias oracle adds two independent +plain-Map models. One checks parent and child rows through a joined QueryRef or +direct include, with shadowed or renamed child aliases, eager or on-demand +sources, and child IDs equal to or different from the outer filter literal. +The other checks that a projected parent rank controls a child QueryRef after +preload, a child-rank write, and a parent-rank write. Both compare public rows; +the on-demand cases also check which child source rows were admitted. + +At the original production head `7486c5360`, six of the ten new cases failed +at the initial public-row comparison: four lost a child whose ID differed +from the outer filter literal, and two evaluated a captured parent rank as +the child's rank. The neighboring child-ID-1 cases passed. The repair converts +each query scope's alias-keyed filter to its SourceId before merging child +compilation results. It also retains a parent-dependent predicate outside a +child QueryRef rather than pushing it into the child source. At the semantic +repair head, all ten new cases, the 268-case generated scope oracle, and the +56-case optimizer oracle pass. The full query runtime suite passes 4,476 tests; +the DB test TypeScript check, targeted lint, and formatting checks pass. + +Oracle-guide audit: ORC-001 uses the architecture law and names the finite +scope in each opening comment. ORC-002 uses plain source-role maps rather than +compiler metadata. ORC-003 places the law, model, grammar, public driver, +observations, and checkpoint beside their code. ORC-004 and ORC-007 do not +apply to these finite enumerations; the existing generated scope campaigns +retain their own controls and replay. ORC-005 observes complete selected rows +and, for on-demand sources, admitted source rows after preload. ORC-006 is the +six assertion failures at the intended checkpoint on the original head. +ORC-008 retains parent and child rank because the two legal writes distinguish +their effects; the model has no planner or provider state. ORC-009 maps plain +Map entries to source Collection rows and their selected public result. +ORC-010 uses `withHistoryCleanup` to retain the primary assertion failure +while releasing Collections. ORC-011 uses alias renaming as a second +metamorphic formulation beside the source-role model. ORC-012 is this +exact-head record and the updated coverage map. ORC-013 is distinguished by +child ID 1 versus 2 and by parent rank A beside child rank Z, then the +independent rank writes. ORC-014 makes no real-provider or browser claim: the +on-demand sync adapters are finite controlled fixtures. + +These witnesses cover those two predicate paths and checkpoints. They do not +claim that all optimizer rewrites, nested joins, or temporal demand histories +preserve lexical bindings; the alias-scope row in the coverage map retains +those cells. + +## Ancestor source declarations inside union and QueryRef sources + +CodeRabbit review `5471809088` at `eda70b96c` noted that a parent built from +`unionAll(branchA, branchB)` could reuse `branchA` as an include child without +the fresh-declaration error. The accepted law is ARCHITECTURE.md §Identity and +law 1: one source declaration cannot serve in both an ancestor source tree and +its descendant include. A union's projected fields are visible to callbacks; +its branch source aliases are not. The admission check must still know the +branches' source identities. The same distinction applies to a QueryRef used +as the parent's source. + +Two finite oracle witnesses at `3617eacd5` returned a builder instead of +throwing: one reused a parent union branch as an include child, and the other +reused a source inside a parent QueryRef. Each supplied a separate joined +anchor for the required correlation, so a missing-correlation error could not +mask the reuse. The new checker compares the construction result with the +documented fresh-Query error. Both cases were RED before the repair and pass +after source-binding collection walks the parent's FROM and JOIN source tree. +Callback proxies still expose only the union result fields. Existing fresh +declaration and sibling-reuse controls remain legal. +The primary, generated-scope, and route-context suites pass 458 tests; the +full query runtime suite passes 4,785 tests, with DB TypeScript, targeted lint, +and formatting checks green. + +This checks those two source placements at construction. The traversal also +handles union-from and joined QueryRef sources; no new public-row or temporal +witness is claimed for them. The broader alias-scope gaps remain in the +coverage map. + +## Nested source placement and captured joined rows + +The starting semantic head is `a4bab56793c72fc02d45bbd8dbd453632cdb041c`. +ARCHITECTURE.md §Identity requires a nested source's identity to survive an +alias collision with an outer joined source. The primary alias oracle now +crosses nested FROM or JOIN placement, field or whole-row capture of the outer +joined `user`, shadowed `user` or renamed `detail` in the child, and eager or +on-demand child acquisition. A plain-Map source-role model predicts exact +public rows at initial publication, child deletion and movement, outer joined +row update and movement, and parent update. On-demand cases compare the two +namings' nonempty source-request sets at every cut. The fixture keeps default +public keys unambiguous; repeated contributors for one key are a separate +materialization contract. + +The eight cases pass on the starting semantic head. A temporary mutant that +disabled projected binding lookup in `compiler/evaluators.ts` failed all eight +at the **initial public-row assertion**: a captured outer assignment ID `101` +was read from the child detail as `201`. This is an assertion failure after +successful setup, not a runtime or cleanup failure. The mutant was restored; +the log is `/tmp/pr2079-projected-binding-mutant.log` on the authoring host. +An alias-only mutation in `compiler/lazy-targets.ts` survived this matrix. +The matrix therefore proves captured-row lookup sensitivity, while indexed +lazy-target identity remains with the earlier self-join witness and needs +broader topologies before claiming full coverage. These controlled sources do +not prove real-provider scheduling, cancellation, RIGHT/FULL joins, arbitrary +join trees, or temporal demand. diff --git a/docs/contributing/oracle-reviews/issue-2072-alias-shadowing-outer-joins.md b/docs/contributing/oracle-reviews/issue-2072-alias-shadowing-outer-joins.md new file mode 100644 index 0000000000..c91ef53f3d --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2072-alias-shadowing-outer-joins.md @@ -0,0 +1,564 @@ +# Alias scope follow-up: outer joins and captured operands + +The contract is `packages/db/src/query/live/ARCHITECTURE.md` §Identity and +normative law 1. A captured reference names its lexical source even when a +child declares the same alias. An absent outer-join side has no row from that +source. A join operand may combine an ancestor value with a local source value; +reversing `eq()` operands does not change the relation. + +The primary executable owner is +`packages/db/tests/query/includes-alias-shadowing-oracle.test.ts`. Its new +plain-row models pair sources by numeric keys and apply ancestor correlation by +source role. They do not use compiler alias maps, binding IDs, route metadata, +or optimizer helpers to calculate expected rows. The driver uses public `Query` +and live Collection entry points. The comparison reads public rows after +preload and after each source write. + +## RED on merged `main` + +The starting commit was `f7ac2c63a`, the merge of PR #2079. Before the +compiler edit, the new QueryRef RIGHT and FULL cells reached the first public +row check with an unmatched joined row. The expected child `mainId` was +`undefined` and `missingMain` was `true`; the actual values were the ancestor's +`id` of `1` and `false`. A direct joined Collection lost the unmatched row +entirely, because only the main stream carried the parent route. Both eager +and on-demand versions showed the mismatch. The captured-ancestor join cells +failed at compilation with `InvalidJoinCondition` before they could publish +rows; their renamed and reversed controls supplied distinguishable source +roles. + +The first repair exposed one more missing checkpoint. A FULL join whose +correlation was attached to the main side admitted a joined-only row after the +main source had already been filtered by parent keys. The independent model +excluded that row. This was RED at initial publication in both source modes. + +## Repair and GREEN witness + +The compiler now routes the joined input of a correlated RIGHT or FULL join +through the parent stream. At the join output, an absent main side clears its +local aliases from inherited parent context. After a RIGHT or FULL join, the +compiler checks correlation again, since that join can create a row without +the previously filtered main source. Join operand classification uses binding +IDs to distinguish the joined declaration, local main declarations, and +captured ancestors. Ancestor terms are legal on either operand; a joined-side +ancestor term also triggers route attachment before its expression runs. + +The outer-join matrix has 24 cells: three join/correlation layouts, two source +modes, direct or QueryRef joined sources, and shadowed or renamed aliases. +Each cell checks the initial state and six writes, including main removal and +return, a second match, joined-side removal, and an ancestor update. Two parent +routes prevent one route's rows from standing in for another. The operand +matrix has 24 cells: an ancestor term combined with a local main or joined +term, or serving as the entire main key, crossed with both equality orders, +two aliases, and two source modes. It checks five cuts across ancestor, child, +and joined-source changes. The original implementation fails witnesses +for each reported bug at the intended construction or public-row checkpoint; +the repaired implementation passes the matrix. + +Four targeted mutants confirm sensitivity at the intended checkpoint: + +- Retaining inherited local aliases makes the shadowed QueryRef RIGHT cell + fail its first public-row comparison. +- Omitting parent routing for direct RIGHT/FULL joined inputs makes the direct + RIGHT cell lose its unmatched row at the first public-row comparison. +- Omitting the post-join correlation check admits a joined-only FULL row whose + correlation belongs to the absent main side. +- Classifying a bound join reference by alias text makes the captured-operand + cells throw `InvalidJoinCondition` during compilation. + +The final focused run passed 716 tests across seven join, include, optimizer, +and oracle suites. TypeScript checking, lint, and whitespace checks passed. +The package-wide run with its browser-capable configuration passed 11,905 +tests in 272 files. Its `oracle-replay.test.ts` child-process harness failed +21 tests because this local checkout links `node_modules` to a directory that +the sandbox cannot write; Vite reported `EPERM` while creating its temporary +config. No query or join test failed in that run. The package's `test:oracles` +command now includes the primary alias-shadowing oracle. + +This is bounded evidence for these legal histories and observations. It does +not establish arbitrary chained outer joins, deeper captures through other +recursive query forms, temporal provider races, or every lazy join target. +Those remain in the alias-scope owner entry of `oracle-coverage.md`. + +## Foreign captured join operands — 2026-10-10 follow-up + +CodeRabbit review `5476389606` examined `2d0cbafabddb898d8fadcb7d304ff06fb0154d7e`. +Its one correctness finding is repaired at executable revision +`50407da55dc0be58aec1b5e735ebc10a5b7cd7a9`. This record covers that +revision; a later documentation-only commit may carry the record itself. + +**Law and test gap.** `ARCHITECTURE.md` §Identity and law 1 make aliases lexical +names. A join operand can read a local or actual ancestor declaration; a +reference captured from an unrelated query is outside scope even if its alias +matches. The earlier operand matrix exercised valid local and ancestor roles, +but lacked an unrelated provenance role and a rejection checkpoint. It was +correctly green for its narrower grammar. + +**Independent oracle.** The primary +`packages/db/tests/query/includes-alias-shadowing-oracle.test.ts` now uses the +source declaration's role, not binding IDs or compiler alias maps, to predict +admission. Its bounded grammar crosses root versus include placement, eager +versus on-demand sources, and a foreign alias that matches the local alias +versus one with different text. The driver captures the foreign reference +through the public `Query.select()` callback, builds the join through the +public builder, and observes rejection during construction or preload before +rows are accepted. A valid local operand publishes exactly one row in the +same fixture; the pre-existing 24-cell ancestor-operand matrix keeps legal +captures accepted across source mode, alias spelling, operand order, and +updates. The foreign source's ID is `999` while the local and joined IDs are +`1`, so substituting the local row cannot masquerade as the foreign source's +value. The test cleans up every constructed live-query Collection and source +Collection while preserving a primary assertion failure. + +**RED, calibration, GREEN.** On the reviewed head, the public-path probe +published `[{localId:1,joinedId:1}]` with the unrelated `local.id` operand, +the same as the legal local-operand control. The newly added oracle was RED +in all four placement/source-mode tests because each preload fulfilled rather +than rejecting. With the final oracle unchanged, a temporary mutant that +disabled raw-plan scope validation failed all four at the intended rejection +assertion; it was an assertion failure, not a setup error or timeout. The mutant +was removed. On the repaired executable revision the full primary oracle +passed 103 tests, and seven affected join, include, and optimizer suites +passed 493 tests. The production-error-message oracle passed 19 tests; +TypeScript, ESLint, and `git diff --check` passed. A package-wide run passed +11,909 tests in 272 files. Its 21 oracle-replay subprocess tests could not +write Vite's temporary config through this checkout's restricted dependency +link; an additional Vitest typecheck temporary file hit the same sandbox +boundary. These are local harness failures, not a claim of a fully green +package-wide run. CI remains the normal unrestricted receiving check. + +**Repair boundary.** Scope validation walks the original query before +optimization, carrying declarations visible at each join through `QueryRef`, +union branches, and includes. It rejects a bound join ref absent from the +current and ancestor sets. Recursive compilation uses the already validated +tree, while the public compiler entry keeps its original signature. The +checker passes only the current query's visible declarations and its actual +ancestors to include validation. It does not infer ancestry from alias text or +expose declarations hidden inside a `QueryRef` or union branch. The remaining +owner entry in `oracle-coverage.md` tracks foreign refs outside join operands, +other recursive join shapes, and provider schedules; this bounded evidence +does not establish those cells. + +Guide audit for this oracle repair: + +| Requirement | Evidence or reason it does not apply | +| --- | --- | +| ORC-001 | §Identity and law 1 authorize lexical provenance and fail-fast rejection; the paragraph above states the join-only bound. | +| ORC-002 | Expected admission comes from source roles declared in the fixture, independent of compiler binding lookup or alias maps. | +| ORC-003 | The executable block states the law and limits beside its role model, finite grammar, public driver, preload observation, and comparison. | +| ORC-004 | The new matrix is bounded enumeration, not a generated-history property. Both aliases, placements, and modes are explicit; matching alias kills an alias-only rule, include placement requires an actual ancestor context, and the valid local and ancestor controls prevent a universal rejection rule. | +| ORC-005 | Public `Query` and `createLiveQueryCollection` reach compilation; the assertion checks rejection at construction or preload, and the local control checks exact public rows. | +| ORC-006 | The unvalidated implementation and the guard-disabled mutant both reach the promised checkpoint and fail by assertion. | +| ORC-007 | No new generated property or seed/replay interface was introduced. | +| ORC-008 | The provenance rule is stateless; it adds no reference-model state. | +| ORC-009 | Model roles `local`, `ancestor`, and `unrelated` denote source declarations in lexical scopes, not compiler IDs or runtime Collections. | +| ORC-010 | `withHistoryCleanup` preserves the primary rejection mismatch and separately reports cleanup failures. | +| ORC-011 | Matching versus different foreign alias text is a second, metamorphic formulation of the same rejection law; the legal ancestor matrix is the opposite control. | +| ORC-012 | This table records every other applicable guide requirement and the finite closure boundary. | +| ORC-013 | The matching-alias foreign witness rejects the plausible wrong rule “same alias means local”; the local and ancestor witnesses reject “all captured refs are invalid.” | +| ORC-014 | No claim crosses from the controlled source adapter to a real provider; the tested admission happens before provider-specific behavior matters. | + +## Chained joins and main-correlated route work — 2026-10-10 follow-up + +The nine-item user review inspected `2d0cbafabddb898d8fadcb7d304ff06fb0154d7e` without executing its findings. The current repair started from `43d7c3d771c52c39bff38ba5a4fb7b1d02e99215`; its executable commit is `b41dc35bf`. This section records the bounded oracle evidence for the two reported behavior gaps. + +**Absent local binding law.** `ARCHITECTURE.md` §Identity and law 1 distinguish a captured ancestor's `issue` from a child's same-named declaration. Once a LEFT join has no child `issue`, a later routed joined row may carry the ancestor context but cannot supply the missing child source. The prior outer-join matrix had one join only. It was green for its declared boundary and could not generate the reported two-join sequence. + +The primary `includes-alias-shadowing-oracle.test.ts` now recomputes two LEFT joins from plain parent, comment, issue, and tag rows. Its legal history has two parent routes: one child issue is absent and the other is matched. It adds and removes the absent issue, then removes and restores the second joined side. Each cut compares exact public child rows, including the local issue ID and `isUndefined`, under shadowed and renamed aliases, eager and on-demand sources, and routing caused by either a parent-dependent QueryRef or a captured ancestor operand. The model uses source roles and plain numeric relations; it does not read compiler namespaces, binding maps, or route metadata. The QueryRef filters tags by parent, while the direct source reads the parent in its equality operand; the model states that distinction explicitly. + +A 2026-10-11 review control adds a parent label to this plain-row model and +selected child result, then changes that label while the two joins have matched +rows. Both source modes and both route causes preserve the changed parent +label, including the shadowed local alias. This reaches a matched-join public +observation that the earlier absence-only assertion did not name. It does not +settle the reviewer's broader claim about every joined QueryRef shape or every +extra namespaced key; a concrete contrary query would need its own legal +history and public-row witness. + +With the final oracle in place, restoring the original merge order made both source-mode tests fail at **initial public rows**: child comment 10 had expected local issue `undefined` and `missingLocalIssue: true`, but received ancestor issue ID `1` and `false`. The QueryRef route produced the same RED observation in an earlier calibration run. The repair copies the main row's scope when it exists and only the joined source from the joined row; when the main side is absent, it still retains the joined row's route context and clears local main aliases. The repaired oracle passes both tests and its neighboring single-join matrix. + +**Route-work law.** `ARCHITECTURE.md` law 12 says irrelevant rows must not activate unrelated routes when an applicable index exists. A direct joined source in a main-correlated RIGHT or FULL include has no parent-dependent expression. Joined-only output has no main correlation and is removed at the post-join correlation cut, so adding unmatched joined rows cannot increase parent-route assembly for that source. A joined-correlated outer join and a parent-dependent QueryRef remain distinct; the primary row matrix retains both as correctness controls. + +The separate `includes-outer-join-route-work-oracle.test.ts` owns this work observation. A transparent wrapper counts calls to the existing route assembler while public rows are checked against plain relational recomputation. With two parents, it compares one matching anchor with that anchor plus two irrelevant anchors across RIGHT/FULL and eager/on-demand source modes. The old unconditional routing rule was a hostile mutant: all four cells kept the expected rows but grew from **2 to 6** route assemblies at preload and failed the work assertion. The repaired rule had equal counts (**0 and 0**) and the same public rows. Equality, rather than a mandated zero, is the work law; another implementation may do constant useful route work. This is an internal assembly count, not a claim about real-provider requests or every source scan. + +The reviewed head's foreign-bound-operand finding was repaired earlier in `50407da55` with a provenance oracle. The new review's mixed bound/unbound same-alias example does not reach its claimed same-source error: a public Query using a bound ancestor operand and an unbound raw-IR joined operand preloaded and returned the correct row. The role and same-source logic were unchanged from `2d0cbaf` to `43d7c3d77`. The new control remains in the primary oracle. The review's test-gap observation was valid: the prior matrix omitted a two-join history and main-correlated RIGHT. Those dimensions are now executable. Join-ref classification and the three expression walkers were consolidated; the join's local-binding set remains intentionally incremental because the builder helper includes future joins. + +The follow-up production diff from `43d7c3d77` is 59 added and 111 removed lines, net **−52**. The review's 86/44 (+42) count was accurate for `2d0cbaf`; after the earlier foreign-scope validator and this follow-up, cumulative production code remains larger than merged main. The extra validator enforces the explicit out-of-scope rejection law; line count alone is not a row-correctness witness. + +Focused primary and work owners passed 112 tests. The wider query directory passed 4,198 tests in 100 files. TypeScript checking, ESLint, Prettier, and `git diff --check` passed. The right-main row cells cover initial publication and six source writes; the chained law covers initial publication and four writes; the work law is checked at preload. This does **not** prove arbitrary chained join orders or kinds, every recursive joined source, a full source-work bound, publication events, or real-provider scheduling. The alias-scope coverage-map owner retains those limits. + +| Guide requirement | Evidence or limit for this follow-up | +| --- | --- | +| ORC-001 | §Identity/law 1 authorizes lexical source absence; law 12 authorizes the bounded route-work relation. The scopes and exclusions are stated above and in each oracle's opening prose. | +| ORC-002 | Plain source-row pairing predicts public rows; the work relation compares irrelevant-anchor growth. Neither expected result uses compiler role classification or route assembly. | +| ORC-003 | Both executable files place law and limits before mechanics, with model, finite history, public driver, observations, and comparison described beside code. | +| ORC-004 | No generated property was added. The bounded tables cross the named modes, aliases, route causes, and join kinds; matching and absent controls are explicit. | +| ORC-005 | Public Query and live Collection preload/writes reach compilation. Assertions read exact public child rows at each named cut; the transparent work wrapper counts route assembly at preload. | +| ORC-006 | Restoring the original merge order fails both chained-row tests at the first public-row assertion. Restoring unconditional RIGHT/FULL routing fails all four work cells at the count comparison while rows remain right. These are assertion failures, not setup errors. | +| ORC-007 | No important generated property was added; fixed-seed/random campaign parity and replay do not apply. | +| ORC-008 | Both expected results are stateless recomputations from current plain rows; no reference-model lifecycle state changed. | +| ORC-009 | Model parent, main/comment, local issue, tag/anchor, and route cause denote source roles and grammar choices; they do not stand for compiler alias maps or route metadata. | +| ORC-010 | `withHistoryCleanup` preserves the primary mismatch and cleans live and source Collections, reporting secondary cleanup errors separately. | +| ORC-011 | Alpha-renamed aliases and two route causes provide a second way to expose ancestor substitution. No independent real-provider work formulation is claimed; that handoff remains open. | +| ORC-012 | This table records every applicable guide requirement and states the finite closure boundary and unresolved cells above. | +| ORC-013 | An absent versus matched local issue distinguishes the source-binding consequence; one versus three anchors distinguishes useful joined rows from unrelated route work. The original merge and unconditional-route mutants are rejected at those checkpoints. | +| ORC-014 | Eager and controlled on-demand Collections supply the source premise. No claim is made about an external provider's request work; it needs a receiving witness before extension. | + +## Include validation and hidden source bindings — 2026-10-10 follow-up + +CodeRabbit review `5479589545` covered `43d7c3d77`, before the chained-join +repair, and reported that an include child could capture a source declared +inside its parent's `QueryRef` or union branch. The reviewed implementation +passed all bindings in the parent's source tree to the child validator. +`ARCHITECTURE.md` §Identity/law 1 instead gives the child the parent's visible +declarations and actual ancestors. A nested source's result may be visible, but +its private declaration is not. A child join operand bound to that declaration +must be rejected before public rows are accepted, regardless of alias text. + +The primary alias oracle now crosses hidden-source placement (`QueryRef` or +union branch), matching or renamed alias, and eager or on-demand source mode. +It captures the hidden source through the public builder and attempts an +include join through `createLiveQueryCollection`. The expected rejection comes +from the fixture's declaration provenance; it does not inspect compiler +binding sets. Valid local and genuine ancestor captures in the same owner are +opposite controls. The observation is rejection during construction or preload, +before public rows are accepted. All eight matrix tests failed on `6bd64e86f` +because preload resolved instead of rejecting. This was an assertion failure +at the intended checkpoint, not a setup error. Passing the validator's +existing visible-binding set to include validation made all eight pass. + +The query directory passed 4,593 tests in 110 files with Vitest inline +typecheck disabled; separate TypeScript and ESLint checks passed. A first run +with inline typecheck enabled passed 4,818 tests in 130 files before the alias +cells were split for independent RED assertions, then exited nonzero because +the linked Vitest installation could not write its temporary +TypeScript cache outside this workspace. Prettier and whitespace checks passed +after formatting. The finite matrix does not prove every hidden-source +expression position, deeper recursive source topology, or provider schedule. +Those remain under the alias-scope owner in `oracle-coverage.md`. + +| Guide requirement | Evidence or limit for this follow-up | +| --- | --- | +| ORC-001 | §Identity/law 1 authorizes lexical visibility; the parent result does not export its nested source declarations. | +| ORC-002 | The fixture labels visible and hidden declarations independently of the compiler's binding collection. | +| ORC-003 | The executable block states the law, model distinction, finite grammar, public driver, rejection cut, and limits beside the test. | +| ORC-004 | The bounded matrix crosses placement, alias spelling, and source mode; valid local and ancestor controls distinguish overbroad rejection. | +| ORC-005 | Public builders and live Collection preload reach the validator; each cell asserts rejection before accepting rows. | +| ORC-006 | Restoring the pre-repair source-tree collector fails all eight final cells by resolving preload; the repaired compiler passes them. | +| ORC-007 | No generated property or seed interface changed. | +| ORC-008 | Declaration provenance is a static model rule; no model lifecycle state changed. | +| ORC-009 | `hidden`, `local`, and `ancestor` name lexical source roles, not compiler IDs. | +| ORC-010 | `withHistoryCleanup` preserves rejection failures and disposes live and source Collections. | +| ORC-011 | Matching and renamed aliases are alternate formulations of the same visibility law. | +| ORC-012 | This table records the applicable guide requirements and the finite boundary above. | +| ORC-013 | The original recursive source-tree collector is the rejected wrong design; valid captures rule out a blanket ban. | +| ORC-014 | Both controlled source modes reach public preload. Real-provider scheduling is not claimed. | + +## Joined source scope — 2026-10-10 follow-up + +CodeRabbit review `5479690200` covered `6bd64e86f` and reported that a +joined `QueryRef` could capture a source declared beside it in the enclosing +query. This remained reachable after the include-scope repair. The compiler +compiles the joined `QueryRef` as a separate source stream; it receives any +actual ancestor route, but not the enclosing query's sibling rows. The law in +`ARCHITECTURE.md` §Identity therefore requires rejection of a bound sibling +reference inside the joined source. Only the enclosing join condition can +combine those two sources. The previous validator passed preceding sibling +bindings to the joined `QueryRef` and admitted the invalid capture. + +The primary oracle now crosses root/include placement, a joined source whose +inner alias matches or differs from the sibling's alias, and eager/on-demand +source mode. Each of eight invalid cells captures the sibling through a public +builder callback, places that reference inside the joined `QueryRef`'s own +join condition, and asserts rejection at construction or preload before public +rows are accepted. Four neighboring cells instead capture a true include +ancestor in the same nested join and compare exact public rows with plain +relational recomputation. These expected results come from source roles and +input rows, not compiler binding IDs or route metadata. + +On the pre-repair `e7ad3be2f` head, all eight invalid cells failed at their +rejection assertion because preload resolved. Restoring only the permissive +validator on the final oracle made the same eight fail while all four valid +ancestor controls passed. Passing actual `ancestorBindings` to joined-source +validation made all twelve cells pass. The visible-binding set remains in use +for the enclosing join condition and include children. This is bounded +admission and row evidence: it does not prove every expression position, +deeper recursive source form, or real-provider schedule. + +The final query-directory run passed 4,605 tests in 110 files with Vitest's +inline typecheck disabled. Separate TypeScript, ESLint, Prettier, and whitespace +checks passed. The linked test-runner cache limitation described above still +applies to inline typechecking in this local checkout. + +| Oracle responsibility | Evidence | +| --- | --- | +| Contract and model | §Identity names which declarations a joined source receives; plain source roles predict rejection or joined rows. | +| Grammar and driver | Root/include × matching/renamed alias × eager/on-demand mode; public Query and live Collection preload. | +| Observations and refinement | Eight rejection assertions before accepted rows; four true-ancestor controls compare exact public rows after preload. | +| Sensitivity and limits | A one-line permissive validator mutant fails all eight invalid cells and passes all four controls; the recursive and provider limits remain with the alias-scope owner. | + +## Expanded alias-scope laws — 2026-10-10 follow-up + +This expansion starts from `a598fa2bb`. The primary executable owner is +`packages/db/tests/query/includes-alias-shadowing-oracle.test.ts`; the +companion route-work owner is +`packages/db/tests/query/includes-outer-join-route-work-oracle.test.ts`. +`ARCHITECTURE.md` §Identity and law 1 authorize lexical source identity and +rejection of an unrelated bound reference before its expression accepts rows. +Law 12 supplies the bounded route-work relation. These tests use plain source +rows and declaration roles to predict results, without compiler binding maps, +parent-route metadata, or join classifiers. + +**Legal histories and observations.** The primary owner now recomputes four +two-join chains: RIGHT then LEFT, RIGHT then RIGHT, FULL then FULL, and FULL +then LEFT. The finite grammar crosses main, first-joined, or second-joined +parent correlation as applicable; shadowed and renamed aliases; eager and +controlled on-demand sources; and six writes that remove or restore first and +second join matches or change an ancestor value. Two parent routes distinguish +leakage between routes. At initial publication and after every write, the +driver compares exact public child rows, including the presence of each source. +The route-work owner now compares public rows and route-assembly counts at +preload and four main-source writes while irrelevant joined rows grow. It +asserts equal useful work across fixture sizes, without asserting that a +particular implementation performs zero assemblies. + +**Provenance grammar.** A source hidden inside a parent QueryRef or union +branch remains unavailable to an include child at one or two recursive levels. +Sixteen cells cross the four source placements, matching or renamed alias, and +source mode. Twelve non-join cells reject an unrelated bound reference in WHERE, +GROUP BY, HAVING, ORDER BY, direct SELECT, or conditional SELECT, with matching +and renamed aliases. A separate grouped include accepts actual ancestor refs +through shadowing, grouping, HAVING, ordering, and parent or child writes. Four +parent-filter cells reject a foreign reference moved out of the child's WHERE; +four extracted-correlation cells reject a foreign parent field removed from +that WHERE. Each has an exact-row control using the real parent. Public preload +is the rejection checkpoint; the valid controls compare public rows. + +**Sensitivity and repair.** A temporary later-FULL-as-LEFT mutant failed two +mixed-chain cells at their first public-row comparison. Restoring the +source-tree-binding mistake failed eight deeper hidden-source cells by +resolving preload instead of rejecting. Disabling the new non-join expression +checks failed all twelve cells at that same rejection assertion. The +parent-filter gap was RED on the starting validator and a focused mutant +omitting its repair failed all four parent-filter cells. Omitting validation +of the extracted correlation field failed all four new correlation cells. +These were assertion failures at the promised checkpoints, not setup errors or +timeouts; every temporary mutant was removed. The final validator checks +visible bindings in join operands, predicates, grouping, ordering, select +expressions, extracted parent filters, and both correlation fields before +optimization. The development text for error 238 now says “Query reference” +to match its wider scope. + +**GREEN and limits.** On the final working tree, the two focused owners passed +170 tests; the query directory passed 4,643 tests in 110 files; and the +production-error-message oracle passed 19 tests. Separate TypeScript, ESLint, +Prettier, and whitespace checks passed. The production compiler diff is 65 +added and 14 removed lines, net **+51**; the independent oracle tests grow +separately to prove the expanded law. The validation machinery accounts for +the positive production weight. This evidence covers the enumerated histories +and public checkpoints. Other expression forms, arbitrary join-subquery and +lazy-target shapes, deeper captures, other chained outer joins, external +provider schedules, and broader source-work bounds remain open in the +alias-scope coverage-map owner. + +| Oracle responsibility | Evidence or limit | +| --- | --- | +| Contract and model (ORC-001–003, 009) | §Identity and laws 1 and 12 supply the promise; plain relational rows and fixture declaration roles supply independent expected results. The executable comments place the law, limits, model, histories, public path, and comparison beside the tests. | +| Grammar (ORC-004, 007–008) | These are bounded matrices and stateless recomputations, not an important generated property or a new stateful model. The named axes expose absent and present source roles, alias renaming, recursion depth, and extraction boundaries. | +| Public path and comparison (ORC-005, 010–011) | Public Query and live Collection preload or writes reach compilation. Exact rows, rejection before accepted rows, and bounded route work are checked at their stated cuts; cleanup preserves the primary failure. Valid local and ancestor controls provide opposite formulations. | +| Calibration (ORC-006, 013) | The five temporary mistakes above fail at intended assertions; none survived or failed only in setup. | +| Handoff and record (ORC-012, 014) | The coverage-map owner states remaining paths. Controlled on-demand sources do not establish external-provider scheduling or work bounds. | + +## Sibling builder reuse and spread identity — 2026-10-10 review + +The merged #2079 review targeted `84dc3601c`. Its shared-builder finding +distinguishes **placing** a query builder twice from **capturing** a reference +declared inside a sibling query. `ARCHITECTURE.md` §Identity and law 1 allow +sibling reuse. The existing hidden-source oracle correctly rejects a bound +reference *into* the parent's QueryRef or union branch; it does not require +rejecting the same builder when that builder is independently placed in the +include child. Two older tests conflated the two acts and expected error 236 +for legal sibling placement. + +The primary oracle now places one builder both inside a parent QueryRef, +`unionAll` branch, or joined QueryRef and inside a sibling include. Six cells +cross these positions with eager or controlled on-demand sources. The plain +row model pairs current employees with their current manager; construction, +public rows after preload, and rows after a manager change are the asserted +cuts. The original source-tree guard failed all six cells at construction with +error 236. Restricting the guard to declarations in the containing query's +lexical scope makes them pass; the existing actual ancestor/descendant reuse +cells still reject before publication. This is admission and row evidence for +those three placements and one update, not an arbitrary recursion proof. + +The joined placement also has one manager without a matching helper row. Its +absence from the public output distinguishes a real joined-source execution +from an implementation that simply drops the helper join. + +The same review found that each enumeration of a captured reference proxy left +a spread marker behind. An independent selected-plan control now discards one +enumeration, then selects the same parent and child fields as a plan without +that discarded enumeration. It asserts equal query identity before preload and +exact public rows before and after a child update. The retained-marker +implementation failed at the identity assertion. A proxy now offers only the +marker for the current enumeration; the unchanged control and repaired plan +pass the same checks. Other spread layouts remain covered by neighboring +field, whole-row, nested-path, and alias-renaming cells rather than this one +identity control alone. + +The review's optimizer and work observations require narrower conclusions. +A scratch public-path probe of a pure same-alias QueryRef wrapper with an +indexed on-demand source observed a full-source request and two installed rows +for an outer equality selecting one row; the direct-source control requested +only that key. This confirms extra work in the current wrapper shape, but the +pre-merge optimizer extracted source predicates before wrapper collapse too, +so this probe does not establish that #2079 introduced the full-source request. +The wrapper's extra stage is real; its exact work budget and a safe binding +remap remain unproved. A nested `unionAll` ordered wrapper with the minimum +row in its second on-demand source published that row and requested both +sources; direct union sources are excluded from ordered-prefix optimization. +That probe does not prove every union shape safe. A bound root reference caused +an extra route-metadata check relative to an unbound reference, and a +function-form query failed `queriesMatchForCaching` even when compared with +itself. Those are deterministic mechanism observations, without a measured +public work bound. All scratch probes were removed; their evidence is kept in +the task-local review ledger, while the primary owner and coverage map retain +the behavior witnesses and their limits. + +## Follow-up verification of grouped rewrites and work observations + +The reported GROUP BY failure requires a ref rewritten without its binding ID +to reach group-key matching. A controlled outer grouped query with a nested +QueryRef and a second joined source reached predicate pushdown: the optimized +child acquired an unbound `WHERE u.category` ref, while the outer `GROUP BY +x.category` kept its binding ID. Its public result was `{category: 'A', +total: 2}`. When the grouped query was the inner QueryRef instead, the safety +check kept the outer WHERE outside (`outerWhere: 1`, `innerWhere: 0`). The +optimizer's only new `PropRef` is placed in a pushed WHERE; group-key matching +compares SELECT and GROUP BY expressions within one query. The suggested +WHERE-rewrite-to-GROUP-BY-matcher path is therefore not present in these +controlled shapes. + +The primary alias oracle now pairs a plain-row grouping model with four +outer-grouped histories: shadowed or renamed inner alias, eager or on-demand +source, and source/tag writes. It compares public groups after preload, after +a source enters the selected group, after tag removal, after a second tag +joins one source, and after a source leaves the group. The model counts both +joined copies. All 20 comparisons passed. A hostile GROUP BY matcher that +ignored binding IDs failed two existing same-path group cells at the first +public-row comparison: child ranks were replaced by parent ranks. A second +mutant dropped the pushed predicate while removing the outer one; all four +new cells failed at preload by publishing the unselected B group. Both mutants +were restored and all eight selected group cells passed. This proves the +lexical group-key law for the named histories; another alleged rewrite into +group matching needs a concrete path or fixture. + +The remaining work findings have narrower measured consequences: + +- **Pure wrapper (M3).** At base `8b0e1defb`, a pure same-alias wrapper + optimized to a CollectionRef. At `b5313bf00`, it remained a QueryRef over + the CollectionRef, confirming one extra stage. On both revisions an indexed + on-demand source received `null` (full-source) for the wrapped outer + equality, while the direct control received `eq(id, 1)`. A lazy joined + wrapper received the same keyed `in(id, [1])` request on both revisions. + Public rows agreed. The reported loss of indexed demand is not a new + consequence of the retained wrapper in these paths. A stage-count work + budget and safe binding remap have not been specified. +- **Root ref lookup (M7).** Ten evaluations of a bound public-builder ref on + proxy rows caused 20 route-metadata symbol checks on `b5313bf00`, versus + zero for an unbound control and zero for the base builder ref. Values agreed + in all cases. This is deterministic per-row overhead, while the primary + oracle's captured-ancestor rows show why removing all projected-binding + lookup would be wrong. No root-query lookup budget has been adopted. +- **Subquery cache (M8).** The same function-form builder placed on both sides + of a join compiled two function-query results and invoked its projection + four times for two source rows on both base and current heads. A stateful + callback produced different left and right sequence fields, so sharing one + pipeline can change observable behavior. For one ordinary query, + `queriesMatchForCaching(ir, ir)` made 28 watched root-property reads at base + and 66 at `b5313bf00`; the current comparator adds stable-identity work to + the earlier normalization. A function-form query compares unequal to + itself on the current head, but that alone did not increase compilation in + the two-placement probe. A callback identity or purity rule is needed before + broadening cache reuse. There is no accepted comparison-work budget. + +These work probes are diagnostic observations, not permanent work laws. The +primary oracle keeps the public behavior claim and its hostile-matcher +calibration. No production repair was made for M3, M6, M7, or M8 in this +follow-up. + +## Missing captured projection invariant on the combined tree + +This follow-up starts from #2077's pushed combined head `712e572bf`. A +builder-created include projects each captured ancestor reference into the +child route. An absent projection is a malformed internal plan, not a valid +way to reinterpret a bound ancestor reference as a same-named local source. +The compiler must reject it before child rows are accepted. + +The primary alias oracle builds a valid shadowed include through `Query`, +confirms its captured parent projection exists, then removes that projection +from the resulting IR. Before the compiler guard, the query compiled and its +preload fulfilled; the expected rejection assertion was RED. A separate +hostile mutant that made the builder omit parent projections was rejected by +the existing `QueryRef` public-row comparison at initial publication: both +children became empty. The new compiler guard reuses the builder's external- +reference walk, checks bound parent references in the child plan and extracted +parent filters against the projected list, and throws before compilation when +one is absent. The malformed-plan witness and all 178 primary alias tests now +pass. This check does not assert that arbitrary hand-written IR is otherwise +valid, or prove every possible runtime route-metadata transport path; the +public-row oracle retains the bounded legal histories stated above. + +On the repaired combined tree, the primary alias owner passed 178 tests and +the broader DB query runtime suite passed 4,675. DB source and test TypeScript +checks, changed-code lint, formatting, generated error documentation, and +production error-text checks passed. The broad runtime run disabled Vitest's +built-in typecheck because that runner tried to write a build-info file through +a dependency symlink outside this worktree; the separate TypeScript checks +passed. The pushed head and CI remain a separate gate. + +## Joined QueryRef lazy-target identity — 2026-10-11 review control + +The review observed that alias-keyed metadata can be overwritten when a +joined QueryRef's inner source shadows an outer alias, and that a +Collection/SourceId mismatch can turn off lazy targeting. The reviewer later +withdrew the claimed missing-load and wrong-row consequences after testing +public queries on the PR heads. These remain internal work and identity +questions, so this oracle checks a nearby legal receiving history without +calling the retracted consequence a current defect. ARCHITECTURE.md §Identity +says lexical source identity survives alias text reuse; law 12 says irrelevant +indexed rows do not require unrelated acquisition. + +The primary alias oracle now uses three on-demand sources: one user, two posts, +and two tags. Its joined QueryRef joins posts to tags and projects the tag's ID +as the outer LEFT join key. The post alias either shadows the outer user alias +or is renamed. A plain-map model predicts the user/post/tag result without +consulting compiler maps. At preload, only post 10 and tag 7 should have +entered their Collections. Moving the user to tag 8 admits post 11 and tag 8; +changing tag 8's label updates the public row. Exact public rows and installed +post/tag IDs are asserted at all three cuts. The installed-row observation is +needed because a full-source fallback can produce the right public row while +doing wrong lazy work. + +Starting from combined head `688eac636`, the implementation passed both alias +cells. A temporary wrong-target mutant paired the projected tag SourceId with +the QueryRef's post Collection. Both cells failed by assertion at preload: +post 11 was installed early instead of only post 10. Restoring the original +source pair made both cells GREEN. This establishes the law for one nested +joined QueryRef, one outer LEFT join, two alias spellings, two join-key values, +and one tag update. It does not prove that every recursive source shape avoids +alias-map collisions, nor does the controlled provider establish a real-provider +request bound. + +For ORC-001–003 and 005, the executable prose identifies the identity/work +law, plain-map model, finite grammar, public Query/Collection path, rows and +installed-source comparisons at each cut. ORC-004 and 007 do not apply because +this is bounded enumeration rather than a generated property. The wrong +Collection/SourceId mutant and shadowed-versus-renamed controls calibrate +ORC-006 and 013; ORC-008 adds no model state. ORC-009 maps model user, post, +and tag roles to source declarations rather than compiler alias maps. +withHistoryCleanup preserves a primary assertion and releases all four +Collections (ORC-010). A second semantic formulation is not required without +a named shared-fault hypothesis (ORC-011). This section states the starting-head +scope and remaining paths for ORC-012; ORC-014 leaves external providers +unclaimed. diff --git a/docs/contributing/oracle-reviews/issue-2072-alias-shadowing.md b/docs/contributing/oracle-reviews/issue-2072-alias-shadowing.md new file mode 100644 index 0000000000..c7c00dd50d --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2072-alias-shadowing.md @@ -0,0 +1,175 @@ +# Issue #2072: ancestor alias shadowing + +Reviewed implementation: `fd7aa208a`, based on `origin/main` at +`2ab7f3e5`. This record ships with the implementation it reviews. The +contract owners are `packages/db/src/query/live/ARCHITECTURE.md` §Identity and +law 1, `packages/db/tests/query/includes-oracle.property.test.ts` with its +scope companion, and `packages/db/tests/query/includes-alias-shadowing-oracle.test.ts`. +The independent oracle-enforcement correction below reviews `c0e42b168`. + +## Question and rival predictions + +Issue #2072 reports that a reusable `q.from({ item: items })` query cannot be +joined beneath another query whose source is also named `item`. The existing +`DuplicateAliasInSubqueryError` was intentional: removing its guard on the +original implementation let alias text select the wrong source. The reporter +also wants to compose helpers without managing a global alias generator. + +The proposed law is lexical. A source alias is a name in one query scope. A +reference captured by a callback names the source that supplied it, even if a +nested query later declares the same name. Same-scope source aliases and the +branch aliases of one `unionAll()` remain unique. Structured plans with an +explicit projection are alpha-equivalent when their references are renamed +with their sources. Implicit namespaced rows and functional callback inputs +still expose the user's alias; functional callbacks are alpha-equivalent only +when their behavior is alias-equivariant. + +| Design | Prediction for the shadowed correlated child | Prediction for public aliases | +| --- | --- | --- | +| Keep ancestor rejection | Construction throws before publication. | No child row to inspect. | +| Remove the guard and resolve by alias text | A child can read its own row where a captured parent was intended. Operand order can change the wrong result. | Internal alias rewriting can leak into implicit rows or callback input. | +| Bind each reference to its lexical source | The child sees its own row and the captured parent independently. | Implicit rows and callback input retain the declared keys. | + +The first design was the established contract. The second was a diagnostic +guard bypass, not a proposed repair. The third is the user-authorized contract +revision tested here. + +## Experiment and result + +The implementation assigns a binding ID when it creates each `CollectionRef` +or `QueryRef`. Callback-created `PropRef`s retain that ID across helper +placement and optimizer copies. Parent-reference discovery, correlation +extraction, predicate pushdown, compiled parent-context lookup, and query +identity use the binding. Source input lookup continues to use `SourceId`. +Public aliases remain the user's strings. + +The role-based model in the exact-output oracle reads only maps of locks and +votes. Direct and implicit-join forms match `vote.lockId` to `lock.id`. The +`QueryRef` and union left branch also match `vote.lockName` to `lock.name`; +the union right branch selects vote 11 by ID and joins it by `lockId`. The +production driver uses `Query` and `createLiveQueryCollection`. +It compares all enumerable key names and user values, including the presence +of virtual keys, plus multiplicity after preload +and after child insertion, parent insertion, a child insertion for that new +parent, a child move, child deletion, and parent deletion. The finite matrix +crosses direct, nested `QueryRef`, union, and implicit joined children with +shadowed or renamed aliases, both equality operand orders, and eager or +on-demand child sources. Focused witnesses cover one helper placed twice, +grandparent capture through a nested include, grouping, functional callback +input, semantic query identity, and a local child equality beside a real +correlation. + +The primary scope oracle also runs its fixed-seed and random campaigns with +ancestor shadowing legal. It compares a canonical naming with plain +recomputation, a generated naming with the canonical result, and on-demand +requests by Collection. Its output normalizer deliberately omits virtual +fields, so the new exact-output owner checks public keys separately. The +reported `item`/`item` join and a joined-`QueryRef` multiplicity case are +asserted in `validate-aliases.test.ts`; same-scope and union-branch duplicate +rejection stays there. + +| Probe | Observation | +| --- | --- | +| Original implementation | The exact issue form throws `DuplicateAliasInSubqueryError`. With only the guard bypassed, a correlated shadowed child publishes empty arrays with child-first equality; reversing operands can publish parent IDs instead of child IDs. Both are public-row failures. | +| Repaired implementation | The scope campaigns, exact-output cases, original issue form, and affected query suites pass. | +| Lost-binding mutant | Ignoring projected binding lookup survives the direct child: its equality was extracted before evaluation. It fails the nested `QueryRef` case at the **initial public-row comparison** with empty children. This distinguishes path reach from a vacuous green test. | +| False-correlation mutant | Treating an equality between two local child refs as a parent-child correlation fails the local-equality witness at the **initial public-row comparison** with empty children. | +| Alias boundary | The shadowed and renamed explicit projections agree with the role model. Their implicit joined children have the distinct declared keys `lock` and `vote`, as the public shape requires. A functional callback sees the declared `lock` key and a child vote's ID, lock ID, and lock name. | +| Identity boundary | An explicit child projection has the same query identity after consistent `lock`→`vote` renaming. `child.id = child.id` and `child.id = capturedParent.id` have different identities despite identical selected field names. | +| Dropped `QueryRef` inner predicate | Removing only its captured `lockName = parent.name` condition leaves the required outer `lockId = parent.id` correlation legal. Vote 11 then appears incorrectly under lock 1. The check fails at the **initial public-row assertion**. | +| Dropped union left predicate | Removing only its captured name condition leaves the outer `lockId` join and parent filter legal. The nonempty right branch still supplies vote 11; the left now supplies an extra copy. The check fails at the **initial public-row assertion**. | +| Parent-shaped functional input | Replacing the callback's child value with a parent-shaped lock leaves the `lock` key present but makes the child-specific predicate false. The check fails at the **initial public-row assertion** with empty children. | + +### Independent review correction + +The first exact-output oracle had three enforcement gaps. The `QueryRef` +inner and outer predicates both constrained `lockId`, so deleting the inner +predicate left the same public rows. The union's outer join and parent filter +implied its left predicate, while its right branch was empty. The functional +callback checked only its input key, so a parent row under `lock` looked valid. +These were false-green oracle designs; the review found no new production bug. + +The revised finite grammar keeps the outer `lockId` correlation required for +include admission and gives each recursive inner plan an independent captured +`lockName` predicate. Vote 11 matches lock 1 by ID but not by name. The union +right branch contributes vote 11 at initial publication, and a later move +makes both branches contribute it under lock 2. The functional callback reads +child fields, filters vote 11, and records its input values. The three +temporary wrong-query controls above reached the intended public comparison +and failed by assertion, rather than by setup error or timeout. + +## Bug-class boundary and remaining work + +The demonstrated claim is contract × history × path × observation: lexical +binding preservation for the finite direct, `QueryRef`, union, implicit join, +nested include, and grouped paths above; eager and controlled on-demand +sources; initial publication and the named writes; enumerable public key names, +user values, multiplicity, and query identity. Virtual metadata values are +outside this alias-scope comparison. The primary generated owner extends +the alias/topology and source-write grammar within its stated limits. No +reachable counterexample remains in these exercised cells. Passing the random +campaign is not a universal proof. + +The coverage map retains owners and needed witnesses for arbitrary deeper +nested includes, include-inside-subquery forms, `having`, RIGHT/FULL joins, +publication events, unload, ordered windows, lazy join loading through merged +alias remapping, and temporal demand. The controlled on-demand fixture does +not establish a real backend's acquisition schedule. React `useLiveQuery` +receives this builder/compiler behavior, but the exact issue form is asserted +here at the live Collection boundary rather than in a mounted React hook. + +## Oracle guide audit + +- **ORC-001:** The revised architecture law is the authority. The exact-output + oracle and coverage map state the bounded claim and open cells. +- **ORC-002:** The role model recomputes from source maps; it does not import + binding, compiler, optimizer, or output-classifier logic. +- **ORC-003:** Opening prose states the law; adjacent comments explain the + role model, legal finite grammar, real production driver, public recorder, + and comparison checkpoints. The primary generated owner names its companion. +- **ORC-004:** Every finite matrix member reconstructs from the four form, + two alias, two operand-order, and two source-mode axes. Renamed controls + ablate shadowing; reversing equality distinguishes direction-dependent + mistakes; the original guard distinguishes admission from execution. IDs + are 1–3 for parents and 10–15 for children, with one orphan. Initial votes + include a matching ID with a mismatching name; a later move creates union + branch overlap. Writes cover new, moved, and deleted rows. Same-scope and + union-branch repeats are nearby invalid cases. The primary generated grammar + retains its documented + three-name pool, bounded rows/writes, fixed and random runs, and replay. +- **ORC-005:** Real live Collections are read at initial and named write + checkpoints. The recorder retains every enumerable key and duplicate child. + The callback recorder sees vote 11 before its predicate excludes that row. +- **ORC-006:** Both temporary production mutants above fail at the intended + public-row assertion. The direct-case survival of the lost-binding mutant + is recorded, rather than counted as a kill. Three later wrong-query controls + each fail at initial public-row comparison after the independent review. +- **ORC-007:** The important generated property is the existing scope owner; + it runs the same grammar and check at fixed seed `1715` and without a seed. + `TANSTACK_DB_ORACLE_PROPERTY=includes.scoped-alpha-renaming` plus + `TANSTACK_DB_ORACLE_SEED` and `TANSTACK_DB_ORACLE_PATH` directly replay a + shrink. The new exact-output matrix is finite and does not claim a generated + campaign. +- **ORC-008:** The exact-output model retains one row per source ID so a later + put, move, or delete can distinguish states. It adds no model-only lifecycle + state. The primary owner is stateless recomputation. +- **ORC-009:** `lock` and `vote` are lexical aliases in production; `locks` + and `votes` are model source roles. The model's maps are current source rows, + not `SourceId`s, binding IDs, or D2 relation nodes. +- **ORC-010:** The primary owner preserves an assertion and cleanup failures + together. The exact-output owner uses `withHistoryCleanup` for the same + failure fidelity and releases each Collection. +- **ORC-011:** A shared alias bug could make both shadowed and renamed plans + wrong in the same way. Independent role recomputation is the second + formulation; exact public-key checks catch an alias rewrite hidden by the + primary normalizer. +- **ORC-012:** This versioned record documents each applicable obligation and + the bounded closure claim. +- **ORC-013:** A captured parent and a local child with the same alias are a + legal witness; the renamed control preserves explicit results, while the + implicit joined output distinguishes names that are public. Vote 11 + distinguishes the recursive inner predicate from the outer key correlation. + Same-scope and union duplicates distinguish the adjacent rejection boundary. +- **ORC-014:** The on-demand fixture supplies the `loadSubset` premise. The + claim is limited to that controlled provider; real adapter scheduling is an + open receiving boundary in the coverage map. diff --git a/docs/contributing/oracle-reviews/issue-2072-descriptor-query-binding.md b/docs/contributing/oracle-reviews/issue-2072-descriptor-query-binding.md new file mode 100644 index 0000000000..baf34ba798 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2072-descriptor-query-binding.md @@ -0,0 +1,498 @@ +# Standalone descriptor Query binding oracle review + +Reviewed semantic head: e4a002a046a8f2d99a6155cf63f1f897e8633909 + +## Claim and boundary + +The standalone Query contract in the SSR guide permits a Query built before any DbClient exists to use a reusable factory Collection descriptor. A consuming hook binds that plan to its current DbProvider client. Two clients using the same descriptor ID and plan must see their own source rows. A descriptor made from an arbitrary concrete config has a narrower one-client contract and is outside this claim. + +The React owner is packages/react-db/tests/descriptor-query-binding-oracle.test.tsx. Its reference model is one plain row map per client; a write changes only that map. Two fixed legal histories use disjoint IDs with inserts, or one shared ID with different values and updates. Each builds one Query before the clients, mounts it under two providers, writes to each client, and switches one mounted hook A→B→A. At each settled publication, the refinement check compares the complete public projected rows, each row's $key, and the exact public field set against the map for the current client. Query-plan identity is also compared with direct Collection formulations. Model seed rows are copied before entering production. + +The separate core witness in packages/db/tests/db-client.test.ts preloads a nested form of one Query in two clients and compares distinct dehydrated rows with equal query hashes. The React witness establishes the bounded hook behavior above. Neither witness establishes on-demand acquisition, joins, union sources, concrete-config descriptor reuse, or non-React adapters. The coverage map assigns those open cells to the React owner or a receiving owner for the relevant adapter and public observation. + +## Guide audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 authority and limits | Pass. The SSR guide states the standalone Query and factory-descriptor contract; the oracle and coverage map bound the claim. | +| ORC-002 independent judgment | Pass. Plain per-client maps predict rows without importing the query builder, materializer, binding cache, or live-query engine. Production receives copies of the model's seed rows. | +| ORC-003 visible responsibilities | Pass. The opening prose states law, model, histories, observation, and limits; the driver and comparison sit beside the executable history. | +| ORC-004 generated grammar | Not applicable. Two fixed histories are claimed, with no generated-history coverage claim. | +| ORC-005 production path and observation | Pass. The driver uses the public useLiveQuery hook under DbProvider, and compares public rows and $key after each settled publication and provider switch. Both cases execute and pass. | +| ORC-006 calibration | Pass. Three temporary mutants reached the intended comparisons and failed by assertion, as detailed below. None remains in the branch. | +| ORC-007 fixed and random campaigns | Not applicable. The oracle has no important generated property. | +| ORC-008 stateful-model minimality | Not triggered by this change. The existing two-map model retains the same state; the new history varies key collision and write kind. Separate maps are necessary because a write to A must leave B's expected rows unchanged. | +| ORC-009 vocabulary mapping | Pass. Each map is a model-only projection of one client's expected public rows. A model write corresponds to a Collection insert or update; the comparison cut is the settled public hook result, not a sync transaction or source snapshot. | +| ORC-010 failure fidelity and cleanup | Not triggered by shrinking or failure normalization. The fixed tests assert before unmount and use the React test harness for cleanup. | +| ORC-011 independent second formulation | No named shared semantic fault requires a second result model. The direct Collection formulation checks plan identity; it is not presented as a second row oracle. | +| ORC-012 review evidence | Pass for this bounded claim. This record names the exact semantic head, all guide outcomes, mutant outcomes, witness boundary, and open cells. | +| ORC-013 distinguishing boundary witness | Pass. The disjoint history rejects binding to the first client. The colliding history rejects a global row-ID cache that the disjoint history accepts. The $key comparison rejects a separate global public-key cache. | +| ORC-014 controlled-premise handoff | No real-provider claim is made. The mock sync factory supplies independent initial rows and local writes; real adapter timing and persistence remain outside this controlled React witness. | + +## Distinguishing evidence and closeout + +The first-client bound-query mutant returned A's row where the disjoint history expected B's row. It failed at the public row assertion. The colliding case also failed in that run, but the mutant's global cache carried state from the earlier case, so the disjoint case is the clean witness. A global row-ID cache passed the disjoint case and failed the colliding case at B's value: first instead of second. A global public-key cache failed the $key assertion: a instead of b. All three outcomes were assertion failures at the intended checkpoint, not setup failures or timeouts; the temporary mutants were restored. + +This evidence covers reusable factory descriptors, one prebuilt basic projection Query, two controlled React clients, independent insert or same-key update, and a mounted A→B→A switch at settled public-row checkpoints. No reachable counterexample was found in that bounded domain. The broader descriptor-binding law remains open for the cells listed in the coverage map; each needs a receiving oracle with its own public observation before a broader closure claim. + +The React oracle and existing useLiveQuery suite passed 62 tests in two files. ESLint, Prettier, and git diff --check passed. The SSR guide's two bare preloadLiveQuery calls were corrected to the documented config-object form in the reviewed semantic head. + +## Follow-up audit: React Effects and preparation + +Reviewed semantic head: a037c605221fa458e06796dd85ef81774b41c314 (2026-10-08). This entry supplements the earlier e4a002a0 audit; it does not revise that earlier verdict. + +The added Effect history uses the same prebuilt factory-descriptor Query under two DbClients with the same row key and different values. At initial publication, each `useLiveQueryEffect` must report its own client's public row. Switching one mounted Effect to the second provider must report the second row. The model is the pair of plain input rows, copied before seeding production; it does not use the query compiler to predict values. The public observation is the ordered `onEnter` row sequence at the settled test cut. The Effect witness covers initial enters and one A→B switch. It does not establish later source writes, on-demand loading, joins, unions, or non-React Effects. + +The new witness failed on the prior code head 8361d12ad during Effect setup with the unbound descriptor error. On a037c6052 it passed. A temporary hostile fixture made both clients supply the first client's row while retaining the second client's expected row; the oracle failed at its initial public-event comparison (`first` observed where `second` was expected). That was an assertion failure at the intended checkpoint. The fixture was restored. This checks that a wrong cross-client row result cannot pass merely because the two rows share a key. + +| Requirement | Follow-up outcome | +| --- | --- | +| ORC-001 authority and limits | Pass. The standalone descriptor contract and the existing two-client law also govern a client-aware React Effect; the new fixed history and open cells are stated above and in the coverage map. | +| ORC-002 independent judgment | Pass. Plain expected rows are copied before entering production and compared with callback values. | +| ORC-003 visible responsibilities | Pass. The oracle opening prose now names the Effect law, model, fixed history, callback observation, settled cut, and limits beside the executable witness. | +| ORC-004 generated grammar | Not applicable; this follow-up adds a fixed history. | +| ORC-005 production path and observation | Pass. The driver mounts the public hook under `DbProvider` and compares complete projected `onEnter` values after initial publication and a provider switch. | +| ORC-006 checker calibration | Pass. The hostile first-client-row fixture failed by assertion at the public-event checkpoint; the previous production code failed earlier during Effect setup. | +| ORC-007 fixed and random campaigns | Not applicable; no generated property was added. | +| ORC-008 stateful-model minimality | Not triggered; the two independent client inputs remain distinct because a provider switch changes the required event value. | +| ORC-009 vocabulary mapping | Pass. A model row is one client's expected projected public row; an `onEnter` event is the Effect's public observation, not a source change message. | +| ORC-010 failure fidelity and cleanup | Not triggered by shrinking. The fixed test unmounts both hooks after its comparisons. | +| ORC-011 independent second formulation | No additional shared-fault hypothesis is claimed for the fixed Effect history; the existing `useLiveQuery` two-client result is a separate consumer path. | +| ORC-012 review evidence | Pass for the bounded follow-up claim. This entry names the reviewed semantic head, all guide outcomes, the negative controls, and open cells. | +| ORC-013 distinguishing boundary witness | Pass. Same-key, different-value clients and the hostile first-client-row fixture distinguish the required client binding from a wrong cross-client row. | +| ORC-014 controlled-premise handoff | No real-provider claim is made. The controlled sync fixture supplies each client's initial row. | + +Two focused core tests also failed before the fix and pass on a037c6052: preparation of an unfinished builder without a client now returns the builder unchanged, and the core unbound-descriptor error no longer recommends React's `DbProvider` to callers in other frameworks. The focused core and React runs passed 16 tests across three files, with no type errors. React package build, ESLint, Prettier, and `git diff --check` passed. + +## Public IR compatibility follow-up + +Reviewed semantic head: 368bcbca1f8b94cd3d7e4e08537198c7c890a5ac (2026-10-08). The prior `IR.CollectionRef` exposed `collection` as an enumerable, writable own field for a concrete Collection. The descriptor implementation at c99228736 instead exposed an enumerable `source` field and a prototype `collection` getter. A spread lost `collection`, and assignment failed. + +The focused public-API test `packages/db/tests/query/collection-ref-compatibility.test.ts` failed on c99228736 at the own-key comparison. It passes on 368bcbca1. A concrete ref again exposes and permits assignment to its own `collection` field. Its `source` getter follows an assigned Collection. An unbound descriptor ref still keeps its descriptor until a receiving client binds a cloned plan, and reading its `collection` fails with the intended missing-client error. This fixed shape check is sufficient for the finite own-field contract; it makes no general oracle claim about arbitrary IR walkers over unbound plans. + +The focused DB and React runs passed 116 tests with no type errors. DB package build, the private-member minification check, changed-file ESLint and Prettier, and `git diff --check` passed. Generic traversal of unbound descriptor refs remains outside this compatibility claim; callers can distinguish those refs through `descriptor` before reading `collection`. + +## Distinct descriptor IR follow-up + +Reviewed semantic head: ad49d7427f76c4e9dae5d25b24f14d09dd3e07fe (2026-10-08). This entry supersedes the descriptor representation described in the prior public IR compatibility follow-up. It does not change that follow-up's concrete Collection shape result or the earlier React oracle verdicts. + +An unbound descriptor is now an `IR.DescriptorRef` with type `descriptorRef`, an own `descriptor` field, and no `collection` field. `IR.CollectionRef` again represents only a concrete Collection and retains its enumerable, writable own `collection` field. Placement cloning turns each descriptor ref into a concrete ref when a resolver is supplied. `IR.collectSourceRefs` traverses either source kind. The existing `IR.collectCollectionSources` return type remains concrete and reports the missing client if a descriptor is still unbound. Compilation makes the same check before optimization. + +The focused shape test was written first. On the prior head bfa4d1ffa, its builder observation failed by assertion: `collectionRef` appeared where `descriptorRef` was expected. On ad49d7427, it passes. The test checks the concrete own-field shape, the unbound discriminant and absence of `collection`, the bound clone's new source ID, and a finite IR position matrix for nested query, `unionFrom`, `unionAll`, join, and include sources. Each matrix case names its expected aliases independently of the traversal function, then checks unbound collection traversal rejects and bound traversal returns all concrete refs with distinct source IDs. It does not execute those query forms or compare their public rows. + +The React two-client oracle's contract, model, histories, driver, and public-row comparisons did not change. Its three tests pass on this head. The focused DB descriptor and identity run passed 115 tests, and the nearby optimizer, union, join, include, and identity run passed 447 tests, each with no Vitest type errors. DB and React builds, DB and React TypeScript checks, the private-member minification check, changed-file ESLint and Prettier, and `git diff --check` passed. The changeset is minor because exported `IR.From` now includes `descriptorRef`. Public row behavior for descriptor joins, unions, includes, on-demand sources, and other frameworks remains assigned to the coverage map's receiving owners. + +## Follow-up audit: committed Effects and nested placement + +Reviewed semantic head: 1413277f11f0fb51e44a9d3688ab0c780b350169 (2026-10-08). This entry records bounded evidence for the subsequent review and follow-up changes; it does not revise the earlier heads' observations. + +A committed `useLiveQueryEffect` may acquire a descriptor Collection without render deferral. The existing Effect row model still compares two clients at initial enter and a provider switch. A new fixed history uses one client's plain expected row, waits for its public `onEnter` event, then checks that `DbClient.dehydrate()` contains the source row and key. Before the change, the dehydration assertion received no Collection chunk. After the change, it passes. The effect now uses ordinary client materialization in its post-commit setup and has no deferred-source resume or rollback path. A controlled probe of the former setup reached a query-start error followed by a different resume error, showing why that cleanup could replace the original exception. Removing the resume path eliminates that second throw from Effect setup; the probe does not establish error ordering for unrelated source factories. + +A fixed same-hash history mounts a concrete query without a client, then changes that mounted hook to an unbound descriptor query with the same stable plan hash. It checks the public missing-client error at the attempted consumption cut, before any prior row can be returned. The test passes. Hash equality itself is intentional: the earlier two-client oracle and nested preload witness require one descriptor plan and each client's concrete form to share semantic identity. This witness covers the ordinary derived-identity hook path, not an explicit caller-provided `queryKey`. + +A focused core placement witness checks the inner source's IR immediately after a client-aware builder places a standalone descriptor Query. Before the change it observed `descriptorRef`; afterward it observes `collectionRef`. The existing two-client nested preload witness checks public rows at a later consumption cut. The placement witness does not claim public-row coverage for every nested form. A separate focused React regression checks that an Effect query function returning `undefined` reports a clear invalid-query error; before the change it threw a property-access TypeError. Both DB and React packages build, 54 focused DB tests, 253 neighboring subquery/union/join/include tests, and the full 376-test React suite pass with no Vitest type errors. + +The review's repeated-preparation work is real: a controlled three-prepare probe observed three source materialization calls. The ordinary React path prepares once per render, so the claim of several preparations per render is too broad. A bound-builder cache would have to preserve per-render source deferral and account for mutable exported IR; this audit does not establish a safe cache law or implement one. Compiler entry collects concrete sources before later `descriptorRef` branches, while direct IR traversal and Effect source extraction can still reach their own missing-client checks. The generic core error deliberately omits a React-only provider instruction; the existing core test protects that cross-framework wording. Public row behavior for descriptor joins, unions, and includes remains open. + +## Scope and preparation-work follow-up + +User decision on 2026-10-08: descriptor binding in Vue, Solid, and Angular adapters is outside this work's contract. Those adapters have no DbClient input. Their callers use concrete Collections. Svelte has a DbProvider path and remains an open receiving-adapter witness in the coverage map. This decision changes the coverage classification, not the current adapter behavior. + +The warm-client preparation probe on Node 24.19.0 used seven timed batches per shape. It called `prepareLiveQueryValue` with a new deferred set on each iteration. A concrete-Collection plan provided the same-shape comparison. Median prepare times were 0.61 versus 0.43 microseconds for one source, 0.98 versus 0.45 for one source under 16 nested queries, and 5.22 versus 2.99 for a 64-source union. Adding prepared-query identity gave 0.86 versus 0.73, 5.21 versus 4.57, and 13.57 versus 10.58 microseconds for those shapes. A counter observed one resolver call per descriptor source per preparation; the warm client reused its Collections. This isolated microbenchmark does not measure a React render or provider work. The measured extra work does not justify a bound-builder cache in this PR. A representative application profile showing material render cost would reopen that optimization and require a render-abandonment witness. + +## Follow-up audit: placement, React diagnostics, and resolver lifetime + +Reviewed semantic head: 34c600230 (2026-10-08); the fixes below began from ebe0dcc6d. A client-aware builder must bind every descriptor that it places before returning that query clause. The focused placement driver observes source-ref kinds inside the query callback, before `prepareLiveQueryValue` can rebind the returned tree. Its independent expected list names two union branches and direct, nested, materialized, and conditional include positions. Before the fix, union placement yielded two `descriptorRef` sources and includes retained a descriptor child. After the fix, all named positions yield `collectionRef`; the original standalone union builders remain unbound. This is a finite IR-placement check, not a public-row oracle. The two-client descriptor oracle and nested preload witness retain their public-row authority. Union and include rows under separate clients remain open in the coverage map. + +The React descriptor oracle's no-client history now requires the thrown message to name `DbProvider` at the attempted consumption cut, before a same-hash concrete query's prior rows can be reused. The previous message failed that assertion. A separate `useLiveQueryEffect` test reaches the same no-provider boundary after commit. The core missing-client test still requires framework-neutral wording. These checks cover the React remedy and core boundary, not every adapter diagnostic. + +An on-demand source whose `unloadSubset` throws supplies a controlled Effect cleanup failure. Before the hook change, cleanup discarded the rejected `dispose()` result without reporting the source error; after the change, React logs that exact error. `Effect.dispose()` already attaches an internal rejection handler, so the review's unhandled-rejection claim is stronger than the evidence supports. The focused test checks one unmount and one failing release callback; it does not claim a general disposal-history oracle. + +The resolver-lifetime issue remains open. A controlled public-path probe prepared a descriptor Query with a render deferral set, drained that set, then added a new descriptor join and subscribed to the resulting live query. The new source stayed `idle` and its sync callback ran zero times while the live query stayed `loading`. A candidate change that made the returned builder retain a normal client resolver started that source after release, but a distinguishing probe added the join *before* release and showed provider sync starting while the render was still abandonable. The candidate was removed. The design must define when a prepared builder's continuation stops using render deferral, with a witness for source start before release, after release, and after an abandoned render. The descriptor-binding owner and the live-query deferred-acquisition owner are the receiving boundaries; the current finite oracle does not encode that transition. + +The hash/equivalence difference is deliberate: stable query identity uses descriptor and concrete Collection IDs alike, while compiler subquery cache comparison inspects bound structure only after concrete-source collection. The same-hash, no-client React witness rejects reuse of old rows. The exported `IR.From` descriptor variant is covered by the minor changeset. The exported compiler's both-invalid input can report alias reuse before the missing-client error, while normal live-query construction reports the missing client first; no error-precedence contract selects one result for that mixed-invalid input. The lazy-target descriptor guard is unreachable from normal compilation after source collection, but keeps direct calls on the exported `From` type fail-fast and narrows that union for the remaining code. + +## Consumption-bound continuation follow-up + +Reviewed implementation head: f2f0bd94890dd27bfd1d2e8b9f6920186e01efd7 (2026-10-08). The user chose consumption-bound descriptor binding. This supersedes the earlier requirement in this record that a client-aware builder bind descriptors during `from`, `join`, `unionAll`, or include placement. Those earlier observations remain historical evidence for their reviewed heads. + +The law is that query construction and chaining keep descriptor sources in the plan and start no source sync run. A client-aware consumer binds the finished query to concrete Collections for that consumption. Its returned builder is a bound snapshot without a retained resolver. A later descriptor clause remains unbound until another client-aware consumption; a direct core consumer without a client reports the missing-client error. Reuse the original standalone Query across clients, not a prepared snapshot containing one client's Collections. The prior placement law predicted concrete refs inside the query callback; the new law predicts descriptor refs there and concrete refs after final preparation. The nested, union, and direct, nested, materialized, and conditional include placement tests now check both cuts. + +The source-start model predicts zero starts while a continuation is unconsumed or its React render remains abandonable, and one start for the eager fixture after a committed client-aware consumer acquires it. The core driver prepares a descriptor Query with a render deferral set, drains that set, adds a descriptor join, then preloads the continued query through `DbClient`. It checks that chaining neither materializes nor starts the new source, direct consumption without a client fails, and preload starts it once. A neighboring core history consumes the continuation before draining its deferral set: preparation binds the new source but starts it only after release. A captured intermediate builder from a query callback also retains no resolver. The React owner checks the continued query's public joined row after a committed `useLiveQuery` hook and observes zero materializations and sync starts when a Suspense render extends a prepared builder but never commits. + +Before the change, the new core after-release test failed by assertion at the planned ref-kind checkpoint: the new join was already a `collectionRef`, rather than a `descriptorRef`. The earlier public probe then saw that source stay `idle` and its live query stay `loading`. The rejected ordinary-client-resolver candidate started provider work before release. For checker calibration, a temporary hostile line materialized the late descriptor during the abandoned React render; the new comparison failed by assertion with one materialization where zero was expected. That line was removed, and the same test passed. These are distinct checks of the stale-deferral and premature-start wrong designs. + +| Requirement | Continuation follow-up outcome | +| --- | --- | +| ORC-001 authority and limits | Pass. The user selected consumption-bound binding; the standalone Query and React render contracts supply the client and commit boundaries. The bounded histories and open cells appear here and in the coverage map. | +| ORC-002 independent judgment | Pass. Expected rows come from fixed source input values; expected start counts come from whether a consumer committed. Neither expectation calls the resolver, builder, or source-start implementation. | +| ORC-003 visible responsibilities | Pass. The React oracle opening prose names the law, simple start model, legal histories, public observations, checkpoints, and limits beside the drivers and checks. Core placement tests supplement that public owner. | +| ORC-004 generated grammar | Not applicable. These are fixed histories, with no generated-history claim. | +| ORC-005 production path and observation | Pass. The drivers use public `prepareLiveQueryValue`, `DbClient.preloadLiveQuery`, and `useLiveQuery`; they compare sync callback counts and the public row at release, commit, and abandoned-render cuts. | +| ORC-006 calibration | Pass. The old implementation failed the late-ref assertion; the temporary premature-materialization fixture failed the abandoned-render comparison. The prior public probe and rejected plain-resolver candidate supply the adjacent lifecycle counterexamples. | +| ORC-007 campaigns and replay | Not applicable. No generated property was added. | +| ORC-008 stateful-model minimality | Not triggered. The added start model predicts counts at three fixed cuts and does not introduce a stateful reference machine. | +| ORC-009 vocabulary mapping | Pass. A model start is one source Collection sync callback invocation; a committed consumer is the public hook or preload path. The test does not equate render deferral with deferred acquisition. | +| ORC-010 failure fidelity and cleanup | No shrinking or failure normalization occurs. Assertions precede hook unmount; the temporary hostile fixture was removed after its intended failure. | +| ORC-011 second formulation | No additional shared-fault hypothesis requires another expected-result model. Core preload and React hook are different receiving paths but share preparation, so their agreement is not claimed as an independent semantic oracle. | +| ORC-012 review evidence | Pass for the bounded claim. This entry names the implementation head, old and new predictions, wrong-design outcomes, production paths, public observations, and open cells. | +| ORC-013 boundary witness | Pass. Before-release preparation and after-release continuation distinguish a stale set from a live one; the abandoned render and hostile fixture distinguish deferral from immediate startup. | +| ORC-014 controlled-premise handoff | No real-provider or all-render-schedules claim is made. The controlled eager sync fixture supplies the named start and row premise. | + +The full core run executed 11,638 runtime tests without an assertion failure; its first type check found test annotations and an older builder-constructor test that used the removed resolver parameter. After updating those tests, the full core TypeScript check and the focused 22-test DB run passed. The full React suite passed 380 tests, the Svelte suite passed 122 tests, and DB, React, and Svelte builds passed. Changed-file ESLint, Prettier, and `git diff --check` passed. This evidence covers a prepared builder continued before and after deferral release, an abandoned Suspense render, one committed React hook, and one DbClient preload. Concurrent React render schedules, union/include public rows under distinct clients, and consumption of one prepared snapshot under a different client remain outside the claim and in the coverage map's receiving cells. + +## Follow-up audit: independent readers, release failures, and preload identity + +Reviewed implementation head: d3d938f60 (2026-10-09), based on 270716d6d. This entry evaluates the new finite histories against that exact implementation. It does not promote the earlier two-client oracle to a claim about all adapter or render schedules. + +The first law is that an abandoned descriptor render starts no provider work, while a later direct `Collection.preload()` is independent demand and can start its source. The React Suspense driver observes zero sync starts before commit or direct demand, then one start and `ready` after the direct preload. On 270716d6d it failed at the latter checkpoint with zero starts. On d3d938f60 it passes. Its model is a start count governed by the two demand cuts, not a copy of the sync deferral code. Arbitrary concurrent render schedules remain open. + +The second law is that one failed deferred source start does not withhold another source's release. A `DbClient.preloadLiveQuery` union of two descriptor sources reports the first source's exact error and still starts the healthy second source, whose direct preload reaches `ready`. If preparation also fails, that preparation error remains primary after the deferred release attempts. The core owner states these predictions beside its fixed two-source history. The first history failed on 270716d6d at the second source's start count, then passed on d3d938f60. The Svelte receiving oracle runs the same release ordering through `$effect.root` and `flushSync`; its original path also left the second count at zero. The repair starts it and detaches the failed hook subscription. A temporary mutant that omitted the error-path observer release failed the public query Collection's `subscriberCount` assertion, one instead of zero. After a descriptor factory failure, a separate Svelte history checks that a later direct source preload starts once and reaches `ready`. These fixtures use synchronous eager sources; asynchronous failures and larger source sets remain open. + +The third law is local preload ownership: equal query hashes can reuse one preloaded result only when the concrete source Collection object at each query position is the same. The independent model assigns distinct source values before building plans. The driver checks the hash equality, first dehydrated row, second request's rejection, and unchanged first row. Concrete→descriptor, descriptor→concrete, and concrete→concrete collisions failed on 270716d6d because the second request succeeded; a descriptor and its own materialized Collection passed as the same-object control. An interim set-based repair passed those four histories but failed a swapped two-source union: it returned the first result although both source objects had exchanged query positions. Comparing source objects in traversal order rejects that mutant, and all five cases pass on d3d938f60. This law covers local preloads in one `DbClient`, not hydrated streams from another process or the semantics of different explicit query keys. + +The React Effect provider-switch history now writes to the old provider after the switch and expects no update, then writes to the new provider and expects one update with its value. A temporary mutant that skipped `effect.dispose()` failed at the old-provider update checkpoint. The production disposal path passes. This strengthens the existing Effect oracle without claiming a new production fix or a general Effect history model. + +The full core run passed 11,390 tests; its only 21 failures were in `oracle-replay.test.ts`, whose child processes could not write Vitest's cache through this worktree's borrowed dependency directory. The same file passed all 36 tests when that cache was accessible. Focused core oracles passed 228 tests before the final swapped-position addition, and its five identity cases passed afterward. The full React and Svelte suites passed 360 and 117 tests with local source aliases. DB, React, and Svelte type checks, changed-file ESLint and Prettier, the DB build, generated-error-doc check, production-error bundle check, and `git diff --check` passed. The temporary mutants and local test aliases were removed. The coverage map names the remaining legal histories and receiving owners; the bounded cases above have no known reachable counterexample. + +## Follow-up audit: failed preload and hook-first source identity + +Reviewed semantic head: e22a2bbf4 (2026-10-09), based on c2370424b. The external ten-finding review reported code-reading observations without execution evidence. This entry records the independently checked behavior, the repairs, and the remaining policy questions. It does not claim that hydration-first provenance or Svelte recovery after a thrown effect has been specified. + +The failed-preload law is that a query whose deferred source start throws has no result to stream or reuse. At the synchronous error cut, `DbClient.dehydrate({ shouldDehydrateLiveQuery: () => true })` must omit it. A later equal-hash query with a healthy replacement source must publish that source's row. The deferred-acquisition oracle's independent model predicts no result for the first attempt and one `recovered` row for the replacement. Its public driver calls `preloadLiveQuery` and observes the exact thrown error, both sources' start counts, dehydration at the error cut, and the replacement row after a later successful preload. On c2370424b the new dehydration assertion failed because a pending query remained streamable; on e22a2bbf4 it passes. A same-options retry reaches the failed source Collection's terminal error rather than waiting on the old pending stream. It does not automatically restart that source, as the live-query architecture specifies for dependent queries after source failure. These witnesses cover synchronous eager starts and a two-source union. Asynchronous startup failure and larger source sets remain with the deferred-acquisition owner in the coverage map. + +The local stream-identity law is that one `DbClient` may reuse an equal-hash result only when its concrete source Collection objects match in query order. The earlier core preload oracle already rejected concrete/descriptor collisions, source swaps across union branches, and accepted the same object. This review exposed a missing registration origin: a pending React Suspense hook stream followed by a core preload. The React descriptor oracle now builds two same-ID sources with equal plan hashes, starts the hook stream over the first, and asserts that the second source rejects before the stream can answer it; preloading the first source is the neighboring legal case. On c2370424b the different-source call returned the first stream's promise instead of throwing. On e22a2bbf4 the different-source assertion and same-source control pass. A hydration-first record is different: its source Collection lived in another process, and the received snapshot has no local source-object list. The SSR guide promises matching semantic hashes across server and browser objects. Whether an explicit browser preload with a colliding hash should reuse or replace that hydrated record requires an API rule and a receiving hydration-first oracle; the local object-identity check cannot decide it. + +The React diagnostic law requires a missing `DbProvider` remedy when a descriptor query is consumed without a client, regardless of query identity mode. The prior oracle covered derived identity. The new `queryKey` assertion failed on c2370424b with generic core error 113. The repaired `queryKey` and legacy `deps` histories report the provider hint before building a live-query Collection. The core error remains framework neutral. A focused source-extraction test separately found that a bare builder yielded no sources before repair, while a `{ query }` config did; both now yield the same concrete Collection. The review's proposed development/production explanation is unsupported because `query` is absent from `packages/db/mangle-cache.json`. + +The Svelte catch still points `currentObserver` at a disposed observer, but `dispose()` is idempotent. A public hook probe changed the reactive query after the thrown effect and observed no resumed subscription. A separate minimal Svelte effect probe showed that an effect which throws does not rerun when its tracked state changes. Thus the claimed later read through that observer and second disposal were not established. Recovery within the same effect root would need an explicit Svelte contract and a public row/subscription oracle. The coverage map keeps that path open. Normal compilation rejects an unbound `DescriptorRef` before optimization; directly invoked `optimizeQuery(QueryIR)` can still receive one, so descriptor branches there are not universally dead. A bound-IR redesign and the measured repeated-preparation cost remain separate design and performance work. The earlier recorded user decision waits for an application render profile before introducing a binding cache; this repair removes one unused Effect source walk without claiming a render-level speedup. + +| Requirement | Outcome for the repaired oracle claims | +| --- | --- | +| ORC-001 authority and limits | Pass for the bounded laws above. `DbClient` preload/SSR semantics, the SSR guide, the live-query architecture, and the existing React diagnostic contract supply authority. Hydration provenance and Svelte recovery are explicitly unselected rules. | +| ORC-002 independent judgment | Pass. Expected stream presence, source identity, rows, start counts, and provider remedy come from the stated contracts and fixed source values, not the registration map or resolver under test. | +| ORC-003 visible responsibilities | Pass. The deferred-acquisition and React oracle prose names the law, finite histories, simple model, public path, observation cuts, and limits beside the tests. | +| ORC-004 generated grammar | Not applicable: these additions are fixed legal histories, with no generated-history claim. | +| ORC-005 production path and observation | Pass for the named cuts. The tests call public preload, dehydration, and React hooks; assertions observe thrown errors, stream presence, rows, and source starts. They do not claim hydration-first or recovery after a thrown Svelte effect. | +| ORC-006 checker calibration | Pass. Original c2370424b fails at the pending-stream dehydration, hook-first collision, `queryKey` diagnostic, and bare-builder source checkpoints; e22a2bbf4 passes those same checks. The same-source stream and config-form source checks distinguish overbroad rejection. | +| ORC-007 campaigns and replay | Not applicable: no generated property was added. | +| ORC-008 stateful-model minimality | Not triggered: fixed rows, source object identities, and expected start counts need no stateful reference machine. | +| ORC-009 vocabulary mapping | Pass. A source position is one ordered Collection source in the query; a stream is one `DbClient` live-query record; a source start is one sync callback. The tests keep a failed query distinct from its still-errored source Collection. | +| ORC-010 failure fidelity and cleanup | Pass for these fixtures. The exact source error is asserted; callbacks and promises are not normalized into success. Clients, Collections, and React hooks are released in `finally` or unmount. | +| ORC-011 second formulation | No separate shared-fault semantic hypothesis was named. The core preload and hook-first React paths share source extraction, so their agreement is not claimed as an independent model. | +| ORC-012 review evidence | This entry ties the old and repaired observations to exact commits and names the unresolved law cells and owners. It claims closure only for the bounded failure, local source-identity, and diagnostic histories. | +| ORC-013 boundary witness | Pass for those bounded laws: failed versus healthy replacement, same versus different local source object, and derived versus explicit React identity reach and distinguish the relevant premises at their stated cuts. Hydration-first remains outside that boundary. | +| ORC-014 controlled-premise handoff | The eager synchronous fixtures supply source-start behavior. No real-provider or all-render-schedules claim is made, so no provider handoff is asserted. | + +On the repair commit, 227 focused DB tests, nine React descriptor oracle tests, and two Svelte release oracle tests pass; the new same-options check is included in the DB count. Changed-file ESLint, Prettier, and `git diff --check` pass. Earlier neighboring core, React, Svelte, and type checks are recorded in the working audit; they were not rerun for this documentation entry. The new helper also combines the three deferred-source release loops, React source extraction delegates to core with its framework-specific message, and the changeset now names React and Svelte patch releases. The remaining review decisions are hydration-first preload provenance and whether a failed Svelte hook should recover in place. The coverage map assigns both receiving witnesses; repeated preparation retains its prior profile trigger. + +## Main merge validation + +Reviewed merge head: 56b1cf5765ddfd4180e595ae47847f44d0234fd7, combining 86a0446ca3ddd54076922019e282ab369a70e8bf with origin/main db7ff28d36f122b40594b4437cafbad6c93164aa. The generated error files were the only textual conflicts. Main assigned error-class code 233 to `DuplicateTransactionIdError`, which collided with this PR's query-builder error site. The merged tree preserves main's 233, this PR's preload collision code 234, and main's bucket-facade code 235; the query-builder site uses 236. The site fixture and generated documentation agree. This code-number resolution changes no development message or descriptor query behavior. + +After the merge, 246 focused DB tests passed across production error messages, preload identity, deferred acquisition, and preparation, with no TypeScript errors. The React descriptor and source-ID suites passed 28 tests; the Svelte descriptor release suite passed two. The DB build, generated error-document check, and ESM/CommonJS production error-text check passed. These runs validate the reviewed paths on the merge head. They do not settle hydration-first provenance, Svelte recovery after a thrown effect, asynchronous source-start failure, or an application-level preparation-cost budget. + +## Two-source placement and pooled observer follow-up + +This local audit starts from `d117215ea8644fa27b54df6075f5efff8bdbff02`, before reconciling PR #2079. The descriptor law is that one standalone plan consumed under two `DbProvider` clients reads each client's own sources, even when those sources appear in different query positions. The React primary oracle now crosses join, nested FROM, union, and include placements of parent and child descriptors. A plain per-client value map predicts complete selected rows for equal row keys with different values. The public hook is checked after initial publication, a child write in the first client, and a parent write in the second. All four forms pass. A temporary resolver mutant that reused the first client for every binding made all four fail at the second client's initial public-row comparison: `FIRST PARENT`/`FIRST CHILD` appeared where `SECOND PARENT`/`SECOND CHILD` were required. The mutant was removed. The `waitFor` comparison eventually reported the exact row mismatch; this was neither a setup failure nor proof about arbitrary scheduling. This expansion found no new production defect in those four placements. On-demand requests, more sources, and other write interleavings remain outside this finite matrix. + +The deferred-acquisition law also covers an observer's public preload of a config-free pooled view. Its independent model predicts one new source start for an idle eager source, none for an already running eager source, and a ready snapshot containing the source rows in either case. The primary deferred-acquisition oracle crosses those source states with an observer that has no client and one with an SSR cleanup client. On the original `d117215ea8` implementation, all four cells failed at the intended public boundary: the no-client preload rejected with `TypeError: Cannot read properties of undefined (reading 'utils')`, while the SSR-client observer failed its construction assertion with the same error. A pooled view has no query-Collection config, but the observer passed that absent value to the builder lookup. Guarding both the preload source lookup and SSR resource registration makes all four cells pass. This is a new production bug found while expanding #2077 coverage; it is separate from the #2079 alias-scope matrix. The witness covers synchronous eager sources, not on-demand or asynchronous source acquisition. + +The 13-test React descriptor suite and full 210-test deferred-acquisition oracle, including all four pooled-observer cases, pass on the repaired local tree. DB and React TypeScript checks, changed-file ESLint, Prettier, and `git diff --check` pass too. The final combined-tree validation and CI remain to be recorded after PR #2079 is integrated. + +## Receiving the merged alias-scope work + +PR #2079 entered main as squash commit `f7ac2c63a3cabc864caf38b7a4e966088cfe73bb`. This integration combines its lexical binding IDs with descriptor materialization. Source clones keep their binding IDs when a descriptor becomes a Collection, and the compiler uses #2079's per-scope alias validation. The merge assigns code 236 to #2079's reused-source admission error and code 237 to the descriptor query's invalid-builder result; generated error documentation and the site fixture agree. + +The React primary oracle now has a receiving include with a descriptor parent and child under two clients. Its shadowed and renamed child-alias variants compare full selected rows after initial publication, a first-client child write, and a second-client parent write. The child projects the captured parent's value. Disabling lexical parent projection in a temporary alias-only evaluator made the shadowed variant fail at the initial public-row assertion: `FIRST CHILD` replaced `FIRST PARENT`; the renamed control passed. Dropping binding-ID transfer during descriptor materialization left those rows green but failed both variants' public stable-hash comparison after binding. Both mutants were restored. This separates row correctness from plan-identity enforcement; it does not claim arbitrary include depth, lazy joins, or write order. + +The existing #2077 preload-identity owner gained an adapter observer path for config-free pooled views. Its model predicts that an existing stream hash may be reused only for the same source object, and that the pooled view still completes its own preload. With the old observer path, both same-source and different-source cells failed at preload: it called `_markPreload()` on a pooled view, which has no such method. A separate mutant that skipped only the new pooled source check made the different-source cell resolve instead of rejecting, while the same-source control passed. The repaired observer obtains the pooled view's single source without materializing its public Collection, checks the existing stream's source identity, and awaits both the stream and the pooled source preload. Both cells now pass. This is a newly found #2077 integration bug, not a #2079 alias-scope failure. Hydrated streams without local source provenance remain outside this bounded local-identity law. + +On the reconciled worktree, nine focused DB files passed 661 tests, including the #2079 primary and generated scope oracles, the #2077 deferred-acquisition oracle, and the expanded preload-identity owner. The React descriptor and `useLiveQuery` suites passed 75 tests. DB source and test TypeScript, React TypeScript, changed-file ESLint and Prettier, the DB build, generated error-document check, minified public API, and ESM/CommonJS production error-text checks passed. The Svelte descriptor release oracle passed two tests when its local runner resolved `@tanstack/db` to this worktree's source; its borrowed dependency link otherwise loaded an older DB package without `resumeDeferredLiveQueryCollections`. The temporary alias config was removed. The first simultaneous DB test attempt failed in coverage-file output because two Vitest runs shared one coverage directory; rerunning with coverage disabled passed at the assertions. CI still needs to run on the committed combined head. + +## Combined receiving gate for outer joins and hydrated streams + +This local integration starts from #2077 `6e581eb5a9708feb0785f368d22cf0011abc40ed` and combines #2095 `a8edee4a2d038ff9e0af1530a92abc32721fbb27`. Neither published branch was changed during the experiment. The semantic authority is the descriptor contract in `docs/guides/ssr.md`, the lexical alias law in the primary alias oracle, and the live-query source failure rules in `packages/db/src/query/live/ARCHITECTURE.md`. A portable hydrated hash carries result rows, not server Collection object identity. The first local committed hook or explicit preload therefore claims its ordered browser source objects; later local same-hash consumers must use those objects. A newer hydration chunk updates the result without revoking that local claim. + +The React primary owner now receives the lexical outer-join behavior through descriptor materialization. Its independent model is plain per-client maps: pair main and joined rows by ID, retain the unmatched side required by RIGHT or FULL, then apply parent correlation on the selected main or joined side. Neither alias text nor compiler routing computes the expected rows. Eight cells cross RIGHT/FULL, correlation side, and eager/on-demand sources; each runs shadowed and renamed child aliases under two clients with equal IDs and distinct values. The public hook rows are compared after initial acquisition, a main update, main removal and return, joined removal, and parent update. Source starts, on-demand request presence, and prepared/unbound stable identity are observed separately. A temporary absent-main merge mutant initially survived a direct joined-source cell: that source did not carry the parent route. The receiving driver then used a legal parent-dependent joined QueryRef. On that path the mutant failed at the shadowed on-demand cell's initial public-row comparison; restored production passed. This evidence is bounded to one join in the receiving adapter. The core alias owner separately covers its multi-join histories. + +The core preload-identity owner adds settled and pending hydration-first streams. Its simple model gives the first local preload source ownership, then predicts rejection for a different same-ID source at the same hash. It also predicts that a later hydration chunk changes the result row without changing that claim. Both cells failed before the source-claim repair: the conflicting request reused the first stream. Preserving `record.sources` through a newer hydration chunk was needed for the second cut. The React receiving test holds browser source readiness after it hydrates a server row, commits an ordinary hook, checks that row, rejects a conflicting preload, then releases readiness and checks the browser row. Removing the observer's attach-time claim as a temporary mutant made the conflicting-preload assertion fail while the source was still unready; restoring the claim made the same test pass. These are public observations at the ownership and handoff cuts, not an inference from internal registration state. + +The deferred-acquisition owner now controls a later `markError` from the first of two descriptor sources. Before that call, the query stream is pending and both sync runs have started. After it, the preload rejects, dehydration omits the failed stream even when asked to include pending streams, the peer source remains ready, and a healthy same-hash replacement publishes its own row. A temporary mutant that allowed errored records into dehydration failed at the stream-omission assertion; restored production passed. The test exercises one later failure order, not arbitrary asynchronous schedules or larger source sets. + +A controlled Svelte probe changed a reactive query after an uncaught synchronous `$effect` error and saw no effect rerun or healthy source start. A minimal Svelte effect behaved the same way. The Svelte receiving oracle now retires the failed root and proves that a fresh effect root reads an independent healthy query. The accepted lifecycle boundary is that an uncaught effect error terminates that effect; in-place reactive recovery is not promised. Ordinary reactive replacement that does not throw remains a separate coverage cell. + +On the initially resolved combined tree, the focused DB owners passed 398 tests, the wider DB query runtime suite passed 4,903, the full React runtime suite passed 397, and the full Svelte runtime suite passed 125. Standalone DB and React TypeScript checks, Svelte checking, and the DB build passed. The new held-readiness and later-failure mutants were run after those wider suites: both failed by assertion at their intended checkpoints and passed after restoration. These results establish a bounded local integration; the final formatted tree, pushed head, CI, and external review still require separate verification. + +## Suspense preload on a pending hydrated stream + +The follow-up starts from pushed combined head `712e572bf`. A normal +`useLiveQuery` render waits until its committed subscription to claim a +hydrated stream's local source objects. `useLiveSuspenseQuery` has a different +request boundary: it preloads during render so React receives a promise to +retry. That preload counts as local demand even if the render is abandoned. +The first source object therefore claims the pending hydrated hash; a later +same-source preload is accepted and a different same-hash source rejects. +This is the existing `DbClient` source-identity rule applied at the Suspense +preload cut, not a claim that every React render starts provider work. + +The primary React descriptor oracle holds a hydrated stream result, renders a +Suspense component that never commits, observes the fallback and one source +sync start, then calls public `DbClient.preloadLiveQuery` with the conflicting +and same-source plans before releasing the result. The two plans have equal +stable hashes and different Collection objects. A temporary mutant that +removed the observer's existing-stream source claim made the conflicting +preload resolve; the assertion failed at that cut. Restored code passed. The +same-source control initially ran first and masked the mutant by claiming the +source itself; the oracle was corrected before this RED result was counted. +The test does not cover arbitrary concurrent Suspense renders or cancellation +of already started provider work. + +On this repaired combined tree, the primary React owner passed 25 tests and +the broader React runtime suite passed 377. React TypeScript, changed-code +lint, and formatting checks passed. The runtime run disabled Vitest's built-in +typecheck because its cross-package test imports fall outside the configured +runner root; the separate TypeScript check passed. Final pushed-head CI and a +fresh external review remain distinct gates. + +## Failed stream followed by a replacement observer + +CodeRabbit review 5480955302 covered combined head `c70142812` and found a +receiving path missing from the controlled asynchronous failure history. The +law is that a failed client stream has no result to reuse and no source-object +claim. A new observer for a healthy replacement Collection may subscribe with +the same hash. An active or successful stream still rejects a different source +object, as the separate preload-identity owner checks. This is the existing +same-hash result rule at a new consumer boundary, not a new retry policy for a +source Collection that entered terminal error. + +The deferred-acquisition owner's plain-row model predicts `recovered` from +the replacement source. Its legal history now starts two descriptor sources, +fails the first after startup through controlled `markError`, checks that the +pending client stream rejects and disappears from dehydration, then subscribes +a prepared replacement observer before any new client preload. At the attach +cut it must not throw a source-mismatch error; after preload its public snapshot +must be ready, contain only the replacement row, and expose no stale stream +error. The existing later same-hash client preload checks that the replacement +also publishes through `DbClient`. This adds observer subscription to the +failure history's consumer dimension. The active-stream same-source and +different-source controls remain in the preload-identity owner. + +On the reviewed head, the new attach assertion was RED: `_assertLiveQuerySources` +compared the replacement against the failed record's old sources and threw +`different source Collections`. This was an assertion failure at the intended +attach cut, not a timeout or setup failure. Treating an error record as +non-claiming before consulting the old preload entry made the focused test +GREEN. Both primary owners then passed 220 tests. The witness covers one later +failure, one observer, an eager replacement, the attach cut, settled public +rows, and subsequent client dehydration. Concurrent replacement observers, +other asynchronous orders, and later conflicting preloads while no reusable +client stream exists remain outside this finite history. + +## Failed-stream boundary sensitivity follow-up + +This self-review starts from pushed head `d99a068c6`. The prior observer witness +used a controlled later `markError`; the primary deferred-acquisition owner now +also observes a synchronous source-start throw. The expected result comes from +the same source-ownership rule, not from the shape of `DbClient`'s maps: while a +pending stream can return the original sources' rows, a different same-hash +source must be rejected. Once that stream fails, it has no result to lend and +no source-object claim. A healthy replacement observer can attach before a +new client preload and show only `recovered` at its settled public snapshot. +The separate peer source must still reach `ready` after the throw. + +Two temporary production mutants reached their intended comparisons. Returning +from `_assertLiveQuerySources` for every existing record let the conflicting +preload through and failed the pending-stream `toThrow` assertion. Removing the +error-record guard made the synchronous replacement observer throw `different +source Collections` at its attach assertion. Both mutants were restored. The +unaltered `d99a068c6` implementation passed all four fixed failure histories. +The earlier suspected stale-preload-entry bug was therefore not reproduced; +deferred-source release retires the failed registration at this cut. This new +history strengthens the observer recovery boundary but does not establish +arbitrary failure schedules, concurrent replacement observers, or a provider +outside the controlled source fixture. + +| Requirement | Follow-up outcome | +| --- | --- | +| ORC-001 authority and limits | Pass. The source-ownership and failed-stream laws are stated beside the fixed histories; the finite limits appear above and in the coverage map. | +| ORC-002 independent judgment | Pass. The expected single row is the replacement fixture's input, and the rejection boundary follows whether the old stream can still return a result. No client lookup or status classifier computes the expectation. | +| ORC-003 visible responsibilities | Pass. The owner's prose names the law, two failure orders, real preload and observer paths, public rejection, readiness, row and dehydration cuts, and their comparisons. | +| ORC-004 generated grammar | Not applicable. These are fixed histories, not a generated-history claim. | +| ORC-005 production path and observation | Pass. `DbClient.preloadLiveQuery`, observer subscription and preload, source Collection readiness, snapshots, and dehydration are exercised at the stated cuts. | +| ORC-006 checker calibration | Pass. Both temporary mutants failed by assertion at their intended boundary, rather than by setup failure or timeout. | +| ORC-007 campaigns and replay | Not applicable. No generated property was added. | +| ORC-008 model minimality | Not triggered. The fixed expected-result rule adds no state to the independent grammar model. | +| ORC-009 vocabulary mapping | Pass. A pending stream means a client live-query result that can still settle; the source Collection and public snapshot keep their glossary meanings. | +| ORC-010 failure fidelity and cleanup | Pass. The observer histories preserve their assertion as the primary failure and dispose the observer, live-query Collection, and client in `finally`; no shrinking or failure normalization occurs. | +| ORC-011 second formulation | Not triggered. No shared-fault hypothesis in the row expectation was identified; the pending and failed cuts distinguish source-claim lifetime. | +| ORC-012 review evidence | Pass for this bounded self-review: it records the base head, model prediction, paths, cuts, mutant outcomes, and unresolved cells. | +| ORC-013 boundary witness | Pass. The pending same-hash conflict and the failed-stream replacement are adjacent legal cuts; the two mutants distinguish overly broad and overly persistent source claims. | +| ORC-014 controlled-premise handoff | No real-provider claim is made. The synchronous throw and later `markError` are controlled source Collection premises. | + +## First subscription rejects before retaining a listener + +The 2026-10-11 user review examined #2077 at `a1863ab83`. An active client +stream may be reused only by the same ordered source Collections. A conflicting +observer's first subscription must reject without retaining its listener, so a +retry while the stream remains active must reject again. This is the existing +same-hash source-identity law at the ordinary observer subscription boundary. +The earlier preload-identity owner checked direct and pooled preloads, but did +not include an ordinary observer's subscription and retry in its finite +consumer history. + +The owner now uses two plain sources with the same ID and query hash but +different values. Its independent rule accepts the same source and rejects the +other. The public driver first preloads one source into a `DbClient`, then +subscribes an observer over either the same source or the other source. The +same-source control preloads and reads the `first` row; the conflicting cell +asserts rejection at both the first subscription and its immediate retry. +With the old check in `attach()`, the first call threw after adding a listener, +and the retry returned without throwing. This was an assertion RED at the retry +cut. Moving the check before listener registration made all 11 owner cases +GREEN. Five surrounding DB oracle files passed 416 tests, and DB source and +test TypeScript checks passed. This covers a source conflict on first attach, +not arbitrary failures later in `attach()` or concurrent subscription orders. + +For ORC-001–003 and 005, the owner places the source-identity contract and +limits beside its plain-row model, finite same/different history, public +observer driver, and subscription/row comparisons. ORC-004 and 007 do not +apply because no generated property was added. ORC-006 and 013 are satisfied +by the original assertion RED and the same-source opposite control. ORC-008 +does not apply because the model gained no state. ORC-009 maps the model's +source relation to ordered Collection-object identity, rather than a client +cache status. The `finally` cleanup retains the primary assertion and releases +the observer, view, client, and sources (ORC-010). There is no named semantic +shared-fault hypothesis requiring a second formulation (ORC-011), or claim +about a real external provider (ORC-014). This record gives the bounded +review evidence and remaining attach paths required by ORC-012. + +## Observer preload and no-provider diagnostics — 2026-10-11 review follow-up + +The corrected first-pass review supplied two legal consumer cuts absent from +the primary owners. `LiveQueryObserver.preload()` promises a `Promise`: +reusing an active same-hash stream over different source objects must reject +that Promise, while the existing stream keeps its row. A descriptor consumed +without a `DbProvider` must name that remedy in either React hook, even when +an explicit `queryKey` or legacy dependency list skips derived identity. +The core source-identity rule and React no-client diagnostic already supply +these expectations; the new cells change no product policy. + +The preload-identity owner now calls `observer.preload()` directly in pooled +and graph views over a settled stream and in a graph view over a pending +hydrated stream. It checks the call does not throw, its returned Promise +rejects on a different same-hash source, and the first stream's public row +stays intact. The same-source pooled cell remains the accepted neighbor. +Before repair, both settled conflict cells failed at `not.toThrow()`; the +pending cell reaches the same guard. One `try` block now converts source-check, +pooled preload, and graph preload throws into Promise rejection. The owner +passed 12/12 after repair. The React descriptor owner added both explicit +identity forms of `useLiveSuspenseQuery`; the query-key cell was RED with the +generic core message. Passing client and hook name into the Suspense source +key check made both forms GREEN, and the owner passed 25/25. + +The review also described a reentrant stream registration: after an observer +starts a preload, a source-start or adapter callback can register a conflicting +same-hash record before the observer registers its own promise. The primary +preload-identity owner models this collision at `DbClient._registerLiveQuery` +with a controlled later rejection. On the prior order, its public +`unhandledRejection` observation was RED after the synchronous source error. +Attaching the rejection handler before checking the conflict made the owner +GREEN at 13/13. This is a direct internal registration-boundary witness; no +official adapter was shown to trigger that reentrancy. It proves the discarded +promise is observed at this cut, not every SSR scheduling order. + +The owner contract, plain source rows, same/different source grammar, real +observer and React hook paths, and call/settlement/public-row checkpoints are +stated beside the tests. The original RED assertions and controlled later +rejection calibrate the comparisons; no production classifier computes the +expected source relation. The finite model does not include concurrent first +claims, another framework's message, or a real server stream. + +## A consumed client result releases its source claim + +CodeRabbit review 5483867857 covered `da77a744cb7ef0443994732a8e557aae37a7ebc0` +and found one more source-identity cut. A reusable client result claims its +ordered source Collections while it can still answer another consumer. Once a +subscribed observer's live result becomes authoritative, the client consumes +that result. A retained preload entry cannot extend its source claim. The SSR +guide now states this lifetime explicitly. The earlier owner correctly checked +active, failed, and hydration-first streams, but its ordinary-observer grammar +never consumed a successful result before a second subscription. + +The expanded primary owner starts with two same-ID source Collections holding +different plain rows. It preloads the first query, subscribes and preloads its +observer, then waits until public `DbClient.dehydrate()` omits the consumed +stream. While the first observer stays attached, a second observer subscribes +under the same hash with either the same or the other source object. The +independent rule predicts acceptance in both cells and the second observer's +own source row. Existing active-stream cells reject the other source before +consumption, so the two checkpoints distinguish a claim tied to a reusable +result from a permanent claim tied to a hash or preload entry. + +On the reviewed head, the different-source cell failed at the second public +`subscribe()` assertion with error 234; the same-source cell passed. This is +the original implementation failing at the intended comparison, not a setup +failure. The repair makes `_assertLiveQuerySources` return when no client record +exists, just as it already returns for a failed record. The fallback to a +preload entry remains only while a record can be reused. Both new cells and +the 13 earlier owner cases passed after repair. Seven related DB test files +and three React receiving test files also passed. This evidence covers one +settled preload, one authoritative observer, then one same/different observer +subscription and row read. Concurrent first claims, other adapters, and +arbitrary handoff schedules remain outside the finite history; the coverage +map retains these limits. + +| Requirement | Consumed-result follow-up | +| --- | --- | +| ORC-001 authority and limits | Pass. The SSR guide and owner state the reusable-result source claim and the finite history above. | +| ORC-002 independent judgment | Pass. Two plain source rows and the active/consumed law predict acceptance and each row without consulting the client's cache classifier. | +| ORC-003 visible responsibilities | Pass. The owner's opening prose and nearby test name the law, same/different history, public preload/observer path, dehydration cut, subscription call, and row comparison. | +| ORC-004 generated grammar | Not applicable. The two cases are a bounded enumeration, not a generated-history claim. | +| ORC-005 production and observation | Pass. Real `DbClient.preloadLiveQuery`, two public observers, `dehydrate()`, subscription, and snapshot data reach the stated cuts. | +| ORC-006 calibration | Pass. The original reviewed implementation failed the different-source cell by assertion at the second subscription. | +| ORC-007 campaigns and replay | Not applicable. No important generated property changed. | +| ORC-008 model minimality | Pass. Active and consumed must remain distinct: the legal different-source subscription rejects before consumption and succeeds after it. | +| ORC-009 vocabulary | Pass. The model's active/consumed result maps to whether a client stream is reusable, while source Collection and public row keep their glossary meanings. | +| ORC-010 failure fidelity | Pass. The assertion remains the primary failure, and `finally` releases both observers, live-query Collections, client, and sources. No shrinker replaces the failure. | +| ORC-011 second formulation | Not triggered. No independently plausible shared error in the two-row expectation was identified; the active/consumed opposite cuts challenge the boundary. | +| ORC-012 review evidence | Pass for this bounded repair: the original head, RED/GREEN cut, neighboring control, paths, limits, and every applicable requirement are recorded here. | +| ORC-013 boundary witness | Pass. The active different-source rejection and consumed different-source acceptance distinguish a permanent hash claim from the stated lifetime. | +| ORC-014 controlled-premise handoff | No real-provider claim is made. The synchronous source fixture establishes the readiness premise for this core API history. | + +## Failed stream followed by newer hydration + +CodeRabbit review 5483962718 covered `3a8787998c58d74df9e383ef274cf06c1d37bc7c`. +It found that `DbClient.hydrate()` copied a failed stream's source Collection +claim into a newer hydrated result. The accepted law is that a failed stream +has no reusable result and no source claim. A newer hydrated result carries a +portable hash but no local Collection object identity; its first healthy local +observer may claim its source. The SSR guide states this boundary. The earlier +preload-identity owner checked newer hydration after a successful local claim, +and the deferred-acquisition owner checked a replacement observer directly +after failure. Neither crossed failure with a subsequent hydrated result. + +The primary preload-identity oracle now starts a real source Collection whose +sync run later calls `markError`. It observes a pending client stream, its +rejection, and omission from public dehydration. It then hydrates a newer +result with either a settled snapshot or a held result promise, and subscribes +a healthy same-hash observer backed by a different Collection object with the +same ID. The independent two-row model predicts acceptance at subscription +and the replacement source's row after preload. Both cells were RED on the +reviewed head at the `not.toThrow()` subscription assertion with source error +234. The reviewer's suggested source-copy guard alone left both cells RED: +`_assertLiveQuerySources()` still found the failed stream's source objects in +the retained preload entry. The repair discards that entry when hydration +replaces an error record and copies sources only from non-error records. Both +new cells then passed. Existing active-stream and hydration-first cells are +the opposite checkpoints: a different source must still reject while a +reusable result has a local claim. + +This establishes the failure-then-hydration transition for one later source +failure, one newer settled or pending hydrated result, and one healthy observer +at its public subscription and row-read cuts. It does not enumerate concurrent +first claims, other failure schedules, multiple hydration chunks after the +replacement, or other adapters. The coverage map assigns those limits. The +separate inline documentation finding was also accurate: the React receiving +oracle already shows that `useLiveSuspenseQuery` can preload and claim during +render without a commit, so the SSR guide now names that timing explicitly. diff --git a/docs/contributing/oracle-reviews/issue-2085-leader-close-red.md b/docs/contributing/oracle-reviews/issue-2085-leader-close-red.md new file mode 100644 index 0000000000..989bda1dee --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2085-leader-close-red.md @@ -0,0 +1,722 @@ +# Issue #2085: leader close during a source commit + +Review date: 2026-10-08. Source: [issue #2085](https://github.com/TanStack/db/issues/2085), including its setup, result, and three proposed responses. Baseline commit: `f6aba314e44afdfa413d6d70bf8e272dfa67af85`. This record describes the uncommitted `codex/issue-2085-repro` worktree diff against that commit. The source has no file/line references or severity labels and no visible comments. The claim ledger was written before testing and retains source order. + +## Reviewer assessment + +This is an issue report, not a pull-request review. Its stack trace and failing phase accurately identify the coordinator and persisted wrapper boundaries. The six-run count and proposed remedies are useful hypotheses, but they do not decide the recovery contract. One report does not support a hiring recommendation. + +## Lossless claim ledger + +`confirmed-open` means the reported symptom or a valuable question remains open; it does not imply that every variant was reproduced. `accepted-design` cites existing contract behavior. No item is deferred or called fixed. + +| ID | Source-order claim or proposal | Technical verdict and evidence | Disposition and durable value | +| --- | --- | --- | --- | +| I01 | A normal leader-tab close can leave surviving Collections in permanent error; the reporter saw 3/6 runs. | Confirmed for one Collection in the controlled and real Chromium/OPFS after-durable close history. Frequency and seven-Collection topology untested. | `confirmed-open`; candidate survival law and browser host owner below. | +| I02 | A follower's Electric-sourced `rpc:applyCommittedTx` waits 10 seconds, crosses leadership, and rejects with `IndeterminateCommitError`. | The real browser manual source commit waited for the 10-second RPC deadline and rejected with that exact error across term 1→2. Electric delivery itself was not run. Existing coordinator oracle already pins the direct RPC result. | `accepted-design` for the RPC-level result under #1845; I01 remains open at the Collection level. | +| I03 | Other Collections fail similarly, sometimes through `flushQueuedHydrationTransactionsUnsafe`. | Code has that route, but neither new witness entered it. No seven-Collection or queued-hydration claim follows from this run. | `confirmed-open`; browser composed owner needs a held hydration history. | +| I04 | The Collection stays in `error`, later `update()` throws, and live rows stop. | Both new witnesses saw `error` and a rejected later source commit carrying the same indeterminate error. They did not call `update()` or drive later server rows. | `confirmed-open`; public status and later source-commit subclaims reproduced, mutation/server subclaims need witnesses. | +| I05 | The closing leader logs `PersistedCollectionDurabilityError` about its SQLite connection closing. | The host witness closes after the durable write, so no pending leader SQLite call emits this diagnostic. Its causal role is unknown. | `confirmed-open`; OPFS page lifecycle owner needs an active-write close witness. | +| I06 | In other runs the follower takes all locks and continues sending/receiving. | The real idle-close control elects the follower and accepts its next source commit. The reported 3/6 frequency and all-lock scope are untested. | `accepted-design`; keep the idle-close control beside the crossing case. | +| I07 | A sync source can deliver the same changes again from a durable resume offset. | Electric stages `electric:resume` metadata with its source transaction; the existing live Electric resume oracle covers reopen histories. This run used a manual source, so it cannot prove same-run redelivery or safe automatic reconciliation. | `design-decision`; source-backed recovery needs an Electric receiving witness and policy. | +| I08 | The closing leader could reject or drain pending RPCs, perhaps on `pagehide`, avoiding the wait. | OPFS already aborts its own worker calls on `pagehide`; the coordinator does not notify remote RPC requesters before its timeout. Faster rejection alone leaves a committed, unanswered write indeterminate. | `design-decision`; latency policy and a separate timing law if adopted. | +| I09 | The runtime could rehydrate and resume sync-sourced work itself instead of fail-stopping. | This conflicts with the documented #1845 application-reconciliation policy for uncertain mutating RPCs. The host witness proves the durable-success branch but not the durable-failure branch. | `design-decision`; choose ownership and reconciliation checkpoints before changing production. | +| I10 | If the application owns reconciliation, document whether cleanup followed by a new Collection is supported. | The guide says to check durable state before retrying, but supplies no source-Collection recovery sequence. This experiment did not execute cleanup/recreation as recovery. | `confirmed-open`; documentation and executable application recovery witness. | +| I11 | No standalone reproduction existed; the reporter offered to reduce the app case. | The controlled and Chromium/OPFS fixtures now reproduce the public failure without the app. They manually drive the source and do not run Electric. | `stale` for the absence of a standalone reproduction; an Electric receiving witness remains open under I07 and I11. | + +Accounting: 11 raw items = 5 `confirmed-open` + 2 `accepted-design` + 3 `design-decision` + 1 `stale`. There are no duplicates, refutations, fixes, or agreed deferrals. + +## Law discovery and tension + +**Existing law, approved:** After a mutating RPC loses transport, the requester retries only when it still knows the same non-null leader ID and term. Unknown or changed leadership yields `IndeterminateCommitError`; the application reconciles. The [browser README](../../../packages/browser-db-sqlite-persistence/README.md) and [core README](../../../packages/db-sqlite-persistence-core/README.md) state this. The existing `fails indeterminate when a committed success is lost across leader change` test enforces it. A closed leader can have committed without answering, as the host durable snapshot confirmed; retrying the mutation blindly against the new leader would violate the uncertain-outcome boundary. + +**Candidate law from the issue:** For a source-backed Collection in a surviving tab, a normal close of another tab should not make the surviving sync run permanently unusable. The first source receipt may reject as uncertain. After leadership transfer and reconciliation, a later source commit should settle and the Collection should be `ready`. This candidate is not an approved product contract. The new assertions intentionally stay RED pending a decision. + +The rival policy is application-led reconciliation. It permits the current terminal `error` and requires the application to inspect durable state and restart or recreate the Collection. The issue's source-backed premise could justify a different owner because the source cursor is durable with the rows, but that does not itself specify who restarts the sync run, whether a public snapshot may remain visible while checking durable state, or how pending applied receipts settle. Both policies agree that the original RPC must not be blindly resent to a replacement leader. + +The smallest distinguishing history is the one tested: the leader finishes a durable `PersistedTx`, its answer is held, the page closes, and the follower takes leadership. The idle-close control keeps all other setup similar and shows that ordinary takeover succeeds. A second necessary branch is close before durability; it remains open. The source of policy authority, rather than the test harness, must choose between automatic and application-led recovery. + +## Oracle chain and RED evidence + +| Layer | Controlled owner | Real host receiver | +| --- | --- | --- | +| Contract and model | Candidate later-usability rule stated beside the new test in `browser-coordinator-oracle.test.ts`; it permits either first-receipt outcome. | `expectedSurvivor()` predicts `ready` and a fulfilled later source receipt without consulting production. | +| Legal history | One follower source commit; leader applies durably; mock response is dropped; leader is disposed; follower takes leadership; second source commit. | Same ordering with two Chromium tabs, real BroadcastChannel, Web Locks, OPFS workers, and SQLite. The leader's regular scoped adapter holds after durable apply; `page.close()` supplies the host event. An idle-close control omits the first commit. | +| Production path | `persistedCollectionOptions` wrapped sync → `BrowserCollectionCoordinator.requestApplyCommittedTx` → transport timeout → wrapped receipt and Collection status. | Same wrapper and coordinator through actual browser host and SQLite adapter. This fixture manually drives sync; it does not run the Electric SDK. | +| Observation cuts | Dropped successful response and leader adapter call before disposal; follower election before RPC timeout; Collection status and later receipt after timeout. | Leader hold after durable apply before close; follower election; first receipt after deadline; status, later receipt, public IDs and durable IDs after the later attempt. | +| Calibration | Baseline implementation reaches the public comparison and fails. Existing direct-RPC test passes its expected indeterminate result. | Idle close passes. In-flight close reaches the same public comparison and fails after 10.6 seconds; durable IDs still contain `crossing`. | + +Exact results on baseline `f6aba314e44afdfa413d6d70bf8e272dfa67af85`: + +- The existing direct-RPC indeterminate test passed: 1 passed, 153 skipped. +- The new controlled candidate test failed at `collectionStatus`: expected `ready`, actual `error`; later source receipt expected fulfilled, actual rejected. +- The new Chromium/OPFS pair yielded 1 passed idle-close control and 1 failed in-flight case. The failed case recorded `firstOutcome.errorName = IndeterminateCommitError`, term 1→2, `collectionStatus = error`, the later receipt rejected with the same error, public IDs `[crossing]`, and durable IDs `[crossing]`. Cleanup completed without secondary failure. +- Browser package TypeScript check passed after installing the relevant locked dependencies from the local cache. The full workspace offline install could not complete because an unrelated Electron tarball was absent; the filtered browser and Electric/e2e dependencies sufficed for these checks. + +This is a source-backed *manual* sync witness and a real host transport witness. It is not an Electric redelivery witness. It does not reproduce the reported nondeterministic 3/6 rate, on-demand hydration, seven Collections, `update()`, or the leader's active-write error. Those cells remain assigned to the Browser composed owner, persisted-history owner, and live Electric OPFS receiver as stated above. + +## Oracle guide audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Candidate authority and conflict with #1845 are explicit in each new oracle and this record. No approved automatic-recovery law is claimed. | +| ORC-002 | The later-usability expectation comes from the issue's proposed caller behavior, not the coordinator's route classifier or persisted runtime branches. | +| ORC-003 | Opening prose states the law and limits. The model, two legal schedules, production driver, public observations, and comparison cut are adjacent in the controlled and host tests. | +| ORC-004 | Not triggered: these are fixed, bounded histories, not a generated-property coverage claim. | +| ORC-005 | Both tests exercise the named production entry points. Host reach witnesses show the held durable adapter call, page close, follower election, and final public comparison. | +| ORC-006 | The baseline's terminal fail-stop is a concrete wrong result for the candidate law; both comparisons reject it at the intended public checkpoint. The idle host history passes. The direct-RPC accepted law remains green. | +| ORC-007 | Not triggered: no important generated property was added. | +| ORC-008 | Not triggered: the candidate expected result is stateless and does not introduce reference-model state. | +| ORC-009 | `Collection status`, `sync transaction`, `applied receipt`, `publication`, and `cleanup` follow the project glossary. The model's later-usability projection combines status and a later receipt; it claims no durable-content model. | +| ORC-010 | The controlled test and host test preserve a primary mismatch with separate cleanup diagnostics and release their resources. The host page intentionally closes without graceful application cleanup in the crossing history. | +| ORC-011 | A second semantic formulation of Electric cursor recovery is needed if that policy is chosen. The manual source and controlled route can share an assumption that the real SDK does not meet. | +| ORC-012 | This exact-head record accounts for each applicable requirement and names open law × history × path × observation cells. It does not claim bug-class closure. | +| ORC-013 | The idle host case rejects the overbroad rule that every leader close breaks the survivor. The in-flight case reaches the conditional premise and rejects terminal fail-stop under the candidate law. | +| ORC-014 | Real Chromium/OPFS supplies the held-after-durable close premise used by the controlled seam. Electric SDK delivery of an equivalent in-flight source transaction remains unproved and is not credited. | + +The candidate law is encoded for these two finite host histories and enforced at the named public cut; baseline production proves sensitivity. It is not fully encoded for before-durable loss, queued hydration, Electric restart, or all Collections. Under the current approved application-reconciliation contract, RED demonstrates the requested symptom and a policy conflict, not an authorized production defect repair. + +## Next design decision + +Choose whether a surviving source-backed Collection automatically reconciles an uncertain coordinator commit or whether the application must explicitly restart it. For automatic recovery, specify the durable snapshot and cursor read, first-receipt result, public snapshot during recovery, retry scope, and when the Collection becomes `ready`; then add before/after-durable and Electric receiver histories before changing production. For application-led recovery, document and test a supported cleanup/recreation sequence that checks durable state and resumes source delivery. A faster page-close rejection can be evaluated separately after the outcome policy is set. + +## Follow-up: exact-ID source reconciliation + +The preceding ledger and RED observations are the original baseline audit. +They are retained in source order. The follow-up chose same-run reconciliation +for a source sync transaction when the elected SQLite adapter can certify its +outcome. A direct mutating RPC still rejects as indeterminate after an unknown +or changed route. The persisted wrapper holds the source applied receipt while +the replacement leader checks the immutable `txId` under the writer lock. An +existing ID fulfills the receipt at its original durable position. An absent +ID permits one application only when the durable row version and reset epoch +still equal the pre-send anchor. An intervening durable write, reset, pruned ID, +or unavailable proof retains the original indeterminate error. The bounded +recovery keeps the source Collection and mounted live query in the same sync +run. The published row and opaque source cursor are one durable transaction. + +### Updated claim ledger + +The following dispositions supersede the baseline ledger; the original 11 +source claims above remain unchanged for loss accounting. + +| ID | Current technical verdict and evidence | Current disposition and destination | +| --- | --- | --- | +| I01 | The original terminal source Collection is repaired for controlled and Chromium/OPFS manual-source before/after-durable closes. A live Electric delivery of that exact crossing remains unobserved. | `confirmed-open` for the reported Electric path; Browser Electric receiver owns that witness. | +| I02 | Direct RPC still rejects with `IndeterminateCommitError` on changed leadership; the wrapper alone invokes exact-ID reconciliation. | `accepted-design` under the #1845 RPC contract; coordinator oracle retains the direct-RPC check. | +| I03 | The wrapper oracle now drives a receipt queued behind persisted hydration through an exact-ID decision and later work. The real-host queued case and seven-Collection topology remain unobserved. | `confirmed-open` for the host receiving schedule; Browser hydration owner. | +| I04 | Controlled and OPFS witnesses assert `ready`, one sync run, mounted live-query rows, and a fulfilled later source receipt. A direct `update()` call and later Electric row were not driven. | `confirmed-open` for those reported entry points; Browser Electric receiver and mutation-path owner. | +| I05 | No new active-write page-close witness reproduces the closing leader's SQLite diagnostic. The after-durable hold closes after its SQLite call. | `confirmed-open`; Browser OPFS page-lifecycle owner. | +| I06 | Idle close still transfers leadership and accepts later work. | `accepted-design`; retained host control. | +| I07 | Durable source cursor metadata is atomic with each manual source row, and exact-ID reconciliation prevents a blind replay. The installed Electric SDK was not driven through the crossing. | `confirmed-open` for the real provider receiving path; Browser Electric receiver. | +| I08 | Earlier page-close rejection could reduce the 10-second wait but cannot decide a committed, unanswered write. | `design-decision` about latency, separate from the certified outcome law. | +| I09 | The chosen source-backed policy holds the first receipt through certification and stays in the same sync run on a certified result. Unknown results still fail closed. | `fixed-now` for the bounded source/SQLite policy; wrapper, adapter, coordinator, and OPFS owners below. | +| I10 | Automatic source reconciliation replaces the proposed application cleanup/recreation sequence for certified source transactions. Direct mutating RPC callers still own reconciliation, as both package guides say. | `stale` for this source-recovery alternative; retain the direct-RPC documentation question under I02. | +| I11 | The controlled and OPFS standalone fixtures remain executable. | `already-fixed`; the missing Electric provider witness is retained under I01 and I07. | + +Accounting: 11 original items = 5 `confirmed-open` + 2 `accepted-design` + +1 `design-decision` + 1 `fixed-now` + 1 `stale` + 1 `already-fixed`. +There are no refutations, duplicates, or agreed deferrals. The open rows name +unproved receiving paths; they are not claims that the bounded repair failed. + +### Adversarial oracle review ledger + +Two independent reviewers attacked the model, production driver, and receiving +schedules. This ledger retains their distinct findings and challenges rather +than merging them into the issue ledger. + +| ID | Reviewer claim or challenge | Verdict, action, and durable value | +| --- | --- | --- | +| R01 | The replacement coordinator could release its writer lock before advancing its stream position. | Confirmed and fixed inside the lock; the Browser coordinator oracle checks a queued later write. | +| R02 | A reconciliation wire request without a durable anchor could apply an uncertain transaction. | Confirmed and fixed at RPC and SQLite boundaries; malformed-anchor assertions reject before storage work. | +| R03 | A newly elected peer may miss the original committed-row notification. | Confirmed by the OPFS after-durable history and fixed with a reconciliation reload notification. | +| R04 | An absent ID after a peer write could overwrite a newer same-key row and opaque cursor. | Confirmed design constraint: fail closed. The SQLite oracle retains the same-key/cursor witness; no late apply is claimed. | +| R05 | Reconciliation reload after the original notification could duplicate public change events. | Confirmed and fixed by skipping only an unchanged reconciliation replacement. The wrapper and passive OPFS observer assert exact event counts. | +| R06 | A local SQLite failure during reconciliation could be replaced with the original indeterminate error. | Confirmed and fixed; remote and local durability classifications have separate wrapper checks. | +| R07 | `NOT_LEADER` during reconciliation could fail although a newer route is available. | Confirmed and fixed with bounded exact-ID rerouting; the Browser coordinator oracle drives the route change. | +| R08 | The SQLite exact-ID test reused a transaction ID with a different payload. | Confirmed test-integrity flaw and fixed; the test now keeps the immutable payload while changing only proposed stream position. | +| R09 | A peer-write host history could reconcile before the peer became durable. | Confirmed test-integrity flaw and fixed with a gate before the requester enters the writer lock; the host asserts durable version 2 before release. | +| R10 | Skipping an equal snapshot might fail to reorder Collection keys. | Unconfirmed. A direct Collection probe truncated and reinserted equal rows in reverse order without changing public key order. The new skip is limited to reconciliation reloads. | +| R11 | Equal row values can hide row metadata for a missing key, so the shortcut might skip required cleanup. | Confirmed by a RED wrapper oracle: `ghost` metadata remained after reload. A Collection internal delegation now finds orphan metadata; reconciliation removes it without republishing unchanged rows. | +| R12 | The old coverage map still described a RED candidate and missing before/after schedules. | Confirmed and corrected in the coverage map; this addendum preserves the historical RED record. | +| R13 | Mixed-version tabs may not understand the new reconciliation reload marker. | Valid coverage limit. Both guides scope same-run recovery to same-version tabs; no cross-version behavior is claimed. | +| R14 | A controlled queued-hydration witness and manual source driver do not prove real-host queued hydration or Electric SDK delivery. | Valid receiving gaps, owned by the Browser hydration and Electric suites. | + +The reviewers separated product failures, test-integrity defects, a refuted +ordering concern, and real receiving gaps. Their high-signal findings changed +the oracle and repair. Both would receive a positive reviewer recommendation; +neither claimed a passing finite suite proved arbitrary histories. + +### Law chain, checkpoints, and calibration + +| Law | Independent expectation and legal histories | Production path and asserted cut | Sensitivity | +| --- | --- | --- | --- | +| Certified source receipt | One immutable source payload is either already durable by ID or may be applied once against an unchanged anchor. Before/after-durable, queued hydration, and later source work are authored independently of the coordinator classifier. | Wrapped `commit()` through Browser coordination and SQLite. At the uncertain cut its receipt is pending; after certification it fulfills with Collection/live query ready in the same run; a later receipt fulfills. | Original baseline failed at Collection `error` and later receipt rejection. Repaired controlled and OPFS histories pass. | +| Durable exclusion | An absent ID with a changed row version or reset epoch cannot authorize late application. Same-key row and opaque cursor distinguish unsafe overwrite. | SQLite transaction checks ID and anchor under the writer lock; adapter oracle compares result, rows, cursor, version, and exact applied IDs. | The original blind late-apply design would add or overwrite an applied row; the unknown-result and unchanged-anchor controls distinguish the rule. | +| Peer publication | A peer that missed the original notification reloads rows even at an equal stream position, while a peer that already published them emits no duplicate batch. | Three real OPFS tabs at the passive public source/live-query checkpoint; `after-peer` holds before reconciliation until peer version 2 is durable. `after-notify` records exact passive event keys. | Removing only `reconciliationReload: true` reached the peer's durable version 2 gate, then failed at the passive checkpoint: public and live IDs were `[peer]` instead of `[crossing, peer]`. Marker restored; both histories pass. | +| Metadata cleanup without row republish | Equal loaded rows can coexist with metadata for a missing key; a reconciliation reload removes that orphan while keeping equal row events empty. | Wrapper oracle records `metadata.row.get('ghost')`, source/live change batches, and row sets after the reload. | Before the cleanup fix, `ghost` remained; after the first guard-only fix, `crossing` republished. The final targeted metadata deletion passes both assertions. | +| Failure classification | An unprovable outcome retains indeterminate source failure; a known local or remote SQLite failure keeps its durability class and path. | Wrapper oracle checks first receipt and later source admission at terminal cuts for unknown, remote durability, and local durability. | The local broad-catch design lost the durability class; the refined branch preserves it. | + +The controlled wrapper oracle is the primary source-lifecycle owner. The +SQLite adapter oracle supplies an independent durable ledger. The Browser +coordinator oracle owns route and writer-lock order. The OPFS host test is the +receiving witness for real browser transport and storage; its manual source is +not the Electric SDK. `source-reconciliation-repeat` also tests orphan metadata +through the real Collection sync implementation. The orphan key is absent from +loaded rows, so full replacement would clear it. The added internal delegation +finds that exact mismatch without exposing the state map to the persistence +package. + +### Verification and remaining boundary + +The focused SQLite CLI contract passed 43 tests; the wrapper suite passed 653 +with one existing todo; the Browser coordinator suite passed 158 after its +local `better-sqlite3` binary was restored from the matching package version. +Chromium/OPFS passed all five schedules. Core and Browser TypeScript checks +passed. The new orphan-metadata assertion was RED before the fix and GREEN +after it. The reconciliation-marker mutant was killed at the first passive +public row checkpoint after the peer-before-reconciliation gate and restored. +Vitest printed unrelated missing Expo tsconfig diagnostics; its test results +above completed. + +These are finite same-version, manual-source histories. A live Electric +receiving witness, a real-host queued-hydration close, and an active-write +leader diagnostic remain outside this evidence. They are recorded in the +coverage map with receiving owners. The implementation deliberately keeps an +absent ID after another durable write indeterminate, because no contiguous +ledger proof can establish that a late source payload will not regress a +newer row or cursor. That valid history does not enter same-run recovery. + +## Follow-up: Electric receiving and prep review + +The preceding verification paragraph records its earlier manual-source cut. +The new `electric-leader-close.opfs.spec.ts` supplies the installed Electric +SDK at the reported after-durable boundary. PostgreSQL authors one +`crossing` row. The follower's Electric sync creates its source transaction, +and the leader's collection-specific scoped adapter holds only after SQLite +applies it. The page then closes without an RPC answer. At the first receipt +checkpoint, the original Electric applied receipt has fulfilled in sync run +one; the source Collection and mounted live query are ready with the row; the +durable row and the exact applied transaction ID agree. The source transaction +itself contains the `electric:resume` set. Its kind, offset, handle, and +shape ID equal the first durable resume identity, whose offset has advanced +from the empty baseline. A later PostgreSQL row +advances the offset again and reaches public, live, and durable rows in the +same run. The direct coordinator RPC still reports +`IndeterminateCommitError`. A manual-source Collection owns only the first +leader term; this does not model two Electric streams contending in both tabs +or seven concurrent Collections. + +The first two Electric attempts timed out before the held boundary because +the fixture intercepted the root adapter rather than the Collection-specific +adapter; the unmounted live query also cleaned itself up. They are setup +failures, not RED evidence. The corrected driver reaches the held durable +boundary and passes. The complete Electric OPFS suite passed 16 tests with +the new witness. + +### Electric oracle attack ledger + +Two independent oracle reviewers inspected the new receiving law and driver. +Each raw suggestion remains distinct even where it overlaps another. + +| ID | Reviewer claim | Technical verdict, PR action, and durable value | +| --- | --- | --- | +| EL01 | RPC rejection and reconciliation booleans do not prove the Electric `commit()` applied receipt settles. | Confirmed gap; `fixed-now`. The source sync wrapper observes the crossing receipt pending at the hold and fulfilled at the first checkpoint, with one sync run. | +| EL02 | A resume marker after crossing might be the empty stream's earlier marker. | Confirmed gap; `fixed-now`. Baseline and first durable offsets are compared. | +| EL03 | Comparing resume objects after the later row can pass from `updatedAt` churn at the same offset. | Confirmed gap; `fixed-now`. The test compares offsets at all three checkpoints. | +| EL04 | A manual first-term leader does not prove two Electric streams or seven Collections. | Valid limit; `fixed-now` in the executable opening prose and coverage map. The first-term source choice does not weaken the follower's installed SDK path. | +| EL05 | Distinct durable offsets alone do not show that the crossing row and cursor were in the same source transaction. | Confirmed gap; `fixed-now`. The follower records the crossing `PersistedTx.collectionMetadataMutations` and compares its resume offset with the first durable offset. | +| EL06 | The Electric resume handle and shape ID could differ even at the same offset. | Confirmed precision gap; `fixed-now`. The source transaction and first durable marker compare kind, offset, handle, and shape ID; `updatedAt` is intentionally excluded. | +| ED01 | The first durable marker could predate the crossing row. | `duplicate` of EL02; retained as the driver's missing baseline observation. | +| ED02 | Retaining only the first crossing `txId` could hide a second logical crossing commit with a fresh ID. | Confirmed gap; `fixed-now`. The driver records every crossing ID and the spec requires exactly one before and after the later row. | +| ED03 | Status and later rows do not prove the original receipt or one sync run. | `duplicate` of EL01; retained as the driver's required `write`/`commit` observation. | +| ED04 | Reconciliation success booleans do not identify which transaction was certified. | Confirmed precision gap; `fixed-now`. Successful reconciliation IDs must equal the authored crossing ID at the first checkpoint. | + +The reviewers correctly separated a setup path, receipt settlement, cursor +provenance, and a valid receiving limit. Their findings improved the test +without changing the product law. Both would receive a positive reviewer +recommendation: the findings were technically accurate, specific, and +prioritized by false-green risk. + +Electric review loss audit: 10 raw items = 8 `fixed-now` + 2 `duplicate`. +No Electric review item remains open inside the declared one-provider +after-durable history. + +### Prep review ledger + +The correctness review and simplifier review were read-only. Their raw items +appear in source order within each review; `C03` and `S01` intentionally +overlap. + +| ID | Raw review claim or proposal | Technical verdict, PR action, and durable value | +| --- | --- | --- | +| C01 | Ordinary `deepEquals` can call a sparse public array equal to SQLite's dense `null` array, suppressing a needed reload; an enumerable extra array property is another lost shape. P2 at `persisted.ts` snapshot comparison. | Confirmed by two RED wrapper histories. `fixed-now` with `deepEqualsStrict` and the two GREEN histories. Law: a skipped replacement must preserve complete durable row shape at the reconciliation checkpoint. The old oracle observed rows and duplicate events but lacked this value-shape axis. | +| C02 | The OPFS host read exact applied IDs but asserted only their count. P3 coverage idea. | Confirmed host false-green gap. `fixed-now`: authored IDs now equal the complete durable ID sequence at the first and later checkpoints; SQLite's independent adapter oracle also compares exact IDs. | +| C03 | The reconciliation response's `current` field was never read by the wrapper. P4 cleanup. | Confirmed; `fixed-now`. Removed only the wire field, retaining the coordinator-local current position for peer publication. Typechecks and the wrapper/coordinator suites pass. | +| S01 | Remove the unused `current` wire field and mock fields. | `duplicate` of C03; the same cleanup was applied. | +| S02 | Put optional `reconcileCommittedTx` in `CoordinatorAdapter`'s `Pick` union. | Confirmed safe type cleanup; `fixed-now`. Runtime behavior is unchanged and package typechecks pass. | +| S03 | Name and reuse the common reset-epoch predicate inside SQLite's locked anchor branch. | Confirmed small clarity improvement; `fixed-now` as `sameResetEpoch`. The direct adapter guard remains independent of wire validation. | +| S04 | Do not merge the host expected-result computation with driver wait values merely to save lines. | `already-fixed`: the independent expected history and production driver remain separate under ORC-002/003. | +| S05 | Do not share an anchor validator across untrusted RPC and direct SQLite boundaries. | `already-fixed`: both checks remain at their respective trust boundaries. | +| S06 | Do not remove unchanged-snapshot and orphan-metadata handling for a broad line reduction. | `already-fixed`: the passive duplicate-event and orphan-metadata witnesses require both behaviors. | +| S07 | Keep `alreadyApplied` in the new response even though the wrapper does not read it. | `already-fixed`: coordinator oracle assertions retain the explicit exact-ID outcome. | + +Prep loss audit: 10 raw items = 5 `fixed-now` + 1 `duplicate` + +4 `already-fixed`. There are no omitted footnotes, unagreed deferrals, or +remaining prep-review findings. The correctness reviewer found one P2 product +bug and one host assertion gap; its proposed narrow repairs were accurate. +The simplifier's two wire/type reductions and reset-epoch name were safe, and +its four rejected reductions preserved real oracle or trust-boundary value. +Both reviews had high signal and would receive positive reviewer +recommendations. + +### Updated law coverage and limits + +The repaired claim is bounded by source-backed immutable transaction IDs, +same-version tabs, exact-ID certification under SQLite's writer lock, and an +unchanged durable anchor for an absent ID. The wrapper's legal histories cover +before/after durability, held receipt, queued hydration, unknown result, +local/remote durability failures, later work, equal-row reload, orphan +metadata, sparse arrays, and extra array properties. SQLite owns the ledger, +version, reset, and cursor decision. Browser coordination owns route and lock +order. The five manual-source OPFS schedules observe real transport, +publication, live rows, event counts, and exact IDs. The Electric receiver +adds the reported provider path for one after-durable crossing and later row. +Each owner asserts its promised public observation at the named held, +certified, and later checkpoints. + +ORC-001/002/003: the contract and its limits appear beside independent +models; the Electric expected rows come from PostgreSQL, and no production +classifier computes them. ORC-004/007/008: the new host history is fixed and +adds no generated grammar or stateful reference model. ORC-005: the held +scoped-adapter apply, SDK-authored transaction, original receipt, exact ID, +public/live rows, and durable cursor are reached and compared. ORC-006: +baseline manual-source terminal failure, the peer-publication mutant, the +orphan-metadata shortcut, and sparse-array comparison failure reject +plausible wrong designs at their respective checkpoints. A temporary +Electric-path mutant that threw the original indeterminate error after a +successful exact-ID response reached the first receipt checkpoint and failed: +the applied receipt rejected and source/live status became `error` despite +the durable row. The mutant was restored; the Electric test passed again. +ORC-009: source +transaction, applied receipt, sync run, and publication follow the glossary. +ORC-010: the host preserves a primary mismatch separately from cleanup +failures and closes its pages and PostgreSQL connection. ORC-011/014: the +installed Electric SDK is a second provider formulation and supplies the +after-durable premise previously controlled manually. ORC-012/013: the review +record names the original crossing, idle/before-durable neighboring +histories, hostile wrong designs, and remaining owner cells. + +The remaining receiving limits are a real-host queued-hydration close, an +active-write leader diagnostic, two Electric streams in both tabs, the +reported seven-Collection topology, and other browsers. Direct application +`update()` after recovery is also unobserved because this fixture has no +application mutation handler; the later installed Electric row proves source +continuation. These are named coverage cells, not evidence of a reachable +in-scope counterexample to the certified source receipt law. An absent ID +after an intervening durable write still fails closed by design. + +### Final integrated verification + +The exact reviewed executable head is +`013aa2cb788f562e87ffa0bbc10c1a1073dead43`. It includes the +`362be03006baa210461ce31ba2cf134f3243596b` merge of `origin/main`. +The later review-record and changeset commit changes no executable code. +At this head, the real SQLite adapter oracle passed 43 tests. The persisted +wrapper oracle passed 655 tests with one existing todo. The Browser +coordinator oracle passed 158 tests. All five manual-source Chromium/OPFS +leader-close schedules passed. The full Electric OPFS suite passed 16 tests. +TypeScript checks passed for `@tanstack/db`, SQLite persistence core, and +Browser persistence after the final fixture type correction. The earlier +Vitest Expo tsconfig warnings did not fail these suites. + +## Follow-up: CodeRabbit evaluation and Electron receiving path + +This addendum evaluates CodeRabbit review `5463528038` at reviewed commit +`613dbc9e001f6a2b93076375e1fcb05932078eda`. Its four inline comments, +subclaims, and optional CLI suggestion yield ten source-order items. The exact +executable follow-up commit is +`a1a3dd8c1cecc6761e93f2c7feaf946bc0830b45`; this addendum changes no +executable code. The earlier verification paragraphs remain historical records +for their named commits. + +| ID | Original claim or proposal | Verdict, PR action, and durable value | +| --- | --- | --- | +| CR-1 | Fence reconciliation on `state.leaderId !== this.nodeId`, like ordinary apply. | The inconsistency was real, but the proposed fence fails after a legal no-write Web Lock takeover: the former owner's same-term heartbeat can overwrite the successor's route. `fixed-now`: a local Web Lock leader ignores peer heartbeats. The Browser oracle retains both the queued exact-ID check and a later source write. | +| CR-2a | Alphabetize the `IndeterminateCommitError` import. | `already-fixed` at `6a64f4c67`; affected lint passes. | +| CR-2b | Alphabetize the `ReconciledCommittedTx` import. | `already-fixed` at `6a64f4c67`; affected lint passes. | +| CR-2c | Order `PersistedTx` before `PersistenceAdapter`. | `already-fixed` at `6a64f4c67`; affected lint passes. | +| CR-2d | Keep both defensive anchor guards and correct the nullable SQL type rather than suppressing lint broadly. | `already-fixed` at `6a64f4c67`: RPC and SQLite each validate before work, and `reset_epoch` is nullable at the SQL boundary. | +| CR-3a | Make the first cursor lookup `string \| undefined` before `?? null`. | `already-fixed` at `6a64f4c67`; the nullable fallback remains typed. | +| CR-3b | Apply the same type correction at the second cursor lookup. | `already-fixed` at `6a64f4c67`; this distinct site is retained. | +| CR-4a | Wait for orphan metadata cleanup rather than relying on one async flush. | `fixed-now`: the wrapper oracle holds the reload, checks pending metadata and event state, then waits for cleanup and checks settled public rows and events. | +| CR-4b | Wait for the durable array shape at the second reload site. | `fixed-now`: sparse-slot and enumerable-array-property histories check authored shape during the hold and durable public shape after release. | +| CR-5 | Consider `coderabbit review --agent` after changes. | `refuted` as a required action: this is an optional process suggestion, not a defect claim. Raw comments, independent oracle attacks, mutants, lint, types, and affected suites supplied direct review evidence. The optional idea remains recorded here. | + +The review had high signal: its lint and timing findings were accurate, and +CR-1 exposed a real route inconsistency. Its suggested ownership fence was +wrong for a legal same-term history. Fix quality was mixed; the reviewer earns +a positive recommendation with that caveat. The final loss audit is ten raw +items = three `fixed-now` + six `already-fixed` + one `refuted` as a required +process action. No CodeRabbit item is deferred or omitted. + +### Law, history, path, and checkpoint audit + +For CR-1, exclusive per-Collection Web Lock ownership is the route authority. +The Browser oracle captures a real former-owner heartbeat before a no-write +release, elects the successor at the same term, queues exact-ID reconciliation +behind the writer lock, then delivers the captured heartbeat. It observes the +successor's route at the queued cut, an applied-once exact-ID result after +release, and the next sequence on a later source write; the former adapter +applies nothing. The original handler let reconciliation succeed but made that +later write fail. CodeRabbit's fence made reconciliation fail. Both RED runs +reached their intended comparisons; the repaired handler passes. The existing +queued-transfer history separately checks genuine ownership loss. These are +bounded controlled Browser histories, not arbitrary browser scheduling. + +For CR-4a/b, the established reconciliation-reload law removes orphan row +metadata without duplicate row events and publishes durable value shape even +when ordinary change-event equality equates sparse and dense arrays. The +wrapper oracle controls `loadSubset` entry and release. At the held cut, +metadata and authored array shapes remain public. At the settled cut, orphan +metadata is absent, source and live-query rows have the durable shape, and +unchanged rows emitted no duplicate events. The old single-flush assertions +failed before reload completion. A skip-publication mutant reached the +positive settled comparisons and failed all three cases. The OPFS passive-tab +history remains the separate real-browser publication receiver. + +CI uncovered a separate Electron receiving gap: the renderer/main IPC bridge +did not expose `reconcileCommittedTx`, so the shared SQLite adapter contract +failed at its explicit operation assertion. The bridge now transports the +immutable transaction and durable anchor, returns the complete result, and +requires protocol v3. The registered SQLite contract exercises exact-ID +reconciliation through both in-process handlers and a real Electron main +process. A resolved-adapter witness checks distinct Collection modes and +schema versions in the reconciliation envelopes; dropping resolution fails at +that assertion. An adapter without the optional operation returns `unknown` +without a durable write; a temporary blind-apply-then-unknown mutant changed +the durable row and version and failed at the no-write assertion. Tests also +reject v1 and v2 peers in both directions and preserve durability error code +and path over IPC. The Electron correctness review found the missing error +path; its fix passed a test that was RED at the path assertion. The simplifier's +only remaining proposal was a shorter forwarding method with no behavior or +clarity gain, so the explicit method remains. + +The full Electron package passed 163 tests with no type errors. The exact-ID +contract passed through a real Electron main process in isolation. The full +Browser coordinator suite passed 263 tests, the persisted wrapper suite +passed 876 tests with two existing todos, and all five real Chromium/OPFS +leader-close schedules passed on the named executable commit. The final +affected-file ESLint check had no errors and 37 existing `require-await` +warnings in the large Browser and wrapper oracle files. Electron TypeScript, +formatting, and `git diff --check` passed. An optional broader real-process +Electron run had +five other failures: two multi-Collection leadership checks and three +function-bearing collation cases, so that broader run is not claimed green. + +The real-process witness executes the renderer adapter in Vitest and sends its +envelope through IPC to Electron main. It does not execute that adapter inside +an Electron renderer or cover concurrent requests from multiple renderers in +one live main process. A delayed reconciliation IPC reply after main applies +is an untested transport history, recorded with the Electron receiving owner +in the coverage map. Passing prompt-reply histories do not establish general +Electron same-run recovery. The Browser and Electron coverage boundaries stay +separate from the original source-backed Collection law. + +## Follow-up: CodeRabbit review 5463932998 + +CodeRabbit reviewed `b25d83b50cb24308b793b858bc25eb4918975c6b` and +reported one inline documentation defect. Its review body also repeated an +optional CLI suggestion. The checkbox, run metadata, file inventory, and +skipped-file list contained no other findings. + +| ID | Claim | Evidence and disposition | +| --- | --- | --- | +| CR2-1 | The unescaped union pipe split the CR-3a audit cell. Escape it inside the code span. | `fixed-now`. Local GFM rendering split the original code span and omitted the verdict from the visible row. The escaped pipe renders three intact cells, including the `string \| undefined` code span and verdict. Prettier and `git diff --check` pass. This is a documentation rendering defect, so a product oracle is not applicable. | +| CR2-2 | Consider running `coderabbit review --agent` locally. | `refuted` as a required action. The raw review, local rendering check, and formatting check supplied direct evidence. The optional process idea remains here. | + +The reviewer made one accurate, precise finding with a minimal correct fix. +This is a positive recommendation for targeted documentation review. This +review does not establish product-law review depth. The final loss audit is two +raw items = one `fixed-now` + one `refuted` as a required process action. No +item remains open or deferred. + + +## Follow-up: medium-effort review at b4a46a4fe + +The user supplied ten code-reading findings against +`b4a46a4fe5d3b08edfa4084dc7a46299857f6c60`; the review reported no +executed checks. The executable repair is +`d6e2493b17b3f023b30ddef0482bae88f9f674ee`. The earlier review records +above remain evidence for their named commits. This addendum accounts for each +new claim and the oracle attack that followed the repair. + +### Laws, experiments, and inference + +| Law and authority | What the earlier oracle proved | Decisive experiment and inference | Enforcement at d6e2493b1 | +| --- | --- | --- | --- | +| **One durable term per election; passive followers retain the newer route** (M01, M02). The Browser coordinator's Web Lock and routing contract requires a successor's identity to order after its predecessor, even after a no-write term. | The old controlled history protected the new lock holder from a delayed former-owner heartbeat. It had no passive C after B's heartbeat, and its stub did not persist an empty election. | A/B/C histories were RED at C's route after the delayed A heartbeat. A temporary `Math.max(memory, durable) + 1` repair passed when B had heard A, but failed for a cold B. The downstream RPC timeout in the review was not inevitable in the controlled probe. | Browser histories reach known and cold successors, compare C's route after both notices, and check later routed work. The SQLite adapter reserves a term inside a transaction before announcement; its real SQLite owner sees two empty elections with distinct durable terms. Six Chromium/OPFS leader-close histories, including a cold successor, passed on the repair. A combined real-host missed-notice, empty-election, passive-row history remains with the Chromium/OPFS owner. | +| **Only the elected local owner may allocate the next stream position** (M03). The ordinary apply guard and fail-fast invariant rule are the authority. | Normal apply checked both `isLeader` and `leaderId`; reconcile checked only `isLeader`. No legal trace was found that gives a Web Lock holder the contradictory identity. | A controlled contradictory-state witness rejects reconciliation before adapter entry. This is an invariant gap, not a demonstrated public route failure. | One `nextPersistedTxIfLeader` function now performs the identity, term, disposal, and Collection-state checks and position allocation for both paths. The Browser coordinator oracle passes after the extraction. | +| **An exact ID acknowledges its original committed position, while later durable writes can invalidate owned resume evidence** (M04). The core README defines the original-position receipt and fail-closed anchor. | SQLite already reports committed version 1 and latest version 2 for an earlier ID followed by a peer write. A separate wrapper witness rejects a resume generation advanced outside its owner. | The review correctly saw the old position but inferred a stale-resume bug from it. Returning the peer's later position as X's commit would misattribute that write. No same-path RED supports that proposed change. | The original committed position stays. A composed exact-ID, peer-write, wrapper-binding witness remains with the persisted wrapper owner in the coverage map. | +| **A settled reconciliation reload publishes durable row shape** (M09). The glossary's persisted-snapshot equality is the skip condition. | The wrapper covered sparse slots and enumerable array properties, but omitted equal-keyed nested class versus plain record. Ordinary `deepEquals` can equate those shapes. | A source write followed by a controlled durable plain-record reload was RED at the public nested prototype; a prototype-sensitive mutant passed. The repaired wrapper case checks both source and mounted live-query rows. | `equalPersistedSnapshotValues` distinguishes nested prototypes and preserves the existing array, Map, and Set shape rules. The newly introduced `deepEqualsStrict` root export is replaced. A real SQLite serialization and wrapper publication witness remains with the Node SQLite receiving owner. | +| **A reset starts a new row-version series; a position-only read does not publish rows** (oracle attack after M09). The SQLite schema-reset contract and publicly applied row-version distinction are the authority. | The wrapper had no history joining reset, delayed pre-reset notice, and a new post-reset commit. Its position watermark could falsely certify rows loaded before a later metadata read. | The lower-version and old-notice histories were RED on the first repair. Two reviewers then found both delayed-notice orders and a commit between reset reload and position read. A temporary no-fence mutant failed both notice orders; a mutant that set the public version from the metadata read failed at the missing public row. These were assertion failures at the intended cuts. | Three reset witnesses cover lower version, both notice orders, and the between-reads race. The wrapper rebases the durable version, keeps an old-term fence, and conservatively starts the new public version at zero. Its full oracle passes. The controlled reset premise is separately shown by real SQLite; a combined real-adapter wrapper reset and notice remains with the persisted SQLite receiving owner. | + +The model, history, path, and observation boundaries are explicit in those +owners. The term model keeps durable term state because a later cold election +distinguishes two histories with the same caller memory. The reset checks keep +durable position and publicly applied position distinct because the next +notification can distinguish them. The controlled Browser channel supplies +notice ordering; the real coordinator supplies route transitions. The wrapper +adapter supplies held read cuts; the real wrapper supplies Collection and live +query publication. These bounded histories establish their asserted cuts, not +all browser schedules or arbitrary reset interleavings. + +### Lossless finding ledger + +| ID | Original claim and proposed action | Technical verdict and evidence | PR action and durable value | +| --- | --- | --- | --- | +| M01 | Followers accept a closed leader's late equal-term heartbeat, reroute to A, then RPCs time out and source/subset work can fail. | Confirmed route defect at passive C; claimed downstream timeout is conditional. A/B/C controlled RED, later repaired route and real OPFS host GREEN. | `fixed-now`. Passive-route law in Browser coordinator and OPFS owner. | +| M02 | Election overwrites a heartbeat-learned term from SQLite and reuses T; use `Math.max` and remove the leader-only special case. | Confirmed reuse. The proposed memory-only maximum failed the cold-successor RED. | `fixed-now`. Reserve each election in the durable `leader_term` ledger. Browser/Electron require the capability; Electron IPC uses protocol v4. | +| M03 | Reconcile omits the local `leaderId` guard and duplicates next-position logic. | Guard mismatch and duplication confirmed; legal user-visible contradiction unproved. Invariant witness reaches adapter-entry cut. | `fixed-now`. Shared guard and next-position function rejects contradictory state before write. | +| M04 | Already-applied reports X's old version instead of the latest peer version, allegedly corrupting resume generation. | Old-position report is real and required by the README's exact-ID receipt. Existing SQLite and wrapper evidence supports fail-closed ownership; composed wrapper witness remains. | `accepted-design`. Preserve original ID identity; coverage map owns the joined witness. | +| M05 | Reconcile and normal source-success branches duplicate lifecycle, stream-position, and resume-generation bookkeeping. | Confirmed from source; no divergent public behavior was shown. | `fixed-now`. Both paths call `recordDurableSourceCommit`; wrapper oracle pins the receipt and row cuts. | +| M06 | Nested three-by-three retry loops can hold the apply mutex for about 90 seconds; cap attempts. | A controlled transport seam reached nine sends only when each outer round first lost two answers and then returned NOT_LEADER. Pure transport failure stopped after three sends. A proposed three-send cap failed the legal lost-answer/later-route witness, which requires a fourth send. No 90-second host latency or contract limit was established. | `accepted-design`. Keep the bounded existing reroute policy required for exact-ID liveness. Its possible work cost remains recorded; a latency budget or measured host violation would reopen optimization. | +| M07 | Exact `tx_id` lookup scans retained per-Collection rows because the primary key orders by term/seq; add a composite index. | Confirmed by real SQLite plan: the old plan sought only `collection_id`; the new plan seeks both predicates. No elapsed-time claim is made. | `fixed-now`. Add `idx_applied_tx_collection_tx_id` and a plan assertion in the SQLite adapter owner. | +| M08 | An older tab ignores the new reload marker because BroadcastChannel remains protocol v1; rolling deploy may leave stale public rows. | Cross-version mechanism is real. The Browser same-run recovery contract explicitly assumes the same protocol version; a version bump alone cannot make old code reload. | `accepted-design`. Keep the same-version boundary. A future cross-version guarantee needs negotiation or forced refresh and its own receiving oracle. | +| M09 | Newly exported `deepEqualsStrict` is misleading: class/plain values can compare equal and Map/Set ordering is significant, so reload may retain stale shape. | Confirmed for nested class/plain public rows. Map/Set order is conservative inequality, not the stale-row cause. Controlled source/live RED and repaired GREEN. | `fixed-now`. Use the qualified persisted-snapshot comparator and remove that new export name. | +| M10 | Every exact-ID reconciliation reloads and compares active rows, even after the original notification. | Work cost confirmed; the review overstated its frequency as every leadership handoff. A missed original notice makes a same-position reload necessary under the current contract. | `accepted-design`. Retain the correctness tradeoff. Any shortcut needs independent already-published evidence and must pass the missed-notice OPFS receiver. | + +The four accepted designs preserve observations or limits already in the +README, glossary, and coverage map. None is a deferred in-scope repair. The +remaining receiving cells have named owners above; no reachable counterexample +to the repaired laws was left known at this head. + +### Oracle-guide audit for the follow-up + +- ORC-001 to ORC-003: the README, glossary, and opening oracle prose state the + laws and bounds; independent durable-term, authored-row, and value-shape + ledgers sit beside their grammars, production drivers, public observations, + and checkpoints. No production comparator computes the expected prototype. +- ORC-004 and ORC-007: this follow-up adds fixed, controlled histories, not a + new important generated property. Existing generated campaigns and replay + interfaces are unchanged. +- ORC-005 and ORC-006: the Browser coordinator, real SQLite adapter, persisted + wrapper, and Chromium host run production paths. The original route and + nested-shape RED checks and the reset fence/public-version mutants fail at + public comparisons. Setup and timeout failures are not counted as kills. +- ORC-008 and ORC-009: the durable-term ledger retains state distinguishable + by a cold successor; durable and publicly applied positions remain separate. + New cross-subsystem terms appear in the glossary. +- ORC-010: controlled gates release in cleanup; the wrapper and host preserve + primary failure separately from cleanup diagnostics. +- ORC-011 and ORC-014: real SQLite supplies the durable no-write reservation + and version-zero reset premises; Chromium supplies the Web Lock/OPFS election. + The joined real-adapter reset/publication and nested-serialization receiving + cells remain explicitly open with their owners in the coverage map. +- ORC-012 and ORC-013: this revision-bound record states outcomes and + non-applicable generated triggers, original and neighboring histories, + hostile wrong designs, and remaining cells. Known/cold successors and both + reset notice orders distinguish the reusable boundaries. + +### Verification and reviewer assessment + +On `d6e2493b1`, the persisted wrapper oracle passed 662 tests with one +existing todo. Its six focused reset tests passed separately. Both reset +mutants failed the intended public-row assertions and were restored. The +Browser coordinator oracle passed 164 tests after the shared guard extraction. +Earlier on the same repair, the DB utils oracle passed 132 tests, Browser +coordinator suites 320, Electron suites 128, Node SQLite adapter contract 36, +and all six real Chromium/OPFS leader-close histories. Four affected packages +typechecked. Changed files passed Prettier and `git diff --check`; ESLint had +zero errors and 52 warnings in test fixtures. Vitest's optional package +typecheck emitted workspace `rootDir` source errors when run alongside tests; +standalone package TypeScript checks passed. The tests above ran with that +Vitest typecheck disabled. The production source diff at the executable commit +is 330 added and 102 deleted lines; tests and contract documentation are +reported separately in the commit diff. + +The reviewer had high discovery value: passive routing, the missing index, +shared bookkeeping, and nested public shape were all useful findings. +Proposed fixes were less reliable: memory-only term maximum missed the cold +successor, and the retry cap sacrificed a legal reroute. The resume-generation +and 90-second claims exceeded the evidence. Recommend hiring for code review, +with adversarial history checks before treating a proposed mechanism as the +cure. + +Final accounting: ten raw findings = six `fixed-now` (M01, M02, M03, M05, +M07, M09) plus four `accepted-design` (M04, M06, M08, M10). Zero items were +dropped, deferred, or left as confirmed-open. Accounting and authorized repairs +are complete at the named executable commit; the bounded receiving gaps above +remain in the coverage map. + +## Follow-up: collections CI and CodeRabbit review 5472852817 + +This addendum evaluated the complete CodeRabbit review of `3d7bd9e73` and the +failed `Test (collections)` job on that head. The executable repair is +`ef287d8464e3cbd640ab60098a838ac4384751ab`. Earlier entries remain +versioned evidence for their own heads. In particular, the earlier sentence +that called Electron IPC protocol v3 is superseded: the reviewed and repaired +protocol is v4. The Electron README and changeset now say v4, and both +directions of the IPC version test reject v3 as well as v1 and v2. + +### Laws, experiments, and inference + +| Law and authority | Earlier oracle reach | Decisive experiment and inference | Enforcement and remaining limit | +| --- | --- | --- | --- | +| **A row-version gap without `pullSince` replaces one complete durable public snapshot.** The persisted wrapper's complete-publication and accepted-source laws require readers to see an earlier permitted snapshot or the durable replacement at each change callback. | The wrapper had a term-jump row check but no change-callback trace for its no-`pullSince` fallback. | A legal baseline at version 1, missed version 2, and received version 3 were RED when the old fallback published `[]` between the baseline and durable rows. The replacement path passed with no empty or partial cut. The failure was at a public event, not only a final-row mismatch. | The wrapper oracle records each Collection change callback and permits only the baseline or all three durable rows. `reloadActiveSubsetsUnsafe` replaces the snapshot in one publication. Real lost-notice delivery through Electric/OPFS remains with the Browser receiver in the coverage map. | +| **An ordinary peer notice describes an already durable, contiguous commit.** The Electric recovery owner's peer/stream grammar requires the row, metadata marker, and reported position to describe one committed peer step before delivery. | The old fixture changed durable Maps directly, left the adapter position at zero, and announced row version 101. A new gap detector treated that as a missed 100-version history. | CI had eleven Electric recovery failures, including an empty intermediate event and an unexpected subset load. Committing each peer transaction through the fixture adapter before its notice, with the next durable row version, made all 60 recovery tests and all 629 Electric package tests pass without weakening the public-row or marker assertions. | The bounded Electric owner still checks complete intermediate rows, marker reach, deletion, full reload, and peer/stream pairs. Its mock SDK and adapter do not establish real cross-tab delivery. The original impossible notice remains a calibration for fixture legality, not a product RED for an ordinary peer commit. | +| **After a reset, an unlabelled same-term notice cannot publish old-epoch rows.** `CollectionReset` carries an epoch; `TxCommitted` does not. The reset/public-row law requires a durable reread for an ambiguous notice. | Earlier reset histories moved to a newer term. They did not count work after multiple same-term notices. | A controlled history now sends new-epoch commit A, delayed old-epoch notice, then new-epoch commit B under term 1. All public cuts contain only durable rows. It counts three subset reloads. A temporary no-fence mutant was RED at the public cut: `[first, stale]` instead of `[first]`. Restored production is GREEN. | CodeRabbit's hidden H1 cost observation is true at this controlled receive boundary. Its proposed one-reload cap lacks an epoch distinction and would permit the stale row. There is no bounded-work promise for this envelope, and the built-in coordinator currently has no `collection:reset` sender. A real same-term reset sender, or an epoch-bearing fast path with its receiving witness, remains with the Browser/OPFS owner. | +| **Coordinated Electron elections reserve a durable term before publishing a route.** The coordinator checks for `reserveLeadershipTerm` when an adapter is registered and the Electron main handler rejects a missing capability during reservation. | The generic `PersistenceAdapter` type keeps the method optional because `SingleProcessCoordinator` does not elect. The renderer IPC proxy exposes it; the main adapter is resolved per Collection. | Source inspection reaches the exact boundary: a missing main capability produces `InvalidPersistedCollectionConfigError` before `state.isLeader` and route heartbeat. The review's request to let such a custom adapter complete a coordinated election conflicts with the durable-term law. | The README now states the conditional main-adapter requirement and the supported single-renderer case. No new election algorithm was introduced. The existing Browser/Electron election and IPC oracles remain the production owners; an absent-main-capability real-process witness is outside this documentation correction. | + +The reset experiment tests a controlled envelope accepted by the wrapper, not +an emitted built-in reset. Its work count is measured, but no elapsed latency +or host-size bound follows. The older same-version protocol limit and the +separate real SQLite reset premise still apply. The new gap witness is bounded +to eager active rows and a recording adapter without `pullSince`; the Electric +fixture repair covers ordinary contiguous peer histories, not the same +lost-notice schedule. + +### Lossless CodeRabbit ledger + +| ID | Original claim and proposed action | Technical verdict and evidence | PR action and durable value | +| --- | --- | --- | --- | +| CR3-H1 | After `collection:reset`, a same-term leader causes every later `tx:committed` notice to reload, skip position observation, and miss the hydrated fast path. Add at least two same-term commits and make only the first or neither reload, perhaps by carrying an epoch or reserving a new term. The review marked this unverified and hidden. | Confirmed three reloads for three controlled ambiguous notices. The no-fence mutant publishes a stale row. No built-in `collection:reset` sender, latency bound, or work contract was identified. | `accepted-design` for the current conservative receive cost. The new same-term oracle and coverage map preserve the safety law and the condition for a future epoch-aware optimization; a one-reload cap is not a correct standalone fix. | +| CR3-I1 | `PersistenceAdapter.reserveLeadershipTerm` is optional, so a custom Electron main adapter without it cannot elect. Require it for Electron main adapters or return an unsupported result that the coordinator handles. | The inability to elect is real and deliberate. The shared type is optional for single-process use; the coordinated path already rejects before route publication. No successful election without durable reservation is a supported history. | `fixed-now` documentation: the Electron README states the conditional capability and the supported single-renderer path. Existing fail-fast runtime behavior stays. | +| CR3-I2 | Protocol constant is v4, but README and review record say v3; add a v3 rejection test only if that build was published. | Confirmed stale prose. v3 existed in earlier PR commits; main was v2. Both peers already reject any mismatched version, but the test omitted v3. | `fixed-now`: README and changeset say v4, this append-only entry corrects the earlier review record, and the two version-rejection loops include v3. | +| CR3-P1 | Consider running `coderabbit review --agent` locally. | Optional process suggestion in the review body and agent prompt. The raw review and local tests supplied the needed evidence; it is not a product or PR requirement. | `refuted` as a required action. The idea is preserved here. | + +The remaining 17 hidden file comments say LGTM and contain no additional +change request. The autofix checkbox and repeated agent prompts add no distinct +technical claim. Four raw claims are accounted for: two `fixed-now`, one +`accepted-design`, and one `refuted` as a required process action. No item is +deferred or silently dropped. + +### Verification and reviewer assessment + +The original persisted gap witness was RED at an exposed empty Collection and +GREEN after the atomic replacement. The no-fence mutant failed the new +same-term test at the stale public row. On the executable repair, the full +Electric package passed 629 tests, the persisted wrapper oracle passed 664 +tests with one existing todo, and Electron IPC passed 58 tests. All three +affected packages passed standalone TypeScript checks after the Electric +package declarations were built. Affected-file ESLint had zero errors and +pre-existing `require-await` warnings in the large wrapper test; Prettier and +`git diff --check` passed. These local checks do not substitute for the pending +required CI checks on the pushed head. + +CodeRabbit found a real documentation mismatch and a useful reset-work +observation. It assigned major severity to a synthetic receive path without +establishing a built-in reset sender, and its one-reload suggestion would lose +the old-epoch safety witness. It also treated a deliberately optional generic +capability as a supported coordinated-election fallback. Recommend hiring for +review work with explicit protocol-history and oracle calibration before +accepting proposed fixes. The accounting is complete at the reviewed head; +the executable repair is complete for the bounded laws above, with real-host +receiving limits retained in the coverage map. + +## Design grammar and TLC addendum (2026-10-09) + +The new bounded model, runner, case inventory, and exact TLC receipts are in +`review-evidence/issue-2085-tla/`. This is a design experiment, not a +TypeScript/SQLite refinement check. The model abstracts one source transaction +X, one same-key peer write Y, A/B leadership, passive C, one reset, optional +exact-ID pruning, lost and reordered notices, position-only observation, and +receipt settlement. The README states legal actions, exclusions, fairness, and +public checkpoints; the mapping names each production boundary. + +### Laws and rival predictions + +| Law and authority | Rival prediction | TLC and receiving result | +| --- | --- | --- | +| Every election reserves a greater durable term before announcing B. Coordinator README and glossary. | Reuse term 1 after a no-write A term; C accepts B then A's delayed equal-term heartbeat. | `DurableTermUnique` fails at election. With that law omitted, `SuccessorRouteStable` fails at C's route. The existing Browser and OPFS owners supply production election evidence. | +| A present exact X acknowledges X's original committed position even after Y; an absent X needs the unchanged row version and reset epoch. Coordinator README. | Check anchor before ID, report Y's position as X's, apply absent X after Y, or ignore reset epoch. | `PresentIdAcknowledged`, `OriginalPositionReceipt`, and `AbsentApplyNeedsAnchor` each reject the respective fault at the intended cut. Real SQLite already owns the exact-ID decision. | +| A pruned X cannot become a replay authorization. SQLite retention and anchor contract. | Treat absent/pruned X as safe merely because ID lookup missed. | A reachability control finds X applied, pruned, then unknown; the anchor fault is rejected. The model has no pruning age or capacity clock. | +| A reconciliation reload repairs missed rows despite an equal observed position, and a position-only read cannot publish rows. Coordinator contract and glossary. | Drop equal-position reload or publish observed durable rows from metadata alone. | `CertifiedDelivery` and `PositionReadIsNotPublication` reject the faults. The persisted wrapper and OPFS owners supply separate production receiving witnesses. | +| After a reset signal, an old-epoch notice cannot republish old rows. Controlled wrapper reset contract. | Trust the delayed old payload. | `CertifiedDelivery` rejects the fault. The model's reset-known premise matches the existing controlled wrapper histories. | + +TLC exhausted 8 route, 2,866 exact-ID, and 22,746 reset states with no safety +error. The same graphs passed the explicitly fair local-progress properties. +Thirteen wrong-design cases failed at their named invariant, and six negated +reachability controls found the intended lawful histories. The runner rejects +a different invariant or a TLC/tool error as a mutant kill. + +Two control repairs mattered. A first lost-notice witness marked X lost after +C had already reloaded; the retained witness records loss at reconciliation +and then a later reload. Pruning initially allowed A to apply the same +original request twice; the grammar now excludes that illegal history. Neither +was a production defect. + +### Wrapper receiving witness + +The persisted wrapper oracle now composes X's version-1 exact-ID +acknowledgment with a same-key Y at durable version 2, then runs owned resume +certification. Its source receipt fulfills in one sync run; public X and +durable Y/cursor are both observed; the later snapshot makes key-set evidence +`incompatible`. A temporary collaborator mutant that reported Y's position +as X's made that last assertion fail: it returned `consistent` evidence. +The mutation was restored. This covers the wrapper boundary with a controlled +coordinator. A real SQLite adapter plus wrapper in one receiving path remains +open; the adapter's exact-ID result is separately checked against SQLite. + +### Candidate law outside the established cut + +A separate TLC challenge deliberately asserts that no old row can be +published after durable reset *before* C has received a reset signal. It +fails in seven states: A commits X; B reserves term 2 and resets to empty +epoch 1; C receives A's version-1 notice before B's reset signal and +publishes X while durable rows are empty. The signal and commit messages +come from different owners and may be reordered. C has no new-epoch +knowledge at that cut. The built-in coordinator currently has no +`collection:reset` sender, so this is an unresolved proposed cross-tab reset +law, not a confirmed PR #2088 product regression. A future sender must define +the pre-signal public guarantee and add a real Browser/OPFS receiving witness. + +The model is exhaustive only within its stated small grammar. One real host +join of missed notice, empty election, and passive public rows remains open. +Cross-version peers, concurrent source transactions, multiple Collections, +provider cursor semantics, physical crash durability, and Electron IPC reply +loss remain outside this TLC claim. + +## CI follow-up: no-write term reservation during Electric resume (2026-10-09) + +The browser E2E workflow on `bfa09cc8a5b010473e4bb31065ebb1870caba057` +failed twice in `electric-hydration-straddle.opfs.spec.ts` after reopening the +last tab. Both runs reported `Electric persisted resume baseline could not be +certified` at the preloading checkpoint. The host log recorded the error and +public row, but not the generation tuple. The following controlled histories +test a specific explanation; the new-head browser run is still required to +confirm the host repair. + +**Law and authority.** The atomic-resume contract in the persistence README +certifies rows, source cursor, stream position, key set, and reset epoch from +one snapshot. A no-write election may reserve a greater durable term with +sequence zero while row version and reset epoch remain fixed. That transition +does not change rows or the source cursor, so an already certified on-demand +baseline remains usable. A committed write or schema reset does change the +baseline generation and cannot inherit that certification. + +**Competing predictions.** The old exact `(term, seq, rowVersion, resetEpoch)` +guard marks the harmless election incompatible. The repaired guard accepts a +greater term at sequence zero only while row version and reset epoch equal the +owned baseline. It continues to reject a changed row version or reset epoch. + +**Experiments.** A focused persisted-wrapper test gives its recording adapter +`(1, 1, 1, 0)` and then `(2, 0, 1, 0)` during on-demand certification. The +old guard was RED at `getKeySetEvidence()`: `incompatible` instead of +`consistent`. A second held-snapshot test composes the real SQLite adapter, +the persisted wrapper, and a mocked Electric stream. SQLite reserves term 2 +at sequence zero, keeps row version 1 and the reset epoch, and retains +consistent key-set evidence. With the old guard restored temporarily, this +receiving test was RED at the post-up-to-date Collection checkpoint: `error` +instead of `ready`. Restoring the narrow guard made both GREEN. The existing +held-snapshot committed-write and schema-reset tests remain negative controls. + +**Scope and verification.** The full persisted wrapper oracle passed 666 tests +with one existing todo; the full Electric package passed 630 tests. Both +affected packages passed standalone TypeScript checks. ESLint reported zero +errors after removing an unnecessary optional-method guard, with 24 existing +fixture warnings in the large persisted oracle. The local Electric witness +uses real in-memory SQLite and a mocked ShapeStream. It does not enact the +Chromium/OPFS restart, Web Lock election, or live Electric delivery; the new +head's browser E2E owns that receiving result. The coverage map records this +join and its remaining host limit. diff --git a/docs/contributing/oracle-reviews/issue-2152-initial-ordered-fallback.md b/docs/contributing/oracle-reviews/issue-2152-initial-ordered-fallback.md new file mode 100644 index 0000000000..1629f107d4 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2152-initial-ordered-fallback.md @@ -0,0 +1,391 @@ +# Initial ordered fallback readiness (issue #2152) + +Reviewed code commit: `bb1ed1d87ffb2de1b27b8a024fc402d2755dcb26` (parent +`91cefa2602cf35929cbc5402f6afd7c8dbe9c88d`). This record and the final +coverage-map limit are documentation follow-ups to that code commit. + +## Contract and finding + +The initial synchronous cut in +`packages/db/src/query/live/ARCHITECTURE.md` covers every acquisition needed +to complete an unpublished first ordered window. A provider-ordered prefix +whose locale-string or null boundary cannot form a cursor may need a +full-source fallback. When each request returns literal `true` after its rows +apply, the rows and `ready` status are owed before the initiating `preload()` +returns. A Promise fallback stays asynchronous. The cut does not promise a +render-time result for a deferred on-demand React source or synchronous timing +for repair, replay, or a later window operation. + +The old warm null-boundary test read status before `preload()` started +acquisition. It therefore accepted `loading` without observing the promised +cut. The loader also classified a full-source request as initial only if no +earlier source request had settled. That excludes the fallback after the first +ordered page. The revised oracle starts the sync run and records request shape. +It reads public state at fallback entry and immediately after `preload()` starts, +then compares again after settlement. Its two-row ISO-string reference predicts +row 2 for distinct dates and row 1 for tied dates with a public-key tie-breaker. +It does not use the production comparator. The warm null case checks an already +installed source, where the continuation has no new row input to drive another +graph turn. + +The independent oracle reviewer found two gaps before production changed. +First, the architecture said full-source repair always settles +asynchronously, although the prior design review left that timing open. The +architecture now states the actual atomic-publication obligation without +fixing repair scheduling. Second, the new index cells could both have used +the same ordered request path. The first request now checks offset `0` for +the indexed page and no offset for the unindexed prefix. The reviewer found +no remaining blocking issue in the revised architecture, oracle, and coverage +map. The reviewer inspected the path discriminator; no index-selection mutant +was executed. + +## Executed evidence + +| Revision and path | Checkpoint and result | +| --- | --- | +| Parent commit, focused DB oracle | Three intended assertion failures: eager and unindexed locale-string literal-`true` cells returned empty `loading`, and the warm null-boundary case returned empty `loading`. Promise and zero-window neighbors passed; all four index-path shape assertions passed after they were added. | +| Parent commit, React eager receiver | The first layout commit returned `id: undefined`, `status: loading` instead of row 2 and `ready`. An attempted on-demand React first-commit fixture issued no requests by that commit, so it was replaced with the eager receiving path; it is not evidence about the loader gate. | +| Reviewed code commit, focused DB oracle and React receiver | Eight focused DB cells and the React first-commit witness passed. | +| Reviewed code commit, broader tests | 427 ordered DB tests passed across the lifecycle, loader-state, and work owners; 78 React conformance tests passed. Both package TypeScript checks, Prettier, and `git diff --check` passed. ESLint had no errors and two warnings on unchanged lines. | +| Adversarial follow-up, controlled adapter | All 268 ordered lifecycle tests passed, including eight index × settlement × row-order cells. The fallback request has no predicate, order, limit, cursor, or offset. The public rows remain empty and status stays `loading` at fallback entry. | +| Adversarial follow-up, Query DB receiver | Both focused warm ordered tests passed. A warm cache returned literal `true` on both raw adapter calls. Row 2 was visible at the first return, rows 1 and 2 at the fallback return, and the receiving `findOne()` was immediately ready with row 2 without another fetch. Vitest reported no type errors. | +| Event follow-up, controlled adapter | All 276 ordered lifecycle tests passed, including eight subscriber-event cells. Each observes one completed-window insert after fallback settlement, with the complete row visible in its callback. Promise cells have no nonempty event at the immediate cut. A temporary duplicate-event fault fails all eight cells at the publication comparison. | + +Four independent oracle reviewers challenged the contract, request path, +observation checkpoints, and receiving boundary. They found three false-green +gaps in the original four cells. A locale-string cursor mutant passed them +because the oracle omitted the fallback's `where` predicate. A mutant that +dropped fallback source changes also passed the original 264-test lifecycle +suite because distinct dates kept row 2 as the winner. The original oracle +did not read public rows when the fallback started. The new request assertion, +tied-row dimension, and entry checkpoint close those gaps for the eight cells. + +The locale-string cursor mutant now fails all eight cells at the request-shape +assertion. The dropped-fallback-changes mutant fails all four tied-row cells at +the public-row comparison: it returns row 2 instead of row 1. A synthetic +early-publication fault fails all eight cells at the fallback-entry read. These +were comparison failures, not setup failures or timeouts. That state read alone +cannot reject a transient or duplicate event after fallback entry. A separate +subscriber-event assertion now records nonempty change batches across the same +eight histories. A temporary duplicate-event fault fails all eight new cells +at that assertion. Empty readiness callbacks do not count as row publication. + +The original `!hasSettledSourceRequest` gate is the calibrated wrong design: +it reaches both acquisitions but fails the immediate comparison, then passes +after settlement. The production repair preserves the existing exclusions for +ordering repair debt and explicit window operations from the synchronous path. +Its original production diff is five added and three removed lines, net two. +Test and contract prose are accounted for separately. + +## ORC-001 through ORC-014 + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | The authority is the architecture's initial-readiness cut. The owner states that source exhaustion and React render timing are outside the controlled oracle. The separate Query DB test covers a warm real adapter handoff. | +| ORC-002 | Expected ids come from two fixed ISO dates, a descending singleton, and the public-key tie-breaker; synchronous versus Promise readiness comes from the contract. Neither the loader's cursor classifier nor production comparator computes the reference. | +| ORC-003 | Opening and local prose distinguish the law, eight-cell grammar, independent reference, live Collection driver, fallback-entry and immediate observations, subscriber changes, and refinement checks. The warm test explains its no-input continuation. | +| ORC-004 | The finite index × fallback-settlement × row-order matrix runs eight state cells and eight event cells, not a new generated-history claim. Request offsets establish distinct index paths. The existing generated lifecycle grammar was unchanged. | +| ORC-005 | `createLiveQueryCollection()` and `preload()` execute the real core path. The recorder captures both requests before the immediate public `toArray`/`status` comparison. A subscriber registered before the first source write records public change batches without moving the preload cut. The controlled adapter honors predicates, and its writes and `commit()` precede literal `true`. | +| ORC-006 | Unchanged production fails three assertions at the intended immediate checkpoint and passes after the fix. Two plausible production mutants and two synthetic publication faults fail at their intended comparisons in the stronger cells. None is a timeout or setup failure. | +| ORC-007 | Not triggered by the new finite cells. The owner's pre-existing fixed and random campaigns are unchanged and ran in the broad suite. | +| ORC-008 | No stateful reference-model state was introduced or revised; the new reference is a stateless observation over settlement and row order. | +| ORC-009 | “Initial chain” maps to the ordered acquisition plus fallback before first publication. `rows` and `status` are public observations, not model state with a production counterpart. | +| ORC-010 | `finishOracleCleanup` releases the event observer, live Collection, and source Collection while retaining a primary mismatch and separate cleanup errors. The owner's failure-fidelity test remains in the passing broad run; no shrinking is used for the finite cells. | +| ORC-011 | A locale-string cursor mutant exposed a shared request-shape blind spot in the original oracle. The revised oracle rejects it and requires the controlled provider to honor the request predicate. The Query DB warm-cache receiver checks a separate adapter path. | +| ORC-012 | This record identifies applicable obligations, non-applicable triggers, the exact reviewed code commit, and the bounded bug-class result. | +| ORC-013 | Literal `true` versus Promise separates the synchronous boundary; indexed versus unindexed acquisition, distinct versus tied dates, and a warm null boundary are adjacent legal witnesses. The event cells require one final publication after fallback settlement and no Promise publication at the immediate cut. The old gate fails two string cells and the warm cell at the named cut. | +| ORC-014 | The controlled adapter's premise is an applied receipt followed by literal `true`. The Query DB warm-cache test witnesses two real adapter returns of literal `true` with source rows visible at each return and an immediately ready receiver. React's eager source supplies a separate first-commit witness. | + +## Bounded result + +The repair is evidenced for the fixed indexed and unindexed locale-string +`findOne()` histories, distinct and tied rows, a warm null boundary, Promise +fallbacks, an eager React first layout commit, and a warm Query DB adapter +handoff. The Query DB witness covers cached subsets. It does not establish a +first-commit React result for a real on-demand provider or other adapters. +The event assertion covers the eight controlled fallback histories. It does +not establish every event ordering or another provider's publication path. +No reachable counterexample remains in the executed core and receiving paths +at their stated checkpoints. + +## Follow-up review of the readiness proxy + +A seven-item code-reading review targeted `6bc943992`; the evaluation used +`1e4bba40c` after a type-only test fix. The +accepted law promises a synchronous result for a first ordered window whose +required acquisitions return literal `true` after their writes apply. It does +not require a later full-source acquisition to settle asynchronously. A later +request without ordering-repair debt may finish in the same graph turn. + +A public probe reached the reviewer's publication/readiness distinction: a +joined-filter window published row 1 while a pending child demand kept the +live-query Collection `loading`. Removing row 1's join match requested another +ordered page, received a null boundary, then requested the full source after +that first publication. The original `!canRetryRepair()` check classified it +as initial, but its public rows and change event remained coherent. The +reviewer's suggested insert alone did not make a full window need more data. +In a separate ready post-publication probe, dropping the check changed only a +transient `isLoadingSubset` observation: the final row was 2 in both versions. +The revised loader uses `isFullSource && !needsOrderingRepair` for synchronous +eligibility, leaving retry policy with its own callback. The full-source +repair and explicit-window exclusions remain in place. + +An async first page followed by a literal-`true` fallback tested the claimed +request/observe race through a public live-query Collection. Synchronous +fallback writes left status `loading` and rows unpublished until `observe()` +ran; after preload settled, it was `ready` with the expected row. A direct +loader probe could flip a mock callback during `request()` and change the +branch, but did not establish that transition on a legal public path. Removing +the callback from this branch also removes that sensitivity. + +The old ordered lifecycle owner accepted a mutant that removed the readiness +clause: all 276 tests passed. That mutant is permitted by the documented +timing law, so its survival does not expose a missing required timing +assertion. The retained initial cells still reject the old +`!hasSettledSourceRequest` gate at the immediate public checkpoint. A new warm +null-boundary cell covers a constructor-started on-demand sync run: it sees the +first ordered request, but not the fallback or publication, at the immediate +preload cut; after settlement it sees both requests, the four expected rows, +and `ready`. The adjacent preload-started cell remains synchronously ready. +The focused warm cells and all 277 ordered lifecycle tests passed with no type +errors. Other adapters and a real on-demand React first commit remain outside +these witnesses. + +## Post-publication oracle follow-up + +Reviewed oracle and coverage-map commit: `44cf62e2d9597e2a58a213d24c31e8733fdd2884` +(parent `3aca442c6549048246379661f2dcc133f12d0d53`). This section is a +documentation follow-up to that exact code revision; the earlier ORC table +describes the initial-fallback change. + +The ordered-lifecycle oracle now exercises the publication/readiness distinction +through the public live-query Collection. Its finite model joins three root +rows to marker rows and takes the first eligible row in nulls-last order. An +on-demand child holds Collection status at `loading` after root 1's first +window publishes. Two legal marker histories, deletion and retargeting, remove +root 1's match. The next ordered page supplies ineligible root 2 at a null +boundary; a later unfiltered full-source request supplies eligible root 3. +The controlled adapter evaluates request predicates and cursor expressions, +sorts the requested page, and applies its writes before returning literal +`true`. + +The oracle checks the already public row and `loading` status at fallback +entry, the pending child acquisition, the ordered page before the full-source +request, the final public row, and the net delete/insert events. Its explicit +`getKey` uses the root ID, so the comparison does not depend on the compiler's +composite-key encoding. At each nonempty event callback, applying its changes +to the prior public snapshot must reproduce the rows read in that callback. +The net comparison allows either order for changes to distinct keys. It waits +for the final public change rather than a fixed number of scheduler turns. It +also keeps every callback-visible window within the one-row limit. The +refinement check runs before cleanup so its mismatch remains the primary +diagnostic if cleanup fails. It allows coherent intermediate publications and +makes no +synchronous-timing claim for work started after first publication. A temporary +fault kept the full-source request and its reported served rows but dropped +the newly delivered root 3 write. Both histories failed at the final public +snapshot: the faulty run showed `[]` and `ready` instead of root 3 and +`loading`. The fault was removed after calibration. +Another temporary fault duplicated the recorded public event batch. Both +histories failed at the net change-event comparison while their final rows +remained correct; this fault was also removed. + +At the reviewed code commit, all 279 ordered lifecycle tests and the DB test +TypeScript check passed. Prettier and `git diff --check` passed. ESLint had no +errors and one warning on an unchanged line. An independent source review +found and prompted repairs to the key-encoding assumption, event order, +page-before-fallback reach, pending-child reach, fixed-turn wait, callback +window bound, and failure fidelity before this commit. + +This is a controlled core witness for these two marker actions and one null +boundary. It does not establish other provider delivery schedules, real +adapter handoffs, or a first React commit over an on-demand source. The +initial synchronous-cut histories above retain those separate limits. + +| Requirement | Post-publication outcome | +| --- | --- | +| ORC-001 | The architecture's coherent-publication and pending-demand laws govern this later fallback. The synchronous first-window cut expressly excludes work started after publication. | +| ORC-002 | The expected row comes from a finite marker join, nulls-last sort, and one-row slice. It does not call the loader or its ordering classifier. | +| ORC-003 | Prose beside the model and driver names the law, two-action grammar, controlled production path, public cuts, and refinement check. | +| ORC-004 | Not triggered: these are two enumerated histories, with no generated-history coverage claim. | +| ORC-005 | `createLiveQueryCollection` and `preload` reach a published row with a pending child acquisition, then an ordered cursor page before the unfiltered full-source request. Public callback rows and changes are compared after the needed row is delivered. | +| ORC-006 | A temporary dropped full-source write failed both histories at the final public-row comparison; a temporary duplicate event failed both at the net-event comparison. Both faults reached the fallback and were removed. | +| ORC-007 | Not triggered: the owner's existing fixed and random campaigns were unchanged. | +| ORC-008 | Not triggered: the new model is a stateless finite recomputation. | +| ORC-009 | A marker mutation is one source Collection sync transaction; the model's eligible result is the live-query Collection's public row. A pending child request keeps Collection status `loading` after publication. | +| ORC-010 | The comparison runs before cleanup. `finishOracleCleanup` retains a primary assertion and distinct cleanup failures; the child gate is released and every Collection and observer is cleaned up. | +| ORC-011 | No shared semantic fault requiring a second result formulation was identified. The reviewer found an internal-key encoding dependency; explicit `getKey` removes it. | +| ORC-012 | This section records the exact code revision, checks, faults, and bounded claim. | +| ORC-013 | Not triggered: the evidence claims only two named marker actions and one null-boundary path, not a reusable timing or range law beyond them. | +| ORC-014 | No real-provider handoff claim is made for this controlled later-fallback path. Other adapters and on-demand React remain separate receiving limits. | + +## Post-publication response schedules + +Reviewed oracle and coverage-map commit: +`762a29a4e777629470b9011a6c17e537401f005d` (parent +`06ba31eaeecd44e5172391bbde9d3a72957ea29e`). This section supplements +the two-action post-publication record above; no production code changed. + +The finite grammar now runs eight histories. Marker deletion and retargeting +each cross a literal-`true` fallback, a Promise result after applied writes, +and a fallback returning its held applied receipt while child demand remains +pending. Each action also runs the held receipt after the child demand settles. +All histories first observe row 1 published while that child demand still keeps +the live-query Collection `loading`. The ready-child histories then await the +initial preload and observe row 1 with `ready` status and no subset load in +flight before changing the marker. + +The held-receipt provider starts an optimistic update to an unselected root +field after the ordered page applies. While its persistence is pending, root +3's full-source sync transaction has a pending applied receipt and root 3 is +absent from the root Collection. The ready-child histories observe the public +root and live-query `isLoadingSubset` flags become true at this held cut. After +releasing the update, the driver waits for the public row-3 change and the +relevant public loading-end signals before comparing the final window and +change events with the finite join model. The marker mutation may publish an +empty intermediate window or retain row 1; callback replay checks that each +observed event matches its public snapshot. The final net delta must contain +one delete for row 1 and one insert for row 3. + +A temporary loader fault ignored the pending applied receipt while retaining +the provider writes. All four held-receipt histories reached the comparison +and failed at the public live-query loading flag: the faulty run reported +`false` while the root receipt was still pending. The fault was removed. The +normal eight focused histories and all 285 ordered lifecycle tests passed; +DB test TypeScript, Prettier, and `git diff --check` passed. ESLint reported no +errors and one warning on an unchanged line. Two independent oracle reviewers +found and prompted repairs to an overstrict old-row intermediate assertion, a +tautological response-kind check, unwitnessed blocker startup, dropped cleanup +diagnostics, and a final checkpoint tied to one scheduler turn. A final +read-only check found no race or cleanup defect in the revised event wait. + +The Promise-after-applied-writes history exercises the asynchronous callback +path and checks eventual public coherence. The architecture gives later +full-source work no initial synchronous cut, so this case does not distinguish +that Promise from a literal return at an immediate public checkpoint. A held +Promise after applied writes with overlapping source changes remains for this +owner to witness. The held `isLoadingSubset` comparison proves the public load +tracker retains the applied receipt; it does not alone prove that every ordered +publication participant waits for it. The row and event comparisons cover the +named final checkpoint. Real provider handoffs and an on-demand React first +commit remain outside this controlled core witness. + +## Later-fallback re-review at `cdc370b04` + +The initial synchronous cut ends when the first window publishes. A later +full-source fallback without ordering-repair debt may complete in the same +graph turn, but the contract does not require that timing. A controlled public +probe observed row 3 and its change event before the marker transaction's +`commit()` returned for literal `true`. Promise and held-receipt responses had +not published row 3 at that cut. The oracle retains its coherence law across +both schedules instead of turning the current later timing into a guarantee. + +Two additional histories make the first ordered acquisition's adapter unload +throw while a later literal-`true` fallback replaces it. The marker +transaction's `commit()` still returns `true`; the successful row-3 window and +its change event remain, and the live-query Collection reports `error`. A +temporary fault that rethrew the already reported release error from the +subscriber failed both histories at the marker commit. The direct loader's +release-error tests continue to check its separate caller boundary. + +The controlled providers now record applied-receipt shapes and compare them +after production returns. Deliberately reversing one assertion inside the +provider had previously produced six unrelated setup failures among the eight +post-publication histories, showing how request-failure handling could hide +the intended diagnostic. The architecture's prior statement that full-source +repair keeps asynchronous settlement was restored; the untested broad +publication-hold sentence added during this PR was removed. The changeset +names the permitted later completion. Framework render timing remains outside +the core cut, and the React first-layout-commit witness does not claim to +cover Vue, Solid, Svelte, or Angular. + +## Held provider Promise across a later marker change + +Reviewed oracle and coverage-map commit: +`a613f96e07728257b543e65695dfc5266a5c8a70` (parent +`1c557a4ddc7a03b08059ea2f069f26e6770de3dd`). This section supersedes the +earlier open held-Promise item above without changing that historical record. +No production code changed. + +The architecture requires coherent public windows and callback events when +source changes overlap ordered work. It does not prescribe synchronous timing +for a full-source fallback started after first publication. The finite grammar +adds four histories: deletion or retargeting of marker 1, each with pending or +settled child demand. The fallback applies root 3 and returns a Promise that +remains pending while marker 2 retargets from root 3 to root 2. The independent +join model predicts root 3 after the first marker action and root 2 after the +second. A coherent root-3 window may publish before marker 2 changes; it may +remain visible while the later change is pending. The oracle does not require +or forbid either schedule. + +The driver confirms that root 3 applied, the provider Promise remains pending, +and the root Collection reports a pending subset at the overlap cut. It reads +the public window before and after marker 2 changes, then compares the final +window with the model. It replays every public change callback against that +callback's visible rows. If root 3 published before the overlap, its insert +and later delete are expected; an insert first published after the overlap is +rejected. The provider request still has no predicate, cursor, order, limit, +or offset. A synthetic wrong-result control adds a coherent stale root-3 +publication and retraction after the correct root-2 result, leaving final rows +unchanged. The event comparison rejects that control. + +After the commit hook, the full ordered-lifecycle file passed 292/292 tests +with no Vitest type errors; Prettier and `git diff --check` passed. ESLint had +no errors and one warning on an unchanged line. A temporary loader mutant +that replaced `settlesAsync ? result : Promise.resolve()` with +`Promise.resolve()` survived all four new histories. This is a **survival**, +not a kill: all provider writes in this fixture had already applied, and the +source Collection still tracked the provider Promise, so these public cuts did +not distinguish early loader completion. The mutant was removed. The new +witness protects overlap publication and events, but it does not establish +that the loader waits for provider settlement before claiming full-source +authority when later provider writes or rejection are possible. That separate +ordered-acquisition limit is retained in the coverage map. + +| Requirement | Outcome for this follow-up | +| --- | --- | +| ORC-001 | Atomic ordered publication and the absence of a later synchronous cut come from `ARCHITECTURE.md`; the four controlled histories and remaining settlement limit are explicit. | +| ORC-002 | The finite marker-to-root join and null-last sort compute the expected winner without the loader's request or completion classifier. | +| ORC-003 | Opening and local prose distinguish the contract, marker grammar, independent join model, live Collection path, public cuts, and comparison. | +| ORC-004 | No new generated-history claim. The fixed two-marker-action × two-child-state grammar reaches all four cells; root-source changes and later provider writes are excluded. | +| ORC-005 | The real `createLiveQueryCollection`/`preload` path records unfiltered fallback requests, applied root 3, a pending provider Promise, public rows, and callback snapshots at named cuts. | +| ORC-006 | The coherent stale-event wrong-result control is rejected at the event comparison. The separate early-loader-completion mutant survived and narrows the claim as stated above. | +| ORC-007 | No new important generated property; existing fixed and random lifecycle campaigns are unchanged. | +| ORC-008 | The finite model adds a second marker state because the next legal marker action changes the winning root from 3 to 2; it adds no loader state. | +| ORC-009 | Marker mutation, applied source row, provider settlement, public window, and callback publication retain the glossary's separate boundaries. | +| ORC-010 | The provider gate resolves before cleanup even after a primary failure; the existing cleanup helper preserves secondary diagnostics. | +| ORC-011 | No plausible shared semantic fault needing a different join formulation was identified for these three fixed roots. | +| ORC-012 | This exact-commit record gives the observed reach, wrong-result rejection, mutant survivor, and remaining owner limit. | +| ORC-013 | The claim is limited to four named histories and their nearby stale-event control, not a universal provider-settlement boundary law. | +| ORC-014 | The provider is controlled; the test makes no new real-adapter or framework-render claim. | + +## Late provider write and acquisition lease + +Reviewed oracle and coverage-map commit: +`2c01526a67d03040366922f2b78449b70374106b` (parent +`ff23fd2ecd887cef39459add6aba925eb3b09dc6`). This section follows the +held-Promise overlap record above. No production code changed. + +Four neighboring histories keep the same first marker deletion or retarget and +pending or settled child demand. The full-source provider applies root 3, then +holds its returned Promise. Marker 2 retargets from root 3 to the as-yet-absent +root 4. After the gate opens, the provider applies root 4 before its returned +Promise settles. The independent join model predicts root 4 at the final public +checkpoint; callback replay and the net change comparison check the path to +that result. The adapter also records acquisition unloads. The cursor-page +lease must remain active at the held-Promise cut and retire after successful +full-source settlement. This follows the architecture's rule that authoritative +full-source success retires settled finite acquisitions; a pending provider +answer has not established that success. + +The normal focused histories passed, and the full ordered-lifecycle file passed +296/296 tests with no Vitest type errors. Prettier and `git diff --check` passed; +ESLint had no errors and one warning on an unchanged line. A temporary loader +mutant that replaced the provider Promise with `Promise.resolve()` failed all +four new histories at the held cursor-page lease assertion. The provider entry +and overlap cuts were reached, so these are intended-checkpoint assertion +failures. The mutant was removed before the code commit. This closes the +specific false-green result from the prior held-Promise histories: early loader +completion can no longer pass this bounded late-write schedule. Provider +rejection, overlapping root-source changes, real adapter delivery, and an +on-demand React first commit remain outside this witness. diff --git a/docs/contributing/oracle-reviews/issue-2163-eager-restart-truncate-bb2122012.md b/docs/contributing/oracle-reviews/issue-2163-eager-restart-truncate-bb2122012.md new file mode 100644 index 0000000000..afddedcf66 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2163-eager-restart-truncate-bb2122012.md @@ -0,0 +1,115 @@ +# Eager cleanup-restart truncate audit + +Reviewed code commit: `bb21220123b857e5ff9b8f4ecc332481014fd884`. + +This review covers the Collection lifecycle oracle repair and production change +for issue #2163 and PR #2173. The accepted law comes from the Collection restart +contract in `packages/db/src/query/live/ARCHITECTURE.md` and the glossary's +distinct demand, acquisition, sync-run, readiness, and settlement boundaries. +It does not turn an internal direct-subscription result into a public preload +success rule. + +## Law, histories, and observation cuts + +A demand created while its Collection is cleaned up remains detached until a +new sync run supplies its evidence. For an eager Collection, Collection +readiness supplies that evidence without a subset acquisition. Its direct +subscription reports a pending `onLoadSubsetResult` promise synchronously. A +truncate replay or recoverable error before readiness does not fulfill that +promise. At readiness, the promise fulfills and the subscriber has received the +exact replacement rows. An on-demand control instead uses one settled physical +acquisition. + +The bounded matrix enumerates 26 histories: + +- explicit eager and default eager configuration; +- absent and present adapter loaders; +- empty and one-row replacements; +- no interruption, truncate replay while loading, and recoverable error before + same-run readiness; and +- two on-demand controls, one for each replacement. + +The direct production driver publishes an old row, cleans up the Collection, +creates demand through `requestSnapshot`, starts a new sync run, and holds eager +readiness. It compares the result promise's settlement, subscription readiness, +Collection status, accepted physical-acquisition count, and final +subscriber-applied rows. The Query Collection receiving witness uses a real +QueryClient, automatic Collection garbage collection, an eager Query +Collection, and `DbClient.preloadLiveQuery`; it holds the second query result +and compares public preload settlement, Collection status, and final rows. + +The matrix does not govern demand first admitted after an eager restart, +terminal eager failure, retired or concurrent eager owners, same-turn +interruption schedules, or asynchronous on-demand acquisition. Those limits are +recorded in the coverage map. Public preload failure remains separate from the +same-run internal recovery participant exercised here. + +## RED, calibration, and GREEN + +Before the truncate repair, 22 of 26 matrix cells passed. Exactly the four eager +cells with a present loader and a truncate replay fulfilled their result promise +and reported subscription readiness while Collection status was still +`loading`. The corresponding absent-loader cells stayed pending. This rejected +the plausible wrong rule that adapter loader presence, rather than sync mode, +decides whether truncate replay reacquires demand. + +Production now permits subset reload after truncate only for an on-demand +Collection with an installed loader. The same matrix passes 26 of 26 cells, and +the complete lifecycle history file passes 86 of 86 tests. The related lifecycle, +truncate-readiness, and Query Collection run passes 538 of 538 tests across four +files. Both affected packages build; their linters report no errors, only +pre-existing warnings. Prettier and `git diff --check` pass. + +## ORC-001–014 review + +| Requirement | Outcome | +| --- | --- | +| ORC-001 contract authority and limits | Applicable. The architecture's direct-subscription restart contract requires cleanup-detached demand to wait for new-run evidence and says eager Collections have no subset reacquisition barrier. The executable prose and coverage map state the bounded histories and open eager regimes. | +| ORC-002 independent judgment | Applicable. The expected function derives settlement, readiness, exact replacement rows, and accepted acquisition count from sync mode and authored history axes. It imports no production classifier, loader-presence branch, or subscription helper. | +| ORC-003 distinguishable responsibilities | Applicable. Opening prose states the law and limits. The companion grammar contains the bounded cases and independent expected observations. The test keeps its real Collection driver and comparisons beside the named checkpoints. | +| ORC-004 generated-history grammar controls | Not triggered by this repair. The 26 cases are bounded enumeration, not a generated property. Every axis and marginal value is listed above. The broader generated lifecycle grammar remains on-demand only, and the coverage map no longer attributes eager coverage to it. | +| ORC-005 production path and observation | Applicable. The driver calls real `requestSnapshot`, cleanup, restart, truncate, error, and readiness boundaries. It observes immediate promise creation, promise settlement, subscription readiness, Collection status, accepted physical acquisitions, and exact final subscriber rows. The recorder does not claim callback-cardinality coverage; the existing acquisition-cell and reentrancy owners retain that separate law. No missing source fact was identified for these checkpoints. | +| ORC-006 checker calibration | Applicable. The pre-repair loader-presence design reached the intended loading checkpoint and failed exactly four eager truncate cells by assertion. Loader-absent, no-interruption, recoverable-error, and on-demand neighbors passed, distinguishing the repaired rule from a trace-only expectation. | +| ORC-007 fixed/random campaigns and replay | Not triggered. This repair changes fixed bounded enumeration, not an important generated property. The surrounding lifecycle owner's existing fixed/random campaigns and replay interface are unchanged. | +| ORC-008 stateful-model minimality | Not triggered. The focused expected function is a stateless projection over one enumerated history. It adds no state to the surrounding stateful lifecycle model. | +| ORC-009 vocabulary mapping | Applicable. Collection status and subscription status stay distinct. Demand, physical acquisition, truncate replay, sync run, readiness, settlement, public snapshot, and private replacement follow the glossary. `result` is the local observation of the `onLoadSubsetResult` promise, not a new production state. | +| ORC-010 failure fidelity and cleanup | Applicable. The focused driver uses `withOracleCleanup`, which preserves a primary refinement failure as the error or `AggregateError.cause`, retains later cleanup failures separately, unsubscribes, and cleans up the Collection. The surrounding generated driver keeps its existing failure-preserving cleanup and shrink context. | +| ORC-011 independent second formulation | Applicable with a recorded limit. The Query Collection witness independently receives the ordinary successful-restart law through automatic garbage collection, Query adapter restart, and live-query preload with a real QueryClient. It does not exercise loader-present truncate replay or recoverable error. Those histories remain evidence at the controlled core boundary only; no public second formulation is claimed for them. | +| ORC-012 review evidence | Applicable. This record is tied to the exact executable commit, accounts for every numbered requirement, records RED/GREEN evidence and the original finding ledger, and makes only the bounded closure claim above. | +| ORC-013 distinguishing boundary witness | Applicable. Loader-present and loader-absent eager truncate cells share sync mode and replacement but differed under the wrong design. Eager and on-demand cells share loader shape but require different acquisition outcomes. Empty and one-row replacements require exact deletion or replacement at settlement. | +| ORC-014 controlled-premise handoff | Applicable. The controlled core adapter establishes the cleanup/restart/readiness premise. The Query Collection witness receives only the ordinary successful-restart premise through automatic garbage collection and a real QueryClient. It does not receive the loader-present truncate or recoverable-error premises, and a live network provider is not part of the claim. | + +## Original review ledger + +The source review contained eight numbered findings and one execution caveat. +No item is dropped here. + +| ID | Original claim | Verdict and evidence | PR action | Durable value | +| --- | --- | --- | --- | --- | +| R1 | A truncate during eager restart loading can resolve early when a loader exists. | Confirmed. Four loader-present eager truncate cells failed before the repair; loader-absent neighbors stayed pending. | `fixed-now` by the sync-mode truncate gate and matrix expansion. | The loader-shape distinction remains a permanent calibration witness. | +| R2 | An eager restart entering error must settle rather than hang. | Split by boundary. The matrix proves that the direct-subscription promise remains pending through legal same-run recovery, while public preloads have their own rejection boundary. The accepted contract does not decide a direct subscription's terminal eager-failure outcome. | `accepted-design` for this PR's scope; the user explicitly chose not to add a terminal direct-subscription contract. The matrix observes `loading -> error -> ready` without claiming terminal or public preload recovery. | Terminal eager failure remains explicitly open in the coverage map. | +| R3 | A second eager request admitted during loading must wait. | Refuted as a product defect. Public authority requires initial live-query readiness to wait for eager source Collection readiness, not every internal eager snapshot result. | No production change and no overbroad oracle rule. | Demand first admitted after restart remains an explicit coverage limit rather than an invented contract. | +| R4 | The eager rule belongs only in a readiness listener, not restart-demand code. | Refuted as a universal repair. It depended on R2 and R3's unsupported internal rule. The confirmed truncate defect belongs at truncate replay's acquisition decision. | No universal listener change; R1 is fixed at `handleTruncate` using sync mode. | Future fixes must keep Collection readiness separate from subset reacquisition. | +| R5 | The coverage map overstates eager coverage because the broader history driver is on-demand. | Confirmed. | `fixed-now`; the coverage map now identifies the bounded eager owner and open eager histories. | Prevents generated on-demand histories from being cited as eager evidence. | +| R6 | The old matrix is pinned cases and misses truncate and error paths. | Confirmed as a coverage criticism, not a demand to convert it to random generation. | `fixed-now`; bounded enumeration grew from 10 to 26 cells with truncate and recoverable-error cuts. | Same-turn schedules and additional owners remain named limits. | +| R7 | The eager physical-acquisition counter is vacuous because eager mode bypasses the adapter loader. | Partially confirmed. The adapter counter cannot detect the wrong eager replay attempt, so it was insufficient calibration by itself. It still correctly observes zero accepted physical acquisitions; counting `_sync.loadSubset` would instead count an acquisition attempt or replay start. The added settlement and readiness assertions kill the loader-driven early-fulfillment design. | `fixed-now` for checker sensitivity through the new loading checkpoints; the proposed metric replacement is rejected. | The record preserves both the valid sensitivity criticism and the acquisition-attempt versus physical-acquisition distinction. | +| R8 | The Query Collection regression relies on one microtask and can leak fake timers when setup throws. | Confirmed. | `fixed-now`; semantic `waitFor` checks replace the single microtask, and an outer `finally` always restores timers. | The receiving witness now fails on behavior rather than incidental microtask count. | +| E1 | Nothing in the original review was confirmed by execution. | Correct for the review at head `5f2210ab`; its Vitest attempt lacked installed dependencies. | `already-fixed` as an evidence gap in this worktree by the RED calibration and GREEN runs above. | Code-reading claims remain labeled separately from executed evidence. | + +Ledger totals: five `fixed-now` findings or durable gaps (R1, R5, R6, R7, and +R8), two refuted overclaims with retained coverage value (R3 and R4), one +`accepted-design` scope limit (R2), and one `already-fixed` evidence caveat +(E1). Accounting is complete for the raw review. The accepted bounded +implementation and oracle repair are complete; the coverage map owns the +remaining eager histories named above. + +The review supplied a valuable truncate counterexample and correctly challenged +the coverage map, checker sensitivity, and test harness. Its proposed universal +readiness-listener rule conflated public preload readiness with internal +direct-subscription settlement, while its proposed counter replacement +conflated attempts with accepted work. The reviewer showed strong adversarial +instincts but needed better contract-boundary discipline before its complete +proposed fix could be accepted. +The hire recommendation is no for unsupervised oracle ownership at this stage; +the reviewer would still add value in an adversarial code-review role with a +contract-focused reviewer reconciling the findings. diff --git a/docs/contributing/oracle-reviews/issue-2169-optimistic-window.md b/docs/contributing/oracle-reviews/issue-2169-optimistic-window.md new file mode 100644 index 0000000000..853fb88feb --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2169-optimistic-window.md @@ -0,0 +1,147 @@ +# Optimistic ordered-window departure review (#2169, related #2148) + +## Contract and repair location + +The reported #2169 window kept `[3, 2]` while an optimistic membership-removing +update persisted. The issue's maintainer comment accepts an immediate short +window as the way to preserve optimistic visibility while a provider refill is +pending. #2148 shows a related joined/anti-joined path: the departure is +visible, but acquisition does not make useful refill progress. These are two +obligations, not one expected final row array. + +The accepted law is in +`packages/db/src/query/live/ARCHITECTURE.md#atomic-window-publication`. +An optimistic overlay is not an authoritative loss of provider coverage. +Collections and Effects publish its membership departure synchronously. They +recompute over installed rows, even if that produces a short window. If the +provider has a useful missing row, bounded acquisition starts while the +mutation persists. A provider write queued behind that mutation remains +private until its sync transaction applies; publication can precede resolution +of the applied-receipt promise. Rollback removes this overlay and recomputes +from the current base and any remaining overlays. The prior window is restored +only in an otherwise isolated history. + +The repair is in the ordered source loader. A visible delete or order change +normally invalidates the authoritative source prefix and starts a gated repair. +That classification was also applied to optimistic overlay departures. The +loader now consults the source Collection's active overlay for the changed key: +it still sends the change into the graph and asks for needed finite refill, +but does not claim authoritative source-order loss. Existing initial-load, +window-move, and authoritative-repair barriers remain in force. + +## Executable evidence + +The primary owner is +`packages/db/tests/query/ordered-work-oracle.property.test.ts`. Its controlled +provider has a separate six-row ledger. An independent reference recomputes +the public window from explicitly installed IDs and one overlay, then sorts +and slices it. Rows below the visible boundary distinguish bounded refill from +full-source overfetch. The 96-cell core grammar crosses source, joined, and +anti-joined filters; update and delete; Collection and Effect; acknowledgement +and rollback; one- and two-row windows; and indexed and unindexed loading. +Two indexed controls install the replacement independently of the query shape. +A non-prefix control installs rows 3 and 1 while row 2 is absent: deleting 3 +must show `[1]` immediately, then acquire 2 and show `[2]`. + +Eight histories settle acknowledgement or rollback before the held provider +answer, for source and joined paths and both consumers. Eight more mutate the +joined-side marker rather than the root, for joined and anti-joined paths, +both outcomes, and both consumers. The provider answer arrives after marker +settlement in those histories. The driver records the immediate public rows +and callbacks before yielding, provider acquisition while persistence is held, +the queued-answer and transaction-settlement cuts, the late-answer cut, and +two subsequent task turns. It checks both public rows and publication +sequences. A separate installed-extent model checks ascending heads, +descending offset windows, a tied head, and an exhausted window across both +outcomes. The tied case removes whichever equal-ranked key the initial public +window chose, avoiding an unsupported tie-break promise. + +Four controlled histories use ascending limit-one on-demand joined and +anti-joined plans, the published #2148 shape. A rank-only acknowledgement is +an adjacent control, while a combined rank-and-membership departure checks +rollback. An acknowledged joined-side marker departure is followed by marker +restoration: source row 3 must return with a Collection or Effect publication. +This keeps marker eligibility distinct from source installation in the model. + +The provider recorder keeps submitted IDs, requested order direction and +limit, real applied-receipt state, and every request's predicate, rank scope, +identity, phase, and progress. A required refill must submit the useful row +through a finite ordered request no larger than `width + 2` in this fixture. +Every pending indexed acquisition must have that bound or be a one-rank tie +probe. A source-filter request must reject an ineligible peer at a relevant +rank. A repeated identical request in the same settlement phase without a new +row fails. Distinct sequential tie or refill requests are allowed. The +six-row ledger makes full-source work distinguishable from a finite bound. +An emergency eight-request ceiling catches runaway work but is not a +fixed-point proof. Hostile checker controls reject no refill progress, +delayed Collection publication, stale post-settlement publication, +full-source overfetch, a missing source predicate, broad work after a valid +refill, and acquisition deferred until settlement. Named finite waits let a +stalled held event enter cleanup with its checkpoint intact. + +The original loader behavior was reintroduced locally without changing the +oracle. The width-two indexed source-delete/Collection acknowledgement case +then failed at the intended cut: immediate `[3, 2]` instead of `[2]`, no +immediate callback, and a delayed settlement publication. Restoring the +loader change passes the focused histories. The exact #2169 mutation-handler +`utils.refetch()` path also runs through a real Query DB adapter and +QueryClient: only rows 3 and 2 are initially installed, so the immediate +window is `[2]`, not `[2,1]`; after the handler changes the provider row and +refetches, row 1 applies and the window becomes `[2,1]`. Temporarily restoring +the old loader classification fails this receiver at the immediate checkpoint +with `[3,2]`. The receiver also checks that the held callback snapshots remain +`[2]` and that the settled `[2,1]` window reaches subscribers. A temporary +observer mutant that discarded the final callback failed at that assertion. +Its request checker evaluates an unordered probe's uncapped predicate against +the full ledger and an added equal-rank peer; an orderless `limit: 1` cannot +masquerade as a rank-tie probe. With `TANSTACK_DB_ORACLE_SEED=1`, the complete ordered-work +owner passes 267 tests with no type errors; the Query DB receiving test also +passes with runtime and type checks. An unrelated generated history at seed `-1704903709` +fails both with and without this loader change; it is not claimed as a +regression or silently counted as green. + +Three fresh independent oracle-review attacks identified the missing +joined-side, late-answer, explicit-installed-set, and bounded-request +distinctions; those are now executable. They also caught stale claims about +receipt timing and request-count stability, which this review and coverage +map correct. + +## Boundaries and receiving work + +#2148 does not publish its exact historical provider ledger or release +schedule. The controlled ascending limit-one joined/anti-joined histories +reach its stated shape, but the ordered-work owner still owns any recoverable +historical replay. `invalidateLoadedSubsets()` is introduced by open PR #2154, +not this revision; its Query DB owner needs a during-hold immediate-window +witness when that API is integrated. The installed +ascending, offset, tie, and exhausted cases establish immediate local +publication, not all provider-refill schedules for those windows. Provider +rejection, overlapping optimistic transactions, rank-only rollback and other +rank-move histories, real framework +render timing, and exact callback payloads remain separate obligations. A +provider-rejection continuation needs a Collection-versus-Effect contract +decision before adding expectations. The two post-settlement task turns are +bounded observations, not a universal no-retry proof. + +## Oracle-guide accounting + +This audit covers the change from published base `c4071fdba` through the +working-tree evidence above. Recheck the final commit after preparation; a +passing seeded run is not a claim about every generated history. + +| Requirement | Outcome for this change | +| --- | --- | +| ORC-001 | The architecture's optimistic departure and settlement law is stated above; rank-only rollback, provider rejection, and the #2154 signal remain outside its accepted short-window exception. | +| ORC-002 | The controlled expectation filters, orders, and slices an installed-ID ledger. The Query DB receiver separately filters and sorts a remote row ledger; neither expected window imports loader classifications. | +| ORC-003 | The owner and receiver each state the law, model, legal fixture, real production path, public cuts, and comparison beside their code. | +| ORC-004 | Not triggered by the new fixed Cartesian matrix and pinned receiving test. Existing generated campaigns in the ordered-work owner are unchanged. | +| ORC-005 | The controlled owner reaches Collection and Effect rows/callbacks before yielding, at acquisition and settlement, and after a late answer. The receiver reaches the real QueryClient mutation-handler refetch and checks the immediate short window and the held and final callback snapshots. | +| ORC-006 | Restoring the old loader classification fails controlled source and ascending joined/anti-joined cases, and the real Query DB receiver, at immediate publication. Hostile checker observations reject no progress, delayed publication/acquisition, stale output, predicate omission, and broad requests. The receiver rejects an orderless page cap and a temporary missing-final-callback mutant. | +| ORC-007 | Not triggered by the new fixed matrix or receiver. The existing fixed/random campaigns in this file remain separate; the full owner passed at seed 1, while one unseeded-generated failure is known to reproduce without this change. | +| ORC-008 | Source installation and marker eligibility are distinct model facts. Restoring a marker after acknowledgement distinguishes an eligible hidden source row from a physically removed one. | +| ORC-009 | `installedIds` means applied source rows, not remote provider extent. `answerQueued` precedes application; a pending provider answer is not a public row. The receiver's remote ledger is provider authority. | +| ORC-010 | Held-event waits have named finite bounds and enter `withHistoryCleanup`; cleanup releases provider and persistence gates. A runtime test timeout outside those named waits is not evidence of a refinement mismatch. | +| ORC-011 | The real Query DB receiver is a second provider formulation for #2169's controlled premise. It does not stand in for the unavailable #2154 invalidation path. | +| ORC-012 | This table records applicable outcomes and non-applicable triggers; final-head verification remains required before a closure verdict. | +| ORC-013 | Missing, installed, and non-prefix extent; leading rejected rows; ascending joined and anti-joined windows; and late answers distinguish useful repair from no refill, broad fetch, or delayed publication at the named cuts. | +| ORC-014 | The controlled provider's queued answer and short-window premise reach a real Query DB mutation-handler refetch witness. The separate `invalidateLoadedSubsets()` handoff remains open with the Query DB owner after #2154 integration. | diff --git a/docs/contributing/oracle-reviews/issue-2190-order-by-index-diagnostics.md b/docs/contributing/oracle-reviews/issue-2190-order-by-index-diagnostics.md new file mode 100644 index 0000000000..fd210eaad0 --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2190-order-by-index-diagnostics.md @@ -0,0 +1,171 @@ +# Issue 2190: bounded `orderBy` index diagnostics + +Date: 2026-10-16 + +Baseline: `2c83fdc53bd762fa8b8d5e5a8b82e092710618be` (`origin/main`) + +Review scope: the repair recorded by this document. PR 2193 was explicitly +excluded from the investigation. This record reviews the issue report against +the baseline and the executable repair in the same commit as this file. + +## Law + +When a bounded `orderBy` cannot use an installed index for its ordered source +prefix, its development warning must describe the remaining cause: + +- no index matches the ordered field; +- field indexes exist, but none matches the resolved comparison options; or +- an index matches both, but cannot support ordered lazy loading. + +If any candidate matches the field and comparison options and supports ordered +lazy loading, registration order must not hide it. The compiler must use that +candidate and issue a bounded ordered acquisition rather than a full-source +fallback. + +Custom string order is a separate case: even a compatible local index cannot +make the provider paginate by an arbitrary callback. On-demand sources require +a filtered full-source acquisition. A missing ordinary index, by contrast, +can still start with a bounded ordered prefix and local scan or sort. + +The authority is the existing developer warning's stated remediation and the +bounded ordered-acquisition contract. A diagnostic that recommends work the +Collection already has is false. Index registration order is physical planning +and cannot change eligibility. + +## Experiment and inference + +`packages/db/tests/index-suggestion-oracle.test.ts` now contains an independent +finite classifier over field identity, null placement, ordered-read capability, +and candidate order. The production driver compiles and preloads a public +live-query Collection and records the development warning. + +On the baseline, four of the original eleven diagnostic cells failed: + +- a null-placement mismatch emitted the generic missing-index warning; +- matching but order-ineligible indexes emitted the same warning in two cells; +- an ineligible candidate registered before an eligible candidate still caused + fallback and a warning. + +These were assertion mismatches at the intended diagnostic checkpoint, not +setup failures or timeouts. The first three cells establish the issue report's +missing classification. The candidate-order cell exposes a sibling selection +bug: `findIndexForField` returned the first field/options match before the +compiler checked ordered-read capability. + +`packages/db/tests/query/custom-index-order-selection-oracle.test.ts` supplies +the production-path witness. An on-demand source registers an order-ineligible +custom index before a normal eligible index. After preload, the query must show +the independently sorted two-row prefix, issue a bounded ordered request, and +issue no unbounded full-source request. A boundary probe may accompany that +request. Spies on the two readers calibrate which compiler path produced the +public requests; reader identity is not itself a public product observation. + +A hostile first-match mutant that ignores the eligibility predicate failed that +oracle because the eligible index's `takeFromStart` was never called. The same +mutant also failed two diagnostic cells by silently treating an ineligible +index as usable. These were assertion failures at the intended reader and +warning observations. + +An independent prep review then found that the first formatter called +`JSON.stringify` on user-owned comparison options. Both a missing-index and a +mismatch history threw on cyclic `localeOptions`, although `Intl` accepts that +object. A custom comparator was rendered as the unusable string `"[function]"`. +All three added oracle cells were RED on that formatter. The repair describes +only safe scalar fields and tells locale and custom callers to reuse the same +locale configuration or comparator function reference; it does not traverse +those values. + +The initial repaired tree passed 22 index-suggestion cases and five custom-index +selection cases. The production-message census also accepted the new +development-only literals. + +## Finding ledger + +| ID | Source claim | Baseline evidence | Disposition | Durable destination | +| --- | --- | --- | --- | --- | +| I-01 | A same-field index with different `compareOptions` is reported as missing. | The null-placement mismatch cell emitted the missing-field warning. | `fixed-now` | The diagnostic decision table distinguishes field presence from comparison compatibility. | +| I-02 | Full-source fallback is correct when no installed index has compatible ordering. | The incompatible index is rejected and the live-query rows remain correct through fallback. | `fixed-now` | The mismatch and order-ineligible cells preserve both fallback and its reason. | +| I-03 | Suggesting the same `createIndex((row) => row.name)` call is misleading. | The baseline warning repeated the already-satisfied field-only remedy. | `fixed-now` | The mismatch warning shows a `BasicIndex` call with the resolved scalar comparison options and directs callers to carry over locale settings. | +| I-04 | The warning should identify a `compareOptions` mismatch. | The baseline used one generic missing-index branch after selection failed. | `fixed-now` | The finite classifier and public warning assertion preserve this distinction. | +| I-05 | The warning should show the options accepted by `createIndex`. | The baseline omitted the query's resolved options and the nested `options.compareOptions` shape. | `fixed-now` | A later PR #2196 follow-up restored the nested `options.compareOptions` shape and a `BasicIndex` example. The diagnostic oracle now asserts the shape without serializing arbitrary locale options. | + +| S-01 | A first matching but order-ineligible index can hide a later eligible index. | The baseline warned and fell back when the custom index was registered first. | `fixed-now` | Both candidate orders are classified; the production-path witness requires bounded demand without an unbounded full-source request and calibrates the eligible reader. | +| R-01 | Formatting a development warning can throw on valid cyclic `localeOptions`. | Query construction threw in both missing-index and mismatch cells. | `fixed-now` | The diagnostic oracle crosses both causes without traversing the options object. | +| R-02 | Rendering a custom comparator as `"[function]"` produces invalid remediation and loses required reference identity. | The warning embedded the string in an executable-looking `createIndex` example. | `fixed-now` | The custom-collation cell requires the exact reference-identity instruction and rejects the old placeholder. | + +## PR #2196 follow-up + +A later review found that the repaired message still described ordinary +unindexed prefixes as full-source loads and offered a local index as a cure for +custom provider ordering. The revised diagnostic distinguishes custom +collation, virtual fields, and ordinary index failures. Public on-demand +acquisitions show one full-source request for a custom comparator even with a +matching local index, and a bounded ordered request without an index. The +matching-index, virtual-field, and request-shape checks were RED on the prior +message and GREEN on the revised one. Direction is omitted from the mismatch +advice because a compatible index can be read in reverse. + +The neighboring selection checks were also RED before repair: a direct +`optimizedOnly` snapshot ignored the second eligible index, and eager +auto-indexing did not create one when an ineligible manual index existed. The +same public oracle now checks both at initial read. It does not claim provider +paging from an incomplete source or later index changes. +Using full current eligibility as the auto-index creation predicate was a +plausible narrow fix, but it created extra identical indexes for mixed value +domains. The mixed-index oracle rejected it at the index-count checkpoint. +The final admission check distinguishes a custom index's missing static range +capability from a structurally sound index's changing value domain. + +## Oracle review + +- **ORC-001:** The law and limits are stated above and beside both executable + owners. The comparison-options diagnostic grammar currently distinguishes + null placement; `BaseIndex.matchesCompareOptions` has separate direct coverage + for locale, locale options, string-sort mode, direction, and custom comparator + identity. +- **ORC-002:** The diagnostic model uses only declared candidate facts. It does + not call the production field, comparison, or eligibility classifiers. The + ordered-result model sorts a copied row array by numeric rank. +- **ORC-003:** Both owners state their laws, finite histories, production paths, + observation cuts, and limits beside the executable checks. +- **ORC-004:** Not applicable because the diagnostic grammar is bounded + enumeration, not a generated-history claim. The matrix still reconstructs + the report, isolates each classifier axis, crosses both orders for mixed + eligibility, and excludes an unrelated-field index from field presence. +- **ORC-005:** Public live-query construction and preload reach the warning and + acquisition paths. The recorders retain every warning and require the exact + bounded request shape. Reader spies are path calibration only. +- **ORC-006:** The baseline is the rejected generic-diagnostic design. The + first-match mutant is the rejected one-trace selection repair. +- **ORC-007:** Not applicable; neither new check is an important generated + property. +- **ORC-008:** Not applicable; both models are stateless finite judgments. +- **ORC-009:** The tests use Collection, source Collection, acquisition, and + checkpoint as defined by the project glossary. Candidate is a test-only index + description, not a production lifecycle concept. +- **ORC-010:** Both owners preserve a primary assertion failure separately from + cleanup failures and restore spies. +- **ORC-011:** Not applicable. No plausible shared semantic classifier was + identified between the finite model and production. The production-path + witness supplies integration evidence, not an independent second semantics. +- **ORC-012:** This record supplies the exact RED, hostile-mutant, GREEN, and + scope evidence for the oracle repair. +- **ORC-013:** Missing-field and mismatch cells distinguish field presence; + matching-ineligible and matching-eligible cells distinguish capability; the + mixed-order cells reject first-candidate policy. Cyclic options reject both + unconditional and mismatch-only JSON serialization, while the custom cell + rejects remediation that loses comparator identity. +- **ORC-014:** The controlled on-demand adapter proves the compiler-to-adapter + request and reader choice only. It does not claim real-provider paging. + +## Remaining limits + +The diagnostic decision table does not enumerate every comparison-option +dimension, every hostile object behavior, production-build suppression, +repeated warning volume, or every query shape. The formatter does not inspect +user-owned locale-options or comparator values, so cyclic objects and function +identity distinguish the unsafe designs without claiming every object shape. +The existing comparison-option tests own the lower-level matching semantics, +and the production-message oracle owns development-text erasure. +Provider paging from an incomplete source and later source changes remain with +the ordered-acquisition owners. diff --git a/docs/contributing/oracle-reviews/issue-2281-investigation.md b/docs/contributing/oracle-reviews/issue-2281-investigation.md new file mode 100644 index 0000000000..1b0bc305ac --- /dev/null +++ b/docs/contributing/oracle-reviews/issue-2281-investigation.md @@ -0,0 +1,239 @@ +# Electric/SQLite source-progress recovery: issue #2281 + +This record summarizes the repair in [PR #2298](https://github.com/TanStack/db/pull/2298), +verified through implementation commit `0ec58057de52162da0eb4598616c2fc2eed2129b`. +The original investigation used production commit `82c5f999b32c9db827dd74e3f172604dfda28013`. +The [full investigation chronology](https://github.com/TanStack/db/blob/0ec58057de52162da0eb4598616c2fc2eed2129b/docs/contributing/oracle-reviews/issue-2281-investigation.md) +retains superseded results, intermediate manifests, and review corrections. +This summary changes no production code, model, history grammar, comparison, +exclusion, or run budget. + +## Law and failure + +After successful source recovery and complete delivery, durable rows and every +open eager Collection must equal the source head. Authority: Law 2 in the +[Browser multi-tab owner](../../../packages/browser-db-sqlite-persistence/tests/multi-tab-electric-oracle.test.ts), +the [coverage map](../oracle-coverage.md), and [#2209](https://github.com/TanStack/db/issues/2209). +Temporary arrival-order regressions remain allowed. The +[Electric recovery contract](../../collections/electric-collection.md#cleanup-and-resume-safety) +requires a fresh baseline after reset. Under the maintainer decision on #2265, +a reset-abandoned commit fails its source; successful catch-up requires restart. + +The row-loss and resurrection traces share this mechanism: + +1. Tab A commits two source changes to one key, reaching position 2. +2. Lagging tab B commits only the first change, restoring an older row state. +3. A advances shared resume metadata to position 2 with an empty batch or a + batch affecting another key. The affected row stays stale. +4. Both tabs restart beyond the missing corrective change, so catch-up cannot + repair the row. + +Atomic rows and metadata within each commit do not establish that commits from +separate streams describe one source history. Keeping the maximum offset still +permits stale row writes. Suppressing empty commits misses disjoint-key batches. +Physical key integrity also cannot certify source progress. + +The two reset traces exposed a separate oracle checkpoint defect. Final delivery +could first fail a reset-abandoned transaction after the initial restart sweep. +The driver now compares that failed state, restarts the newly failed source, +and completes delivery before judging convergence. This preserves the failure +contract; it does not weaken the source-head comparison. + +All four reported traces pass through the shared model, driver, and comparison: + +| Trace | Seed | Shrink path | +| --- | --- | --- | +| Original reset/deploy | `-1115798760` | `1:4:3:3:3:3:3:3:3:3:3` | +| Missing row | `-1660880085` | `55:0:1:4:4:4:1:1:1:1:3:4:4:4:4` | +| Resurrected row | `-699129038` | `78:2:0:3:9:12:12:13:12:13:13:13:13:14:14:14` | +| Reset without deploy | `-1371495859` | `75:0:0:0:0:0:0:1:1:0:0` | + +## Repair + +Each eager Electric commit supplies an opaque source progress token and its +expected predecessor through an optional persistence capability. The token +contains shape identity, handle, and offset. SQLite compares the predecessor +under its existing writer lock, atomically with rows and resume metadata. +A discontinuity makes source progress evidence incompatible until an +authoritative full replacement. A repeated complete endpoint preserves existing +evidence: it neither invents nor clears a gap. Provider handles need no ordering. + +Startup uses existing full-snapshot recovery when progress is missing, foreign, +or incompatible. This recovery overrides explicit caller positions because an +offset cannot supply missing baseline rows. An interrupted eager replacement's +durable reset requires the same recovery on the next start. Both recovery +admission and effective source position honor that obligation. Other sync modes +retain their explicit-position behavior. + +Source progress evidence has its own SQLite column and remains separate from +physical key integrity. A cached-row counterexample rejected combining them: +cached rows must remain visible during recovery. The additive migration also +covers existing databases. Arrival-order writes and their receipts remain. +Custom adapters advertise the optional capability only with an atomic check. + +## Evidence + +Each behavioral extension ran against unchanged production before its repair. +Setup failures and surviving mutants are excluded from the RED evidence. + +| Experiment | RED before its corresponding repair | Repaired GREEN / calibration | +| --- | --- | --- | +| Source continuity | Browser seed `2281`, path `0:0:0:0`, loses row 1 on `82c5f999b`; SQLite accepts missing gap evidence. Real receiver: four wrong-row failures and four passing draining controls. | Full SQLite owner: 61 passes. Browser restart and tab reopen converge. Ignoring durable gaps fails SQLite, Browser, and real disjoint-advancement comparisons. | +| Startup provenance | Missing/foreign tokens cannot justify the cached baseline. | Bypassing validation fails Browser and both real legacy-token cells. Rejecting duplicate endpoints separately fails SQLite at seed `2281`, path `0:7:1:2:2:2:2:2:2`. | +| Explicit caller positions | Extended Node campaigns fail; eight real numeric/`now` cells fail while initial-offset and handle-only controls pass. | Requesting recovery while preserving the raw caller position still fails the shared final comparison. Correcting both decisions passes. | +| Interrupted replacement | Paired Node campaigns fail at seed `2281`, path `1:0:0`, and random seed `-636405297`, path `14:0:0:0:0`. Four real numeric/`now` cells fail; six none/initial/handle controls pass. | All 30 real receiving cells pass. Keeping reset admission but removing its effective-position override fails the same Node replay; exact restoration passes. | +| Final reset checkpoint | Original production and driver reproduce both reported reset mismatches. | Corrected checkpoint passes on original production. Removing only the final restart/delivery block fails both reset replays. | +| Stop/peer coverage | CI reached SDK-give-up followed by peer commit zero times. | Constructed stop blocks establish every required stop boundary. Removing SDK-give-up abandonment fails a public-row assertion at seed `952051961`, path `1:0:0:0:0:0:0:0:1:1:0:0`. | + +Final implementation verification: complete Browser owner, **312 passes and two +tracked expected failures**; all **30 real receiving cells** pass. Electric +checks report 336 and 97 passes across two commands, without type errors. +Browser TypeScript, capability validation, Electron IPC, replay selection, +`pnpm lint:changed`, and whitespace checks pass. Documentation checking reports +zero new failures across 806 examples. Every temporary mutant was restored; +the final Electric source SHA-256 is +`46a417f2d65ccc21adcc72f56d80388d6f2f9fc32a172ab6e3fa1e4702508047`. + +### Independent judgments and exercised paths + +| Owner / driver | Independent judgment | Production path and observation cut | +| --- | --- | --- | +| Browser multi-tab oracle | Server snapshot log, shared source-head comparison | Real Collections, Browser coordinator with Node BroadcastChannel/Web Locks, native SQLite, controlled Electric stream; durable/public rows and health after each step and complete recovery/delivery. | +| SQLite resume-snapshot oracle | Path of opaque predecessor edges since the last authoritative replacement | Runtime capability, SQLite writer transaction, migration and resume snapshot; compatibility evidence after every durable transaction. | +| Real receiver for the Browser law | PostgreSQL source head, same shared comparison | PostgreSQL → Electric HTTP → installed SDK → Chromium Collections → OPFS SQLite; actual earlier responses held before delivery and reopened rows compared after catch-up. | + +The real matrix retains ten original/legacy, ten explicit-position, and ten +interrupted-recovery cells. Every cell requires held earlier response, stale +commit, later advancement, and reopened comparison. Interrupted cells observe +an actual durable reset before stopping replacement. No fabricated SDK messages +or offsets enter this driver. These are finite receiving witnesses. + +The source-prefix property uses 80 fixed-seed inputs and 80 random inputs. Each +input runs uninterrupted and interrupted neighbors: 160 histories per campaign. +Axes include membership, 1–3 toggle pairs, swapped source roles, empty/disjoint +advancement, draining, restart/reopen, token provenance, and five caller forms. +Numeric noninitial offsets without handles are SDK-invalid and excluded. +SQLite uses 80/80 inputs, 3–16 actions plus replacement, with stale/matching/empty +commits, duplicate endpoints, replacements, and old-schema migration. + +Final measured source-prefix and SQLite reaches: + +| Declared state | Fixed / random histories | +| --- | ---: | +| Present row / empty advancement | 28 / 26 | +| Present row / disjoint advancement | 22 / 14 | +| Absent row / empty advancement | 12 / 34 | +| Absent row / disjoint advancement | 14 / 18 | +| Drained before recovery | 84 / 68 | +| Missing progress token | 52 / 64 | +| Foreign progress token | 42 / 56 | +| Divergent same-Collection restart | 50 / 52 | +| Divergent tab reopen | 26 / 40 | +| Divergent explicit numeric resume | 20 / 12 | +| Divergent explicit `now` | 14 / 16 | +| Explicit initial offset | 34 / 42 | +| Explicit handle | 42 / 32 | +| Interrupted reset before numeric resume | 10 / 6 | +| Interrupted reset before `now` | 7 / 8 | +| SQLite discontinuity | 61 / 53 | +| SQLite matching commit after discontinuity | 37 / 42 | +| SQLite replacement after discontinuity | 44 / 36 | +| SQLite duplicate endpoint | 64 / 61 | +| SQLite legacy migration | 34 / 39 | + +The broader lifecycle property retains 80 accepted inputs per campaign. Four +constructed stop blocks precede 8–24 arbitrary actions, for 32–48 actions. +All four stop kinds cross a distinct peer commit and recovery. Final fixed/random +stop-boundary reaches are 81/80, including one fixed history later discarded +under the existing #2267 gap. Final-catch-up reset recovery reaches 4/2. +All other declared reaches pass. No comparison, exclusion, minimum reach, or +run budget was relaxed. + +### Review corrections + +Four independent reviewers found an unreachable focused restart branch and +replay registrations that ran unrelated campaigns. Actual-path instrumentation +first measured restart 0/reopen 38 and failed. Adding a stop made restart +reachable; gap and provenance mutants then failed that path. The original +row-loss trace already supplied a pre-production same-Collection restart RED; +the focused campaign's restart repair occurred later. Replay probes in both +Browser directions, unrelated-property selection, and SQLite selection verify +that only the requested campaign runs. + +Later CodeRabbit reviews identified the explicit-position and interrupted-reset +gaps above. The latest completed external review covers `124ce667c`; automatic +review is paused. Subsequent eager reset semantics have independent local +source review plus the generated, real receiving, and mutant evidence above. +The lossless task-local audit retains 30 items: 12 fixed, five refuted, one +accepted design, and 12 duplicates. No known in-scope source-progress finding +remains open. CodeRabbit supplied useful defect and maintenance findings; +its one-clause recovery suggestion was incomplete, as the mutant demonstrates. + +The rubric evidence is concentrated in the contract/model/path distinctions, +measured legal histories, calibrated comparisons, isolated replay, and retained +failure diagnostics. Cleanup attempts every release and preserves the primary +violation alongside cleanup failures; injected combined failures verify this. +Fixed examples use the shared oracle without separate expected answers. + +## Cost and limits + +The production delta is 153 net source lines across five files. Against +merge-base `672684846`, independently compressed package bundles grow by +DB +37, SQLite core +368, and Electric +168 gzip bytes: **573 bytes total**. +The standard application fixture adds 45 bytes but does not use Electric or +SQLite, so it does not measure their application cost. The repair adds one +metadata read per qualifying incremental source-progress transaction, stored +continuity evidence, and token processing. Runtime overhead was not benchmarked. +Incompatible progress can require a full source snapshot. + +The evidence covers bounded, same-version eager sources sharing SQLite. It +does not establish arbitrary schedules, mixed versions, monotonic intermediate +writes, on-demand subsets, optimism, native/Electron receiving, individual +receipt fulfillment, or clean teardown. Explicit changed-shape/malformed/tagged +resumes, arbitrary cursor selection over certified rows, other modes' reset +policy, changing caller forms within one history, and real foreign-token +receiving remain outside the claim. The coverage map names remaining witnesses. + +The permanently closed lagging-source history in #2209 remains a tracked +expected failure. The #2267 partial-update guard preserves the safety contract; +its no-error composition expectation remains a separate contract question. +The first #2281 comment's seed-row-metadata defect, #2272 metadata receiving, +and the pilot's replay/reset scheduling and teardown gaps retain separate owners. +The independent cluster report confirmed this repair's mechanism and led to a +separate reach for reset recovery first discovered during final catch-up. +It did not combine those other boundaries with source-progress recovery. + +CI on implementation `0ec58057d` passed 25 checks after one failed-job rerun. +Its original observer cleanup/restart defect also reproduces on clean current +main `bea84cd45`; the aggregate ordering reach gap is tracked in #2289. The +maintainer chose to keep #2298 focused. Observer evidence was sent to the +“Survey open issues and PRs” chat; both issues remain open. A passing retry +closes neither. CodeRabbit's skipped check reflects its paused review. + +## Reproduction + +Run from the repository root with two workers: + +```sh +pnpm --filter @tanstack/browser-db-sqlite-persistence exec vitest --run \ + tests/multi-tab-electric-oracle.test.ts --maxWorkers=2 --coverage.enabled=false +pnpm --filter @tanstack/db-sqlite-persistence-core exec vitest --run \ + tests/sqlite-resume-snapshot-oracle.test.ts --maxWorkers=2 --coverage.enabled=false +pnpm --filter @tanstack/browser-db-sqlite-persistence exec playwright test \ + --config playwright.opfs-electric.config.ts \ + electric-source-prefix-oracle.opfs.spec.ts electric-resume-two-tab.opfs.spec.ts \ + --workers=2 +``` + +For a reported trace, set `TANSTACK_DB_ORACLE_PROPERTY=browser.multi-tab-reserved-history` +and its seed/path from the first table; select `-t 'random or replayed'`. +For the final source-prefix replay, use property `browser.electric-source-prefix`, +seed `2281`, and path `1:0:0` with the same selector. Fixed campaigns stand aside +during path replay. Normal campaigns require no replay variables. + +Final local runs used `pnpm_config_verify_deps_before_run=false` after upstream +release-specifier changes triggered automatic reinstall. Installed external +dependencies were unchanged. No override or lockfile workaround accompanies +this PR. Raw logs and the complete review ledger remain task-local under +`notes/issue-2281/`; essential evidence is summarized here and preserved in the +linked historical record. diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/LoaderAuthority.tla b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/LoaderAuthority.tla new file mode 100644 index 0000000000..d0b6e1c4ee --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/LoaderAuthority.tla @@ -0,0 +1,256 @@ +---- MODULE LoaderAuthority ---- +\* Run 2 of the design grammar for OrderedSourceLoader (#2194 head 943d586f2). +\* State: the F1 grammar (Work slot x Debt lattice), own failure, a ledger of +\* requests with chain membership, the scheduler's pending pointer, and one +\* staged repair step. Weak names one rule from a fixed bug to re-enable. +EXTENDS Integers, FiniteSets +CONSTANTS MaxEpoch, Ids, Weak +\* Weak \subseteq {"lateStamp", "pendingBlocks", "dropStaged", "stepReleases", +\* "epochRetire", "noLeaseCollect", "ownSuperseded"} +VARIABLES epoch, debt, work, own, req, pending, staged, settledOnce, fault, coverage +vars == <> + +None == 0 +Unused == [k |-> "none", ep |-> 0, chain |-> -1, st |-> "unused", sup |-> FALSE, legit |-> FALSE, ownFail |-> FALSE] +Init == /\ epoch = 0 /\ debt = "none" /\ work = [k |-> "idle", ep |-> 0] /\ own = FALSE + /\ req = [i \in Ids |-> Unused] /\ pending = None + /\ staged = [k |-> "none", chain |-> -1] /\ settledOnce = FALSE + /\ fault = TRUE \* one release callback may throw + /\ coverage = [k |-> "nothing", chain |-> -1] + +Inflight == {i \in Ids : req[i].st = "inflight"} +Free == {i \in Ids : req[i].st = "unused"} +Quiet == Inflight = {} /\ staged.k = "none" +IsAuth(r) == r.k \in {"prefix", "step", "full"} +\* Authoritative work supersedes in-flight finite requests once issued. +Supersede(r) == [j \in Ids |-> IF r[j].st = "inflight" /\ r[j].k = "finite" + THEN [r[j] EXCEPT !.sup = TRUE] ELSE r[j]] +New(k, ep, ch) == [k |-> k, ep |-> ep, chain |-> ch, st |-> "inflight", sup |-> FALSE, + legit |-> FALSE, ownFail |-> FALSE] +\* A chain step's ledger epoch: its chain's, or (reverted rule) its issue epoch. +StepEp(ch) == IF "epochRetire" \in Weak THEN epoch ELSE ch +Stale(r) == ~IsAuth(r) /\ r.ep # epoch +\* Release every settled finite entry outside chain ch (or older than ch's +\* epoch, under the reverted epoch rule). +RetireOutside(r, ch) == + [j \in Ids |-> IF r[j].st = "settled" /\ r[j].k # "full" + /\ (IF "epochRetire" \in Weak THEN r[j].ep < ch ELSE r[j].chain # ch) + THEN [r[j] EXCEPT !.st = "released"] ELSE r[j]] +RetireAllFinite(r) == + [j \in Ids |-> IF (r[j].st = "settled" /\ r[j].k # "full") + \/ (r[j].st = "failed" /\ r[j].sup /\ "noLeaseCollect" \notin Weak) + THEN [r[j] EXCEPT !.st = "released"] ELSE r[j]] + +StartAuthority(r) == + \* From an idle slot with debt: a bounded chain or a full-source read. + \E k \in Free : + /\ IF debt = "bounded" + THEN /\ req' = [Supersede(r) EXCEPT ![k] = New("prefix", epoch, epoch)] + /\ work' = [k |-> "chain", ep |-> epoch] + ELSE /\ req' = [Supersede(r) EXCEPT ![k] = New("full", epoch, -1)] + /\ work' = [k |-> "fullHeld", ep |-> epoch] + /\ pending' = IF "pendingBlocks" \in Weak THEN (IF pending = None THEN k ELSE pending) ELSE k + +\* An ordinary finite page; optionally its own delivery invalidates ordering. +IssueFinite(selfInvalidates) == + /\ work.k = "idle" /\ debt = "none" /\ ~own /\ pending = None /\ staged.k = "none" + /\ coverage.k # "full" + /\ (selfInvalidates => epoch < MaxEpoch) + /\ \E k \in Free : + /\ req' = [req EXCEPT ![k] = [New("finite", + IF selfInvalidates /\ "lateStamp" \in Weak THEN epoch + 1 ELSE epoch, -1) + EXCEPT !.legit = TRUE]] + /\ pending' = k + /\ epoch' = IF selfInvalidates THEN epoch + 1 ELSE epoch + /\ debt' = IF selfInvalidates THEN (IF settledOnce THEN "bounded" ELSE "full") ELSE debt + /\ UNCHANGED <> + +Invalidate == + /\ epoch < MaxEpoch /\ coverage.k # "full" + /\ epoch' = epoch + 1 + /\ debt' = IF debt = "none" /\ settledOnce /\ work.k = "idle" THEN "bounded" + ELSE IF debt = "none" THEN "full" ELSE debt + /\ UNCHANGED <> + +\* An ordinary pass: consume a staged step, or start owed authoritative work. +Pass == + /\ ~own + /\ \/ /\ staged.k = "step" + /\ IF pending # None /\ "pendingBlocks" \in Weak + THEN \* the step waits behind a superseded request and is lost + /\ staged' = [k |-> "none", chain |-> -1] + /\ UNCHANGED <> + ELSE \E k \in Free : + /\ req' = [req EXCEPT ![k] = New("step", StepEp(staged.chain), staged.chain)] + /\ pending' = k + /\ staged' = [k |-> "none", chain |-> -1] + /\ UNCHANGED <> + \/ /\ staged.k = "none" /\ work.k = "idle" /\ debt # "none" /\ coverage.k # "full" + /\ StartAuthority(req) + /\ UNCHANGED <> + /\ UNCHANGED <> + +\* A repair prefix that answers at once, as an eager source does: its step is +\* staged until a graph turn resumes it. +SyncPrefix == + /\ ~own /\ staged.k = "none" /\ work.k = "idle" /\ debt = "bounded" + /\ \E k \in Free : + /\ req' = [Supersede(req) EXCEPT ![k] = [New("prefix", epoch, epoch) EXCEPT !.st = "settled"]] + /\ work' = [k |-> "chain", ep |-> epoch] + /\ staged' = [k |-> "step", chain |-> epoch] + /\ pending' = IF "pendingBlocks" \in Weak THEN pending ELSE None + /\ UNCHANGED <> + +Restart(r) == + \* A chain whose epoch moved or whose debt rose to full is replaced. + IF debt = "full" + THEN \E k \in Free : /\ req' = [Supersede(r) EXCEPT ![k] = New("full", epoch, -1)] + /\ work' = [k |-> "fullHeld", ep |-> epoch] /\ pending' = k + ELSE \E k \in Free : /\ req' = [Supersede(r) EXCEPT ![k] = New("prefix", epoch, epoch)] + /\ work' = [k |-> "chain", ep |-> epoch] /\ pending' = k + +SettleOk(i) == + LET r == req[i] + s == [req EXCEPT ![i].st = "settled"] + p == IF pending = i THEN None ELSE pending + IN + /\ r.st = "inflight" + /\ CASE r.k = "finite" -> + /\ IF Stale(r) \/ work.k # "idle" \/ own + THEN \* proves nothing; once a repair finished it is already superseded + \* Full-source coverage retires every settled finite lease. + /\ req' = IF (work.k = "idle" /\ debt = "none" /\ Stale(r)) + \/ work.k = "fullComplete" + THEN [s EXCEPT ![i].st = "released"] ELSE s + /\ UNCHANGED <> + ELSE /\ coverage' = [k |-> "pages", chain |-> coverage.chain] + /\ settledOnce' = TRUE + \* A current page continues with its tie at once. Under debt + \* that tie is not a refinement of current coverage. + /\ \/ req' = s + \/ \E k \in Free : + req' = [s EXCEPT ![k] = [New("finite", epoch, -1) + EXCEPT !.legit = (debt = "none")]] + /\ pending' = p + /\ UNCHANGED <> + [] r.k \in {"prefix", "step"} -> + IF work.k # "chain" \/ work.ep # r.chain + THEN /\ req' = s /\ pending' = p + /\ UNCHANGED <> + ELSE IF work.ep # epoch \/ debt = "full" + THEN /\ Restart(s) + /\ UNCHANGED <> + ELSE IF r.k = "prefix" + THEN \E k \in Free : + /\ req' = [s EXCEPT ![k] = New("step", StepEp(r.chain), r.chain)] + /\ pending' = k + /\ UNCHANGED <> + ELSE /\ req' = RetireOutside(s, r.chain) + /\ work' = [k |-> "idle", ep |-> 0] /\ debt' = "none" + /\ coverage' = [k |-> "chain", chain |-> r.chain] + /\ settledOnce' = TRUE /\ pending' = p + /\ UNCHANGED <> + [] r.k = "full" -> + /\ req' = RetireAllFinite(s) + /\ work' = [k |-> "fullComplete", ep |-> 0] /\ debt' = "none" /\ own' = FALSE + /\ coverage' = [k |-> "full", chain |-> -1] /\ settledOnce' = TRUE /\ pending' = p + /\ UNCHANGED <> + +Fail(i) == + LET r == req[i] + f == [req EXCEPT ![i].st = "failed"] + p == IF pending = i THEN None ELSE pending + superseded == r.k = "finite" /\ r.sup /\ "ownSuperseded" \notin Weak + IN + /\ r.st = "inflight" + /\ staged' = IF r.k = "finite" /\ "dropStaged" \notin Weak THEN staged ELSE [k |-> "none", chain |-> -1] + /\ debt' = IF coverage.k = "full" /\ work.k = "fullComplete" /\ superseded THEN debt ELSE "full" + /\ IF superseded + THEN /\ own' = own + /\ IF work.k = "fullComplete" /\ "noLeaseCollect" \notin Weak + THEN /\ req' = [f EXCEPT ![i].st = "released"] /\ pending' = p /\ UNCHANGED work + ELSE IF ~own /\ work.k = "idle" + THEN \E k \in Free : /\ req' = [Supersede(f) EXCEPT ![k] = New("full", epoch, -1)] + /\ work' = [k |-> "fullHeld", ep |-> epoch] /\ pending' = k + ELSE /\ req' = f /\ pending' = p /\ UNCHANGED work + ELSE /\ own' = TRUE /\ req' = [f EXCEPT ![i].ownFail = TRUE] /\ pending' = p + /\ work' = CASE r.k = "full" -> [k |-> "fullFailed", ep |-> 0] + [] r.k \in {"prefix", "step"} /\ work.k = "chain" /\ work.ep = r.chain + -> [k |-> "idle", ep |-> 0] + [] OTHER -> work + /\ UNCHANGED <> + +\* An explicit window move. It first resumes a staged repair step; the step +\* runs inside it. Failed leases are released by the retry itself, and a +\* release may throw once: the retry reports it and starts nothing more. +Retry == + /\ own \/ \E i \in Ids : req[i].st = "failed" + /\ \/ /\ staged.k = "step" + /\ IF "stepReleases" \in Weak /\ fault + THEN \* the chain step releases failed leases; a throw unwinds it + /\ req' = [j \in Ids |-> IF req[j].st = "failed" THEN [req[j] EXCEPT !.st = "released"] ELSE req[j]] + /\ staged' = [k |-> "none", chain |-> -1] /\ fault' = FALSE + /\ UNCHANGED <> + ELSE \E k \in Free : + /\ req' = [req EXCEPT ![k] = New("step", StepEp(staged.chain), staged.chain)] + /\ pending' = k /\ staged' = [k |-> "none", chain |-> -1] + /\ UNCHANGED <> + /\ UNCHANGED <> + \/ /\ staged.k = "none" + /\ LET cleared == [j \in Ids |-> IF req[j].st = "failed" THEN [req[j] EXCEPT !.st = "released"] ELSE req[j]] + IN IF fault + THEN /\ req' = cleared /\ fault' = FALSE /\ UNCHANGED <> + ELSE /\ own' = FALSE /\ fault' = FALSE + /\ LET w == IF work.k = "fullFailed" THEN [k |-> "idle", ep |-> 0] ELSE work IN + IF w.k = "idle" /\ debt # "none" /\ coverage.k # "full" + THEN \E k \in Free : + /\ req' = [Supersede(cleared) EXCEPT ![k] = New("full", epoch, -1)] + /\ work' = [k |-> "fullHeld", ep |-> epoch] /\ pending' = k + /\ debt' = "full" + ELSE /\ req' = cleared /\ work' = w /\ UNCHANGED <> + /\ UNCHANGED staged + /\ UNCHANGED <> + +\* A request that throws while it starts (request() throws synchronously). +\* It is the loader's own failure and supersedes nothing: an authoritative +\* read counts toward supersession only once issued. +FailOnStart == + /\ ~own /\ staged.k = "none" + /\ \/ /\ work.k = "idle" /\ debt # "none" /\ coverage.k # "full" \* authoritative start + \/ /\ work.k = "idle" /\ debt = "none" /\ pending = None /\ coverage.k # "full" \* finite start + /\ \E k \in Free : + req' = [req EXCEPT ![k] = [New(IF debt # "none" THEN "full" ELSE "finite", epoch, -1) + EXCEPT !.st = "failed", !.ownFail = TRUE]] + /\ own' = TRUE /\ debt' = "full" /\ work' = [k |-> "idle", ep |-> 0] + /\ UNCHANGED <> + +Next == + \/ IssueFinite(FALSE) \/ IssueFinite(TRUE) \/ Invalidate \/ Pass \/ SyncPrefix \/ Retry + \/ FailOnStart + \/ \E i \in Ids : SettleOk(i) \/ Fail(i) + +Spec == Init /\ [][Next]_vars + +TypeOK == debt \in {"none", "bounded", "full"} + /\ work.k \in {"idle", "chain", "fullHeld", "fullComplete", "fullFailed"} + +\* P6: a finished repair still holds its own chain. +ChainHeld == (work.k = "idle" /\ debt = "none" /\ coverage.k = "chain") + => \A i \in Ids : (req[i].chain = coverage.chain /\ req[i].k \in {"prefix", "step"} + /\ req[i].st # "unused" /\ req[i].st # "failed") + => req[i].st = "settled" +\* P6/P7: quiet loading holds nothing a covering read superseded. +NoLeak == (Quiet /\ ~own /\ debt = "none" /\ work.k \in {"idle", "fullComplete"}) + => \A i \in Ids : + /\ (req[i].st = "settled" /\ req[i].k # "full" /\ coverage.k \in {"chain", "pages"}) + => (req[i].chain = coverage.chain + \/ (req[i].k = "finite" /\ req[i].ep = epoch /\ req[i].legit)) + /\ (req[i].st = "settled" /\ req[i].k # "full" /\ coverage.k = "full") => FALSE + /\ (req[i].st = "failed" /\ req[i].sup /\ coverage.k = "full") => FALSE +\* P10: no stall. A held repair with nothing in flight and nothing staged +\* has no step that could resume it. +NoStall == ~(work.k = "chain" /\ Quiet) +\* P5: a superseded failure never becomes the loader's own failure on its +\* own; an own failure needs an own failed request. +OwnIsGrounded == own => \E i \in Ids : req[i].ownFail /\ ~(req[i].k = "finite" /\ req[i].sup) +==== diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/LoaderLedger.tla b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/LoaderLedger.tla new file mode 100644 index 0000000000..416eb6107e --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/LoaderLedger.tla @@ -0,0 +1,168 @@ +---- MODULE LoaderLedger ---- +\* Abstraction of OrderedSourceLoader request authority and acquisition retirement. +\* Grammar trace: debt lattice (I1), staleness classification (I2), ledger retirement (I3). +\* Not modeled: scheduling (I4), cursor memo (O5), tie/refill sub-steps of the chain, window ops. +EXTENDS Naturals, FiniteSets, TLC +CONSTANTS MaxEpoch, Ids, Rules +\* Rules: "current" = the code's patched case rules; "unified" = one normalize step; +\* "oldRetire" | "oldLeak" | "oldRecover" = the three pre-fix rules (weakened controls). +VARIABLES epoch, debt, phase, chainEpoch, failure, coverage, req, settledOnce, coverEpoch +\* coverEpoch: the epoch stamp of the request that established the current coverage. +vars == <> + +Unused == [kind |-> "none", ep |-> 0, st |-> "unused", sup |-> FALSE] +Init == /\ epoch = 0 /\ debt = "none" /\ phase = "idle" /\ chainEpoch = 0 + /\ failure = "none" /\ coverage = "nothing" /\ settledOnce = FALSE + /\ req = [i \in Ids |-> Unused] /\ coverEpoch = 0 + +Inflight(k) == {i \in Ids : req[i].st = "inflight" /\ req[i].kind = k} +AnyInflight == \E i \in Ids : req[i].st = "inflight" +Free == {i \in Ids : req[i].st = "unused"} +New(k) == [kind |-> k, ep |-> epoch, st |-> "inflight", sup |-> FALSE] +\* Authoritative work started while a finite request is in flight supersedes it (P8). +Supersede(r) == [j \in Ids |-> IF r[j].st = "inflight" /\ r[j].kind = "finite" THEN [r[j] EXCEPT !.sup = TRUE] ELSE r[j]] + +\* Unified rule: in a quiescent coverage state, any settled entry older than the epoch is redundant. +Normalize(r, ph, db, ce) == + IF Rules \in {"unified", "unifiedMin"} /\ ph = "idle" /\ db = "none" + THEN [i \in Ids |-> IF r[i].st = "settled" /\ r[i].ep < ce + THEN [r[i] EXCEPT !.st = "released"] ELSE r[i]] + ELSE r + +IssueFinite == + /\ phase = "idle" /\ debt = "none" /\ failure = "none" /\ coverage # "full" + /\ Inflight("finite") = {} + /\ \E i \in Free : req' = [req EXCEPT ![i] = New("finite")] + /\ UNCHANGED <> + +Invalidate == + /\ epoch < MaxEpoch /\ coverage # "full" + /\ epoch' = epoch + 1 + /\ debt' = IF debt = "none" /\ settledOnce /\ phase = "idle" THEN "bounded" + ELSE IF debt = "none" THEN "full" ELSE debt + /\ UNCHANGED <> + +StartRepair == + /\ phase = "idle" /\ debt = "bounded" /\ failure = "none" /\ Inflight("chain") = {} + /\ \E i \in Free : req' = [Supersede(req) EXCEPT ![i] = New("chain")] + /\ phase' = "repair" /\ chainEpoch' = epoch + /\ UNCHANGED <> + +StartFull == + /\ phase = "idle" /\ debt = "full" /\ failure = "none" /\ Inflight("full") = {} + /\ \E i \in Free : req' = [Supersede(req) EXCEPT ![i] = New("full")] + /\ phase' = "fullheld" + /\ UNCHANGED <> + +\* An explicit window move retries a recorded failure (P3). +\* The same synchronous call releases the failed acquisitions and starts the +\* full-source read, so nothing settles between them. +ExplicitRetry == + /\ failure # "none" /\ phase = "idle" /\ debt = "full" /\ Inflight("full") = {} + /\ failure' = "none" + /\ LET cleared == [i \in Ids |-> IF req[i].st = "failed" THEN [req[i] EXCEPT !.st = "released"] ELSE req[i]] + IN \E k \in {i \in Ids : cleared[i].st \in {"unused", "released"}} : + req' = [Supersede(cleared) EXCEPT ![k] = New("full")] + /\ phase' = "fullheld" + /\ UNCHANGED <> + +ReleaseAllSettledExcept(r, keep) == + [j \in Ids |-> IF j # keep /\ r[j].st = "settled" THEN [r[j] EXCEPT !.st = "released"] ELSE r[j]] + +SettleFinite(i) == + LET r == req[i] + current == r.ep = epoch /\ phase = "idle" /\ failure = "none" + s == [req EXCEPT ![i].st = "settled"] + finished == phase = "idle" /\ debt = "none" + self == IF ~current /\ finished /\ Rules \notin {"oldLeak", "unifiedMin", "unifiedMinNoNorm"} + THEN [s EXCEPT ![i].st = "released"] ELSE s + \* Pre-fix rule: a stale prefix supersedes every smaller prefix, including the repair's. + nr == IF ~current /\ Rules = "oldRetire" + THEN [j \in Ids |-> IF self[j].kind = "chain" /\ self[j].st = "settled" + THEN [self[j] EXCEPT !.st = "released"] ELSE self[j]] + ELSE self + IN /\ r.st = "inflight" /\ r.kind = "finite" + /\ coverEpoch' = IF current THEN epoch ELSE coverEpoch + /\ req' = Normalize(nr, phase, debt, IF current THEN epoch ELSE coverEpoch) + /\ coverage' = IF current THEN "pages" ELSE coverage + /\ settledOnce' = (settledOnce \/ current) + /\ UNCHANGED <> + +SettleChain(i) == + LET r == req[i] IN + /\ r.st = "inflight" /\ r.kind = "chain" + /\ IF chainEpoch # epoch + THEN \* a newer invalidation: start the replacement chain (P5) + \E k \in Free : + /\ req' = [[req EXCEPT ![i].st = "settled"] EXCEPT ![k] = New("chain")] + /\ chainEpoch' = epoch + /\ UNCHANGED <> + ELSE IF failure # "none" \/ (Rules \in {"unified", "unifiedNoNorm", "unifiedMin", "unifiedMinNoNorm"} /\ debt = "full") + THEN IF Rules = "oldRecover" + THEN \* abandon: debt stays full, nothing restarts + /\ req' = [req EXCEPT ![i].st = "settled"] + /\ phase' = "idle" /\ debt' = "full" + /\ UNCHANGED <> + ELSE \* abandon and start the full-source read the overlap replaced + \E k \in Free : + /\ req' = [[req EXCEPT ![i].st = "settled"] EXCEPT ![k] = New("full")] + /\ phase' = "fullheld" /\ debt' = "full" + /\ UNCHANGED <> + ELSE LET settled == [req EXCEPT ![i].st = "settled"] + nr == IF Rules \in {"unifiedMin", "unifiedMinNoNorm"} THEN settled ELSE ReleaseAllSettledExcept(settled, i) IN + /\ req' = Normalize(nr, "idle", "none", epoch) + /\ phase' = "idle" /\ debt' = "none" /\ coverage' = "chain" /\ settledOnce' = TRUE + /\ coverEpoch' = epoch + /\ UNCHANGED <> + +SettleFull(i) == + /\ req[i].st = "inflight" /\ req[i].kind = "full" + /\ req' = IF Rules \in {"unifiedMin", "unifiedMinNoNorm"} + THEN Normalize([req EXCEPT ![i].st = "settled"], "idle", "none", req[i].ep) + ELSE ReleaseAllSettledExcept([req EXCEPT ![i].st = "settled"], i) + /\ phase' = "idle" /\ debt' = "none" /\ coverage' = "full" /\ failure' = "none" + /\ settledOnce' = TRUE /\ coverEpoch' = req[i].ep + /\ UNCHANGED <> + +Fail(i) == + LET r == req[i] + finishedBefore == phase = "idle" /\ debt = "none" /\ failure = "none" + \* Authoritative work failing is the loader's own failure: it waits for an + \* explicit or backoff retry, even if a superseded request failed first. + f == IF r.kind \in {"chain", "full"} THEN "own" + ELSE IF failure = "none" THEN (IF r.sup THEN "superseded" ELSE "own") ELSE failure + ph == IF r.kind \in {"chain", "full"} THEN "idle" ELSE phase + \* Unified: a superseded request's failure blocks nothing. Its partial + \* writes only raise the debt to full; ordinary loading then recovers. + upgradeOnly == Rules \in {"unified", "unifiedNoNorm", "unifiedMin", "unifiedMinNoNorm"} /\ r.kind = "finite" /\ r.sup + recover == ~upgradeOnly /\ r.sup /\ finishedBefore /\ Rules # "oldRecover" + failedReq == [req EXCEPT ![i].st = "failed"] + IN /\ r.st = "inflight" + /\ failure' = IF upgradeOnly THEN failure ELSE f + /\ debt' = IF upgradeOnly /\ coverage = "full" /\ phase = "idle" /\ debt = "none" + THEN debt ELSE "full" + /\ \/ /\ recover + /\ \E k \in Free : req' = [failedReq EXCEPT ![k] = New("full")] + /\ phase' = "fullheld" + \/ /\ ~recover + /\ req' = failedReq + /\ phase' = IF upgradeOnly THEN phase ELSE ph + /\ UNCHANGED <> + +Next == + \/ IssueFinite \/ Invalidate \/ StartRepair \/ StartFull \/ ExplicitRetry + \/ \E i \in Ids : SettleFinite(i) \/ SettleChain(i) \/ SettleFull(i) \/ Fail(i) + +Spec == Init /\ [][Next]_vars + +\* P4/P10: a finished repair's own acquisition is still held. +ChainHeld == (phase = "idle" /\ debt = "none" /\ coverage = "chain") + => \E i \in Ids : req[i].kind = "chain" /\ req[i].ep = coverEpoch /\ req[i].st = "settled" +\* P10: in a quiescent coverage state, nothing older than the coverage epoch is still held. +NoLeak == (phase = "idle" /\ debt = "none" /\ ~AnyInflight) + => \A i \in Ids : req[i].st = "settled" => req[i].ep >= coverEpoch +\* P3/P8: a request that authoritative work superseded never strands its failure. +\* (A failure of a request nothing superseded may block until an explicit retry: the failure law.) +NoStrandedStaleFailure == (failure = "superseded") => (phase # "idle" \/ AnyInflight) +TypeOK == debt \in {"none", "bounded", "full"} /\ phase \in {"idle", "repair", "fullheld"} +==== diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/LoaderTable.tla b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/LoaderTable.tla new file mode 100644 index 0000000000..afda13ea94 --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/LoaderTable.tla @@ -0,0 +1,360 @@ +---- MODULE LoaderTable ---- +\* Run 3: run 2 plus the transition table (#2194 after a66163f94). It adds a +\* cursor reset by truncate, the replay that settles it, the incarnation each +\* request is issued in, the work and debt coherence invariant, and the table +\* as an action property. A repair chain is identified by its epoch: a new +\* chain needs new debt, and new bounded debt needs an epoch move. +\* Weak names one rule from a fixed bug to re-enable. +EXTENDS Integers, FiniteSets +CONSTANTS MaxEpoch, Ids, Weak +\* Weak \subseteq {"lateStamp", "pendingBlocks", "dropStaged", "stepReleases", +\* "epochRetire", "noLeaseCollect", "ownSuperseded", +\* "noAbsorb", "replayKeepsDebt", "chainOwnsAfterFinish", "debtUnderCoverage"} +VARIABLES epoch, debt, work, own, req, pending, staged, settledOnce, fault, coverage, + gen, replay, truncated +vars == <> + +None == 0 +Unused == [k |-> "none", ep |-> 0, chain |-> -1, st |-> "unused", sup |-> FALSE, legit |-> FALSE, + ownFail |-> FALSE, gen |-> 0, replayed |-> FALSE] +Init == /\ epoch = 0 /\ debt = "none" /\ work = [k |-> "idle", ep |-> 0] /\ own = FALSE + /\ req = [i \in Ids |-> Unused] /\ pending = None + /\ staged = [k |-> "none", chain |-> -1] /\ settledOnce = FALSE + /\ fault = TRUE \* one release callback may throw + /\ coverage = [k |-> "nothing", chain |-> -1] + /\ gen = 0 /\ replay = FALSE /\ truncated = FALSE + +Inflight == {i \in Ids : req[i].st = "inflight"} +Free == {i \in Ids : req[i].st = "unused"} +Quiet == Inflight = {} /\ staged.k = "none" +IsAuth(r) == r.k \in {"prefix", "step", "full"} +\* Authoritative work supersedes in-flight finite requests once issued. +Supersede(r) == [j \in Ids |-> IF r[j].st = "inflight" /\ r[j].k = "finite" + THEN [r[j] EXCEPT !.sup = TRUE] ELSE r[j]] +New(k, ep, ch) == [k |-> k, ep |-> ep, chain |-> ch, st |-> "inflight", sup |-> FALSE, + legit |-> FALSE, ownFail |-> FALSE, gen |-> gen, replayed |-> FALSE] +Dead(r) == r.gen # gen +\* A chain request owns authority only while its own chain runs. +OwnsChain(r) == work.k = "chain" /\ work.ep = r.chain +\* A chain step's ledger epoch: its chain's, or (reverted rule) its issue epoch. +StepEp(ch) == IF "epochRetire" \in Weak THEN epoch ELSE ch +Stale(r) == ~IsAuth(r) /\ r.ep # epoch +\* Release every settled finite entry outside chain ch (or older than ch's +\* epoch, under the reverted epoch rule). +RetireOutside(r, ch) == + [j \in Ids |-> IF r[j].st = "settled" /\ r[j].k # "full" + /\ (IF "epochRetire" \in Weak THEN r[j].ep < ch ELSE r[j].chain # ch) + THEN [r[j] EXCEPT !.st = "released"] ELSE r[j]] +RetireAllFinite(r) == + [j \in Ids |-> IF (r[j].st = "settled" /\ r[j].k # "full") + \/ (r[j].st = "failed" /\ r[j].sup /\ "noLeaseCollect" \notin Weak) + THEN [r[j] EXCEPT !.st = "released"] ELSE r[j]] + +StartAuthority(r) == + \* From an idle slot with debt: a bounded chain or a full-source read. + \E k \in Free : + /\ IF debt = "bounded" + THEN /\ req' = [Supersede(r) EXCEPT ![k] = New("prefix", epoch, epoch)] + /\ work' = [k |-> "chain", ep |-> epoch] + ELSE /\ req' = [Supersede(r) EXCEPT ![k] = New("full", epoch, -1)] + /\ work' = [k |-> "fullHeld", ep |-> epoch] + /\ pending' = IF "pendingBlocks" \in Weak THEN (IF pending = None THEN k ELSE pending) ELSE k + +\* An ordinary finite page; optionally its own delivery invalidates ordering. +IssueFinite(selfInvalidates) == + /\ ~replay + /\ work.k = "idle" /\ debt = "none" /\ ~own /\ pending = None /\ staged.k = "none" + /\ coverage.k # "full" + /\ (selfInvalidates => epoch < MaxEpoch) + /\ \E k \in Free : + /\ req' = [req EXCEPT ![k] = [New("finite", + IF selfInvalidates /\ "lateStamp" \in Weak THEN epoch + 1 ELSE epoch, -1) + EXCEPT !.legit = TRUE]] + /\ pending' = k + /\ epoch' = IF selfInvalidates THEN epoch + 1 ELSE epoch + /\ debt' = IF selfInvalidates THEN (IF settledOnce THEN "bounded" ELSE "full") ELSE debt + /\ UNCHANGED <> + +Invalidate == + /\ epoch < MaxEpoch /\ coverage.k # "full" + /\ epoch' = epoch + 1 + /\ debt' = IF debt = "none" /\ settledOnce /\ work.k = "idle" THEN "bounded" + ELSE IF debt = "none" THEN "full" ELSE debt + /\ UNCHANGED <> + +\* An ordinary pass: consume a staged step, or start owed authoritative work. +Pass == + /\ ~replay /\ ~own + /\ \/ /\ staged.k = "step" + /\ IF pending # None /\ "pendingBlocks" \in Weak + THEN \* the step waits behind a superseded request and is lost + /\ staged' = [k |-> "none", chain |-> -1] + /\ UNCHANGED <> + ELSE \E k \in Free : + /\ req' = [req EXCEPT ![k] = New("step", StepEp(staged.chain), staged.chain)] + /\ pending' = k + /\ staged' = [k |-> "none", chain |-> -1] + /\ UNCHANGED <> + \/ /\ staged.k = "none" /\ work.k = "idle" /\ debt # "none" /\ coverage.k # "full" + /\ StartAuthority(req) + /\ UNCHANGED <> + /\ UNCHANGED <> + +\* A repair prefix that answers at once, as an eager source does: its step is +\* staged until a graph turn resumes it. +SyncPrefix == + /\ ~replay /\ ~own /\ staged.k = "none" /\ work.k = "idle" /\ debt = "bounded" + /\ \E k \in Free : + /\ req' = [Supersede(req) EXCEPT ![k] = [New("prefix", epoch, epoch) EXCEPT !.st = "settled"]] + /\ work' = [k |-> "chain", ep |-> epoch] + /\ staged' = [k |-> "step", chain |-> epoch] + /\ pending' = IF "pendingBlocks" \in Weak THEN pending ELSE None + /\ UNCHANGED <> + +Restart(r) == + \* A chain whose epoch moved or whose debt rose to full is replaced. + IF debt = "full" + THEN \E k \in Free : /\ req' = [Supersede(r) EXCEPT ![k] = New("full", epoch, -1)] + /\ work' = [k |-> "fullHeld", ep |-> epoch] /\ pending' = k + ELSE \E k \in Free : /\ req' = [Supersede(r) EXCEPT ![k] = New("prefix", epoch, epoch)] + /\ work' = [k |-> "chain", ep |-> epoch] /\ pending' = k + +SettleOk(i) == + LET r == req[i] + s == [req EXCEPT ![i].st = "settled"] + p == IF pending = i THEN None ELSE pending + IN + /\ r.st = "inflight" + /\ UNCHANGED <> + /\ CASE Dead(r) -> + /\ req' = s /\ pending' = p + /\ UNCHANGED <> + [] r.k = "finite" -> + /\ IF Stale(r) \/ work.k # "idle" \/ own + THEN \* proves nothing; once a repair finished it is already superseded + \* Full-source coverage retires every settled finite lease. + /\ req' = IF (work.k = "idle" /\ debt = "none" /\ Stale(r)) + \/ work.k = "fullComplete" + THEN [s EXCEPT ![i].st = "released"] ELSE s + /\ UNCHANGED <> + ELSE /\ coverage' = [k |-> "pages", chain |-> coverage.chain] + /\ settledOnce' = TRUE + \* A current page continues with its tie at once. Under debt + \* that tie is not a refinement of current coverage. + /\ \/ req' = s + \/ \E k \in Free : + req' = [s EXCEPT ![k] = [New("finite", epoch, -1) + EXCEPT !.legit = (debt = "none")]] + /\ pending' = p + /\ UNCHANGED <> + [] r.k \in {"prefix", "step"} -> + IF work.k # "chain" \/ work.ep # r.chain + THEN \* Run 3: full coverage retires it, and so does a finished + \* replacement; the chain that finished keeps its own. + /\ req' = IF work.k = "fullComplete" + \/ (work.k = "idle" /\ debt = "none" /\ coverage.chain # r.chain) + THEN [s EXCEPT ![i].st = "released"] ELSE s + /\ pending' = p + /\ UNCHANGED <> + ELSE IF work.ep # epoch \/ debt = "full" + THEN /\ Restart(s) + /\ UNCHANGED <> + ELSE IF r.k = "prefix" + THEN \E k \in Free : + /\ req' = [s EXCEPT ![k] = New("step", StepEp(r.chain), r.chain)] + /\ pending' = k + /\ UNCHANGED <> + ELSE /\ req' = RetireOutside(s, r.chain) + /\ work' = [k |-> "idle", ep |-> 0] /\ debt' = "none" + /\ coverage' = [k |-> "chain", chain |-> r.chain] + /\ settledOnce' = TRUE /\ pending' = p + /\ UNCHANGED <> + [] r.k = "full" -> + /\ req' = RetireAllFinite(s) + /\ work' = [k |-> "fullComplete", ep |-> 0] /\ debt' = "none" /\ own' = FALSE + /\ coverage' = [k |-> "full", chain |-> -1] /\ settledOnce' = TRUE /\ pending' = p + /\ UNCHANGED <> + +Covered == work.k = "fullComplete" /\ "debtUnderCoverage" \notin Weak +Pending(i) == IF pending = i THEN None ELSE pending + +\* A request from before a cursor reset fails. Its partial writes still raise +\* debt, but it touches nothing staged after the reset. +FailDead(i) == + /\ req[i].st = "inflight" /\ Dead(req[i]) + /\ req' = [req EXCEPT ![i].st = "failed"] /\ pending' = Pending(i) + /\ debt' = IF Covered THEN debt ELSE "full" + /\ UNCHANGED <> + +\* A full-source transport fails after a replay reacquired its demand: the +\* coverage, and its lease, stay in place. +FailAbsorbed(i) == + /\ req[i].st = "inflight" /\ ~Dead(req[i]) + /\ req[i].k = "full" /\ work.k = "fullComplete" /\ "noAbsorb" \notin Weak + /\ req' = [req EXCEPT ![i].st = "settled"] /\ pending' = Pending(i) + /\ UNCHANGED <> + +FailLive(i) == + LET r == req[i] + f == [req EXCEPT ![i].st = "failed"] + p == Pending(i) + superseded == \/ r.k = "finite" /\ r.sup /\ "ownSuperseded" \notin Weak + \/ r.k \in {"prefix", "step"} /\ ~OwnsChain(r) + /\ "chainOwnsAfterFinish" \notin Weak + IN + /\ r.st = "inflight" /\ ~Dead(r) + /\ ~(r.k = "full" /\ work.k = "fullComplete" /\ "noAbsorb" \notin Weak) + /\ staged' = IF (r.k = "finite" \/ superseded) /\ "dropStaged" \notin Weak + THEN staged ELSE [k |-> "none", chain |-> -1] + /\ debt' = IF Covered THEN debt ELSE "full" + /\ IF superseded + THEN /\ own' = own + /\ IF work.k = "fullComplete" /\ "noLeaseCollect" \notin Weak + THEN /\ req' = [f EXCEPT ![i].st = "released"] /\ pending' = p /\ UNCHANGED work + ELSE IF ~own /\ work.k = "idle" + THEN \E k \in Free : /\ req' = [Supersede(f) EXCEPT ![k] = New("full", epoch, -1)] + /\ work' = [k |-> "fullHeld", ep |-> epoch] /\ pending' = k + ELSE /\ req' = f /\ pending' = p /\ UNCHANGED work + ELSE /\ own' = TRUE /\ req' = [f EXCEPT ![i].ownFail = TRUE] /\ pending' = p + /\ work' = CASE r.k = "full" -> [k |-> "fullFailed", ep |-> 0] + [] r.k \in {"prefix", "step"} /\ OwnsChain(r) + -> [k |-> "idle", ep |-> 0] + [] OTHER -> work + /\ UNCHANGED <> + +Fail(i) == FailDead(i) \/ FailAbsorbed(i) \/ FailLive(i) + +\* An explicit window move. It first resumes a staged repair step; the step +\* runs inside it. Failed leases are released by the retry itself, and a +\* release may throw once: the retry reports it and starts nothing more. +Retry == + /\ own \/ \E i \in Ids : req[i].st = "failed" + /\ \/ /\ staged.k = "step" + /\ IF "stepReleases" \in Weak /\ fault + THEN \* the chain step releases failed leases; a throw unwinds it + /\ req' = [j \in Ids |-> IF req[j].st = "failed" THEN [req[j] EXCEPT !.st = "released"] ELSE req[j]] + /\ staged' = [k |-> "none", chain |-> -1] /\ fault' = FALSE + /\ UNCHANGED <> + ELSE \E k \in Free : + /\ req' = [req EXCEPT ![k] = New("step", StepEp(staged.chain), staged.chain)] + /\ pending' = k /\ staged' = [k |-> "none", chain |-> -1] + /\ UNCHANGED <> + /\ UNCHANGED <> + \/ /\ staged.k = "none" + /\ LET cleared == [j \in Ids |-> IF req[j].st = "failed" THEN [req[j] EXCEPT !.st = "released"] ELSE req[j]] + IN IF fault + THEN /\ req' = cleared /\ fault' = FALSE /\ UNCHANGED <> + ELSE /\ own' = FALSE /\ fault' = FALSE + /\ LET w == IF work.k = "fullFailed" THEN [k |-> "idle", ep |-> 0] ELSE work IN + IF w.k = "idle" /\ debt # "none" /\ coverage.k # "full" + THEN \E k \in Free : + /\ req' = [Supersede(cleared) EXCEPT ![k] = New("full", epoch, -1)] + /\ work' = [k |-> "fullHeld", ep |-> epoch] /\ pending' = k + /\ debt' = "full" + ELSE /\ req' = cleared /\ work' = w /\ UNCHANGED <> + /\ UNCHANGED staged + /\ UNCHANGED <> + +\* A request that throws while it starts (request() throws synchronously). +\* It is the loader's own failure and supersedes nothing: an authoritative +\* read counts toward supersession only once issued. +FailOnStart == + /\ ~replay /\ ~own /\ staged.k = "none" + /\ \/ /\ work.k = "idle" /\ debt # "none" /\ coverage.k # "full" \* authoritative start + \/ /\ work.k = "idle" /\ debt = "none" /\ pending = None /\ coverage.k # "full" \* finite start + /\ \E k \in Free : + req' = [req EXCEPT ![k] = [New(IF debt # "none" THEN "full" ELSE "finite", epoch, -1) + EXCEPT !.st = "failed", !.ownFail = TRUE]] + /\ own' = TRUE /\ debt' = "full" /\ work' = [k |-> "idle", ep |-> 0] + /\ UNCHANGED <> + +\* An explicit window move still runs while a replay is pending, and it can +\* start owed authoritative work. +WindowMoveDuringReplay == + /\ replay /\ ~own /\ staged.k = "none" /\ work.k = "idle" /\ debt # "none" + /\ coverage.k # "full" + /\ StartAuthority(req) + /\ UNCHANGED <> + +\* A truncate resets the cursor: requests in flight become dead, complete +\* coverage is held again, and ordinary loading waits for the replay. +Truncate == + /\ ~truncated + /\ truncated' = TRUE /\ gen' = gen + 1 /\ replay' = TRUE + /\ work' = IF work.k = "fullComplete" THEN [k |-> "fullHeld", ep |-> 0] ELSE work + /\ pending' = None /\ staged' = [k |-> "none", chain |-> -1] + /\ coverage' = [k |-> "nothing", chain |-> -1] + /\ UNCHANGED <> + +\* The replay reacquires every held demand, in-flight ones included. It +\* finishes a running chain, or completes held full-source work. +ReplaySettles == + /\ replay + /\ replay' = FALSE + /\ LET r == [j \in Ids |-> IF req[j].st = "inflight" + THEN [req[j] EXCEPT !.replayed = TRUE] ELSE req[j]] IN + IF work.k = "chain" + THEN IF work.ep # epoch \/ debt = "full" + THEN /\ Restart(r) /\ UNCHANGED <> + ELSE /\ req' = RetireOutside(r, work.ep) /\ pending' = pending + /\ work' = [k |-> "idle", ep |-> 0] /\ debt' = "none" + /\ coverage' = [k |-> "chain", chain |-> work.ep] + ELSE IF work.k # "idle" + THEN /\ req' = [j \in Ids |-> IF r[j].st = "settled" /\ r[j].k # "full" + THEN [r[j] EXCEPT !.st = "released"] ELSE r[j]] + /\ work' = [k |-> "fullComplete", ep |-> 0] + /\ debt' = IF "replayKeepsDebt" \in Weak THEN debt ELSE "none" + /\ coverage' = [k |-> "full", chain |-> -1] /\ pending' = pending + ELSE /\ req' = r /\ UNCHANGED <> + /\ UNCHANGED <> + +Next == + \/ IssueFinite(FALSE) \/ IssueFinite(TRUE) \/ Invalidate \/ Pass \/ SyncPrefix \/ Retry + \/ FailOnStart \/ Truncate \/ ReplaySettles \/ WindowMoveDuringReplay + \/ \E i \in Ids : SettleOk(i) \/ Fail(i) + +Spec == Init /\ [][Next]_vars + +TypeOK == debt \in {"none", "bounded", "full"} + /\ work.k \in {"idle", "chain", "fullHeld", "fullComplete", "fullFailed"} + +\* P6: a finished repair still holds its own chain. +ChainHeld == (work.k = "idle" /\ debt = "none" /\ coverage.k = "chain") + => \A i \in Ids : (req[i].chain = coverage.chain /\ req[i].k \in {"prefix", "step"} + /\ req[i].st # "unused" /\ req[i].st # "failed" + /\ ~(req[i].st = "inflight" /\ req[i].replayed) /\ ~Dead(req[i])) + => req[i].st = "settled" +\* P6/P7: quiet loading holds nothing a covering read superseded. +NoLeak == (Quiet /\ ~replay /\ ~own /\ debt = "none" /\ work.k \in {"idle", "fullComplete"}) + => \A i \in Ids : Dead(req[i]) \/ + /\ (req[i].st = "settled" /\ req[i].k # "full" /\ coverage.k \in {"chain", "pages"}) + => (req[i].chain = coverage.chain + \/ (req[i].k = "finite" /\ req[i].ep = epoch /\ req[i].legit)) + /\ (req[i].st = "settled" /\ req[i].k # "full" /\ coverage.k = "full") => FALSE + /\ (req[i].st = "failed" /\ req[i].sup /\ coverage.k = "full") => FALSE +\* P10: no stall. A held repair with nothing in flight and nothing staged +\* has no step that could resume it. +\* Run 3: a pending replay finishes the chain itself. +NoStall == ~(work.k = "chain" /\ Quiet /\ ~replay) +\* P5: a superseded failure never becomes the loader's own failure on its +\* own; an own failure needs an own failed request. +\* Run 3: nor can a request whose demand a replay reacquired. +OwnIsGrounded == own => \E i \in Ids : req[i].ownFail /\ ~(req[i].k = "finite" /\ req[i].sup) + /\ ~req[i].replayed + +\* The work and debt pairs the table allows. +Coherence == /\ work.k = "fullComplete" => debt = "none" + /\ work.k = "chain" => debt # "none" + /\ work.k = "fullFailed" => debt = "full" + +\* The transition table as an action property. A step may keep its state, or +\* make one of these moves (a restart or retry leaves and re-enters idle). +Moves == {<<"idle", "chain">>, <<"idle", "fullHeld">>, <<"chain", "idle">>, + <<"chain", "chain">>, <<"chain", "fullHeld">>, <<"fullHeld", "fullComplete">>, + <<"fullHeld", "fullFailed">>, <<"fullFailed", "fullComplete">>, + <<"fullFailed", "idle">>, <<"fullFailed", "fullHeld">>, + <<"fullComplete", "fullHeld">>, <<"fullHeld", "idle">>} +TableLegal == work'.k = work.k \/ <> \in Moves +Table == [][TableLegal]_vars +==== diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/README.md b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/README.md new file mode 100644 index 0000000000..6fe36888c0 --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/README.md @@ -0,0 +1,255 @@ +# Ordered loader request authority: TLA+ control + +This directory holds the bounded TLA+ control from the design-grammar run on +`OrderedSourceLoader` (#2191). It checks the internal consequences of the +grammar's rules for request authority and acquisition retirement. It does not +show that production refines the model. The generated request-authority lane in +`packages/db/tests/query/ordered-source-loader-state-oracle.test.ts` +(`ordered-loader.request-authority`) owns that claim. It drives the real loader +and checks the same three laws. + +## Frozen preservation list + +The user confirmed these properties before extraction. They come from +ARCHITECTURE.md, "Ordered requests, continuation, and recovery", and the +loader source. + +| ID | Property | +| --- | ------------------------------------------------------------------------------------------------------------------------------------------------ | +| P1 | Request shapes: first page or prefix, tie, continuation, bounded repair (`refetch: true`, `limit = offset + limit`), and full-source read. | +| P2 | Settlement proves only the exact request. The boundary is the last source-derived row. | +| P3 | The loader's own failure blocks ordinary refills until an explicit window move or the bounded backoff retry. | +| P4 | A request issued before an ordering invalidation proves no current coverage. It cannot clear a failure, change a repair, or retire newer reads. | +| P5 | A repair chain is fenced by its invalidation epoch, and its replaced acquisitions retire only after the chain succeeds. | +| P6 | Authoritative work with ordering debt holds publication. | +| P7 | A report is ignored for full-source coverage or a window inside its own first page. | +| P8 | An overlap after the first settlement starts the bounded repair at once. | +| P9 | One request runs at a time; a staged continuation waits for the graph turn that processes its rows. | +| P10 | Every acquisition is released exactly once. A successful full-source read retires every settled finite acquisition. | +| P11 | Window operations carry a generation. | +| P12 | The loader's public methods and the existing oracles stay unchanged. | + +## Grammar to model trace + +| Grammar item | TLA+ element | +| ---------------------------------------------------------------------- | -------------------------------------------------- | +| Debt lattice: none, bounded, full (`needsOrderingRepair`, `canRepair…`) | `debt`, `Invalidate` | +| Work slot: idle, repair chain at an epoch, full-source read | `phase`, `chainEpoch`, `StartRepair`, `StartFull` | +| Request stamp and classification | `req[i].ep`, `epoch` | +| Supersession by later authoritative work (P8) | `req[i].sup`, `Supersede` | +| Own and superseded failures (P3) | `failure`, `Fail` | +| Explicit window retry, atomic with its full-source start (P3) | `ExplicitRetry` | +| Ledger and the coverage epoch (P5, P10) | `req[i].st`, `coverEpoch`, `Normalize` | + +Invariants: + +- `ChainHeld`: a finished repair still holds its own acquisition (P4, P10). +- `NoLeak`: once loading is quiet, nothing older than the coverage epoch is + held (P10). +- `NoStrandedStaleFailure`: a superseded request's failure always has + recovery running (P3, P8). + +The `Rules` constant selects the rule set: + +| Configuration | `Rules` | Meaning | +| --------------------------------- | ------------------ | ---------------------------------------------------------- | +| `safe-unified.cfg` | `unifiedMin` | Superseded failures raise debt only; one epoch normalization. Passes the model but did not transfer (see Limits). | +| `safe-patched-cases.cfg` | `unified` | The same superseded rule, with the per-case retirement rules kept. The shipped rule set. | +| `weak-main-before-2191.cfg` | `current` | The rules on main before #2191. | +| `weak-stale-prefix-retires.cfg` | `oldRetire` | A stale prefix retires the repair's prefix. | +| `weak-stale-held.cfg` | `oldLeak` | A stale request that settles after the repair stays held. | +| `weak-no-recovery.cfg` | `oldRecover` | A superseded failure starts no recovery. | +| `weak-no-normalize.cfg` | `unifiedMinNoNorm` | The unified rules without the normalization step. | + +## Results + +TLC 2.19 (rev 5a47802) through the Field Lab container, `MaxEpoch = 2`, four +request slots, deadlock check off (full coverage is terminal): + +| Configuration | Result | +| ------------------------------- | --------------------------------------------------- | +| `safe-unified.cfg` | No error, 26,048 distinct states, exhaustive | +| `safe-patched-cases.cfg` | No error, 25,172 distinct states, exhaustive | +| `weak-main-before-2191.cfg` | `NoStrandedStaleFailure` violated in 9 steps | +| `weak-stale-prefix-retires.cfg` | `ChainHeld` violated | +| `weak-stale-held.cfg` | `NoLeak` violated | +| `weak-no-recovery.cfg` | `NoStrandedStaleFailure` violated | +| `weak-no-normalize.cfg` | `NoLeak` violated | + +The 9-step trace on main: a page is superseded by a bounded repair, the repair +finishes, a newer invalidation's reaction is deferred, and the old page fails. +The loader recorded the failure as its own and blocked refills. The loader +state oracle reproduced this on main before the fix. + +Four encoding corrections were made during the run; none weakened a frozen +property: + +1. The strand invariant covers only superseded failures, since the failure law + allows an unsuperseded failure to block. +2. The leak and held invariants measure from the coverage epoch, since an + overlapped full-source read still clears the debt. +3. An authoritative failure counts as the loader's own failure. +4. An explicit retry starts its full-source read in the same step. + +## Limits + +The single normalization rule (`safe-unified.cfg`) passed this model but failed +the real loader, and was reverted. The model stamps a request with the epoch it +is issued in and never issues a finite request while repair debt is pending. In +the loader, a stale finite chain can issue a tie in the new epoch before the +repair starts, and a repair's staged continuation can be issued after a newer +invalidation. Retiring by epoch kept both; retiring everything outside the repair +chain releases both. The request-authority lane found these once it generated +self-invalidating pages and synchronous repair answers. + +The model leaves out the scheduler (staged continuations, deferred refills, and +the graph-revision check), cursor and boundary values, tie and refill sub-steps, +same-epoch prefix supersession, truncate replay, and publication holds. Two of +the four flag-interaction bugs found in #2154 lived in parts it leaves out. A +passing bounded run says nothing about states outside these bounds. + +## Run + +```sh +~/.claude/skills/field-lab/scripts/tlc . -config safe-unified.cfg LoaderLedger +``` + + +## Run 2: the shipped state machine + +`LoaderAuthority.tla` models the loader after #2194's review fixes, and it +is the model the `work` and `debt` representation follows. It adds what run 1 +left out: a page's own delivery invalidating ordering, a current page's tie +issued while repair debt is pending, a repair prefix that answers at once with +its step staged, the scheduler's `pending` request, retirement by repair-chain +membership, superseded failed leases, and an explicit retry whose release +throws. + +| Grammar item | Loader field | TLA+ element | +| --- | --- | --- | +| Work slot: idle, repair chain at an epoch, full-source read held, complete or failed | `work` | `work` | +| Debt: none, bounded, full | `debt` | `debt` | +| Request stamp at issue | `RequestStamp`, `standing()` | `req[i].ep`, `Stale` | +| Own and superseded failures | `failedRequest`, `failedAcquisitions[].superseded` | `own`, `req[i].sup` | +| Repair-chain membership | `settledFiniteAcquisitions[].chain` | `req[i].chain`, `RetireOutside` | +| Scheduler: one request, staged repair step | `pending`, `stagedContinuation` | `pending`, `staged` | + +Invariants: `ChainHeld`, `NoLeak` (including superseded failed leases, and +pages issued under debt), `NoStall` (no held repair with nothing in flight or +staged), and `OwnIsGrounded` (an own failure comes from a request that nothing +superseded). + +| Configuration | Weakened rule (one bug fixed in #2194) | Result | +| --- | --- | --- | +| `run2-safe.cfg` | none | No error, 290,401 distinct states | +| `run2-weak-lateStamp.cfg` | Stamp the epoch when the request returns | `NoLeak` violated | +| `run2-weak-epochRetire.cfg` | Retire by issue epoch (the reverted rule) | `NoLeak` violated | +| `run2-weak-ownSuperseded.cfg` | A superseded failure counts as own | `OwnIsGrounded` violated | +| `run2-weak-pendingBlocks.cfg` | A repair waits behind a superseded `pending` | `NoStall` violated | +| `run2-weak-dropStaged.cfg` | A finite failure drops staged repair work | `NoStall` violated | +| `run2-weak-stepReleases.cfg` | A chain step releases failed leases | `NoStall` violated | +| `run2-weak-noLeaseCollect.cfg` | Recovery leaves a superseded lease held | `NoLeak` violated | + +Three weakened rules first passed this model. Each pass exposed a gap that was +closed before the results above: the model let no current page continue under +pending debt, it stamped chain steps by chain under the reverted rule, and its +grounding check accepted any failure. Four encoding corrections were also +made; none weakened a frozen property. + +A review found one history the model could not generate: a request that +throws while it starts (`request()` throws synchronously). The code records it +as the loader's own failure and counts no supersession. `FailOnStart` now +models it; the safe rules still pass, and every weakened rule still fails. + +Two mutants of the code survive every oracle and are equivalent within the +tested domain: keeping a completed full-source read `complete` across +`resetCursor`, and not clearing the debt when a full-source read succeeds. +Once a full-source read completes, the work slot never returns to `idle` +before a reset or replay, so those values are not observed. A witness would +need a path from `complete` back to `idle`, or a report that a replay window +must not ignore. + +Loss audits of the loader code, ARCHITECTURE.md, and the fix history found +these loader rules outside this model, in addition to the limits below: the +optimistic-overlay refill (`needsOptimisticRefill`), the unexpressible-boundary +fallback, leading-column cursors and optimistic headroom, the +window-generation retry gate, report handling and the first-page skip, the +graph-revision check, and synchronous-settlement eligibility. Query DB's report +gate and the coverage law belong to the query-db refetch oracle. + +Limits: boundary and cursor values, tie and refill as separate steps, +joined-demand deferral, truncate replay, publication holds, the backoff timer, +and release faults outside an explicit retry are not modeled. The +request-authority lane and the existing loader oracles check the real loader. + +```sh +~/.claude/skills/field-lab/scripts/tlc . -config run2-safe.cfg LoaderAuthority +``` + +## Run 3: the transition table + +`LoaderTable.tla` extends run 2 with the loader's transition table +(#2194 after a66163f94). It adds a cursor reset by truncate, the replay that +settles it, the incarnation each request is issued in, a window move that +starts owed work while a replay is pending, and four checks: + +- `Coherence`: complete coverage owes no debt, a repair owes some, and a + failed full-source read owes a full-source read. +- `Table`: an action property. Each step keeps the work state or makes one + of the moves the loader's `moveWork()` allows. +- `OwnIsGrounded`, strengthened: an own failure cannot come from a request + whose demand a replay reacquired. +- `NoStall`, `NoLeak`, and `ChainHeld` allow a pending replay, which owns + the requests from before the truncate and finishes a running chain. + +A repair chain is still identified by its epoch. A new chain needs new debt, +and new bounded debt needs an epoch move, so the epoch names the chain in +this model. The loader uses the authority generation for the same purpose. + +| Configuration | Weakened rule (one defect fixed in this round) | Result | +| --- | --- | --- | +| `run3-safe.cfg` | none | No error, 9,368,122 distinct states, depth 17 | +| `run3-weak-noAbsorb.cfg` | A full-source failure after a covering replay becomes own | `OwnIsGrounded` violated in 6 steps | +| `run3-weak-noAbsorb-table.cfg` | The same, checking only the table | `Table` violated in 6 steps | +| `run3-weak-replayKeepsDebt.cfg` | A replay keeps the failed read's full debt | `Coherence` violated in 5 steps | +| `run3-weak-chainOwnsAfterFinish.cfg` | A chain request owns authority after a replay finished its chain | `OwnIsGrounded` violated in 8 steps | +| `run3-weak-debtUnderCoverage.cfg` | A failure raises debt under complete coverage | `Coherence` violated in 5 steps | + +All seven run-2 weakened rules still fail under run 3 with five request +slots. With four slots, `lateStamp` and `epochRetire` pass, so run 3 uses +five. + +Three encoding corrections were made; none weakened a property. `NoStall` +first flagged a running chain whose staged step a truncate had cleared, which +the pending replay finishes. `chainOwnsAfterFinish` first passed because no +action could start a chain while a replay was pending; the explicit window +move does that in the loader. The safe rules then flagged a replaced chain's +request that settled after full coverage, which the loader now retires; the +model's settle rule now retires it too. + +Run 1 (`LoaderLedger.tla`) is kept as history. Its two gaps that a review +noted, a superseded failed lease held after a full-source read and a strand +invariant that holds vacuously under `unifiedMin`, are closed in run 2 by +`NoLeak` and `OwnIsGrounded` and carried into run 3. + +```sh +~/.claude/skills/field-lab/scripts/tlc . -config run3-safe.cfg LoaderTable +``` + +## Later changes the model does not cover + +The loader changed after run 3 in ways that these models do not express. The +production oracles own them, and the coverage map's "Ordered acquisition" row +records their evidence: + +- A throwing `unloadSubset` is an adapter bug. Core and the loader report it + uncaught and continue as if the release succeeded. The models have no + release faults, so their rules are unchanged. +- A report that served pages may have shifted issues no authoritative read + before any request settled, or while the window is inside the loader's own + first page. The models have no reports; the request-authority oracle's + ignored-report law owns this. +- Two compression passes and a differential mutant instrument deleted six + guards that other loader code makes redundant. None of them is a model + action; the transition table and the request stamps are unchanged, so run + 3's receipts still apply to them. diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-safe.cfg b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-safe.cfg new file mode 100644 index 0000000000..2952f7cd04 --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-safe.cfg @@ -0,0 +1,7 @@ +SPECIFICATION Spec +CONSTANTS + MaxEpoch = 2 + Ids = {1, 2, 3, 4, 5} + Weak = {} +INVARIANTS TypeOK ChainHeld NoLeak NoStall OwnIsGrounded +CHECK_DEADLOCK FALSE diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-weak-dropStaged.cfg b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-weak-dropStaged.cfg new file mode 100644 index 0000000000..6e4714d488 --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-weak-dropStaged.cfg @@ -0,0 +1,7 @@ +SPECIFICATION Spec +CONSTANTS + MaxEpoch = 2 + Ids = {1, 2, 3, 4, 5} + Weak = {"dropStaged"} +INVARIANTS TypeOK ChainHeld NoLeak NoStall OwnIsGrounded +CHECK_DEADLOCK FALSE diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-weak-epochRetire.cfg b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-weak-epochRetire.cfg new file mode 100644 index 0000000000..5b9d409ec4 --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-weak-epochRetire.cfg @@ -0,0 +1,7 @@ +SPECIFICATION Spec +CONSTANTS + MaxEpoch = 2 + Ids = {1, 2, 3, 4, 5} + Weak = {"epochRetire"} +INVARIANTS TypeOK ChainHeld NoLeak NoStall OwnIsGrounded +CHECK_DEADLOCK FALSE diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-weak-lateStamp.cfg b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-weak-lateStamp.cfg new file mode 100644 index 0000000000..89a7138e04 --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-weak-lateStamp.cfg @@ -0,0 +1,7 @@ +SPECIFICATION Spec +CONSTANTS + MaxEpoch = 2 + Ids = {1, 2, 3, 4, 5} + Weak = {"lateStamp"} +INVARIANTS TypeOK ChainHeld NoLeak NoStall OwnIsGrounded +CHECK_DEADLOCK FALSE diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-weak-noLeaseCollect.cfg b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-weak-noLeaseCollect.cfg new file mode 100644 index 0000000000..9af70143df --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-weak-noLeaseCollect.cfg @@ -0,0 +1,7 @@ +SPECIFICATION Spec +CONSTANTS + MaxEpoch = 2 + Ids = {1, 2, 3, 4, 5} + Weak = {"noLeaseCollect"} +INVARIANTS TypeOK ChainHeld NoLeak NoStall OwnIsGrounded +CHECK_DEADLOCK FALSE diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-weak-ownSuperseded.cfg b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-weak-ownSuperseded.cfg new file mode 100644 index 0000000000..cf8c6e9df2 --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-weak-ownSuperseded.cfg @@ -0,0 +1,7 @@ +SPECIFICATION Spec +CONSTANTS + MaxEpoch = 2 + Ids = {1, 2, 3, 4, 5} + Weak = {"ownSuperseded"} +INVARIANTS TypeOK ChainHeld NoLeak NoStall OwnIsGrounded +CHECK_DEADLOCK FALSE diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-weak-pendingBlocks.cfg b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-weak-pendingBlocks.cfg new file mode 100644 index 0000000000..000a6427b9 --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-weak-pendingBlocks.cfg @@ -0,0 +1,7 @@ +SPECIFICATION Spec +CONSTANTS + MaxEpoch = 2 + Ids = {1, 2, 3, 4, 5} + Weak = {"pendingBlocks"} +INVARIANTS TypeOK ChainHeld NoLeak NoStall OwnIsGrounded +CHECK_DEADLOCK FALSE diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-weak-stepReleases.cfg b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-weak-stepReleases.cfg new file mode 100644 index 0000000000..5af71afbff --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run2-weak-stepReleases.cfg @@ -0,0 +1,7 @@ +SPECIFICATION Spec +CONSTANTS + MaxEpoch = 2 + Ids = {1, 2, 3, 4, 5} + Weak = {"stepReleases"} +INVARIANTS TypeOK ChainHeld NoLeak NoStall OwnIsGrounded +CHECK_DEADLOCK FALSE diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run3-safe.cfg b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run3-safe.cfg new file mode 100644 index 0000000000..86b84a6362 --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run3-safe.cfg @@ -0,0 +1,8 @@ +SPECIFICATION Spec +CONSTANTS + MaxEpoch = 2 + Ids = {1, 2, 3, 4, 5} + Weak = {} +INVARIANTS TypeOK ChainHeld NoLeak NoStall OwnIsGrounded Coherence +PROPERTIES Table +CHECK_DEADLOCK FALSE diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run3-weak-chainOwnsAfterFinish.cfg b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run3-weak-chainOwnsAfterFinish.cfg new file mode 100644 index 0000000000..2a2381721f --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run3-weak-chainOwnsAfterFinish.cfg @@ -0,0 +1,8 @@ +SPECIFICATION Spec +CONSTANTS + MaxEpoch = 2 + Ids = {1, 2, 3, 4, 5} + Weak = {"chainOwnsAfterFinish"} +INVARIANTS TypeOK ChainHeld NoLeak NoStall OwnIsGrounded Coherence +PROPERTIES Table +CHECK_DEADLOCK FALSE diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run3-weak-debtUnderCoverage.cfg b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run3-weak-debtUnderCoverage.cfg new file mode 100644 index 0000000000..0d22fde8e7 --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run3-weak-debtUnderCoverage.cfg @@ -0,0 +1,8 @@ +SPECIFICATION Spec +CONSTANTS + MaxEpoch = 2 + Ids = {1, 2, 3, 4, 5} + Weak = {"debtUnderCoverage"} +INVARIANTS TypeOK ChainHeld NoLeak NoStall OwnIsGrounded Coherence +PROPERTIES Table +CHECK_DEADLOCK FALSE diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run3-weak-noAbsorb-table.cfg b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run3-weak-noAbsorb-table.cfg new file mode 100644 index 0000000000..c7c83fff40 --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run3-weak-noAbsorb-table.cfg @@ -0,0 +1,7 @@ +SPECIFICATION Spec +CONSTANTS + MaxEpoch = 2 + Ids = {1, 2, 3, 4, 5} + Weak = {"noAbsorb"} +PROPERTIES Table +CHECK_DEADLOCK FALSE diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run3-weak-noAbsorb.cfg b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run3-weak-noAbsorb.cfg new file mode 100644 index 0000000000..cf83b76723 --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run3-weak-noAbsorb.cfg @@ -0,0 +1,8 @@ +SPECIFICATION Spec +CONSTANTS + MaxEpoch = 2 + Ids = {1, 2, 3, 4, 5} + Weak = {"noAbsorb"} +INVARIANTS TypeOK ChainHeld NoLeak NoStall OwnIsGrounded Coherence +PROPERTIES Table +CHECK_DEADLOCK FALSE diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run3-weak-replayKeepsDebt.cfg b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run3-weak-replayKeepsDebt.cfg new file mode 100644 index 0000000000..655c48619e --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/run3-weak-replayKeepsDebt.cfg @@ -0,0 +1,8 @@ +SPECIFICATION Spec +CONSTANTS + MaxEpoch = 2 + Ids = {1, 2, 3, 4, 5} + Weak = {"replayKeepsDebt"} +INVARIANTS TypeOK ChainHeld NoLeak NoStall OwnIsGrounded Coherence +PROPERTIES Table +CHECK_DEADLOCK FALSE diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/safe-patched-cases.cfg b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/safe-patched-cases.cfg new file mode 100644 index 0000000000..c4ad9171a0 --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/safe-patched-cases.cfg @@ -0,0 +1,7 @@ +SPECIFICATION Spec +CONSTANTS + MaxEpoch = 2 + Ids = {1, 2, 3, 4} + Rules = "unified" +INVARIANTS TypeOK ChainHeld NoLeak NoStrandedStaleFailure +CHECK_DEADLOCK FALSE diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/safe-unified.cfg b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/safe-unified.cfg new file mode 100644 index 0000000000..e8f2112789 --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/safe-unified.cfg @@ -0,0 +1,7 @@ +SPECIFICATION Spec +CONSTANTS + MaxEpoch = 2 + Ids = {1, 2, 3, 4} + Rules = "unifiedMin" +INVARIANTS TypeOK ChainHeld NoLeak NoStrandedStaleFailure +CHECK_DEADLOCK FALSE diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/weak-main-before-2191.cfg b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/weak-main-before-2191.cfg new file mode 100644 index 0000000000..1db0b87384 --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/weak-main-before-2191.cfg @@ -0,0 +1,7 @@ +SPECIFICATION Spec +CONSTANTS + MaxEpoch = 2 + Ids = {1, 2, 3, 4} + Rules = "current" +INVARIANTS TypeOK ChainHeld NoLeak NoStrandedStaleFailure +CHECK_DEADLOCK FALSE diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/weak-no-normalize.cfg b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/weak-no-normalize.cfg new file mode 100644 index 0000000000..80be2bf2ab --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/weak-no-normalize.cfg @@ -0,0 +1,7 @@ +SPECIFICATION Spec +CONSTANTS + MaxEpoch = 2 + Ids = {1, 2, 3, 4} + Rules = "unifiedMinNoNorm" +INVARIANTS TypeOK ChainHeld NoLeak NoStrandedStaleFailure +CHECK_DEADLOCK FALSE diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/weak-no-recovery.cfg b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/weak-no-recovery.cfg new file mode 100644 index 0000000000..ce2b299c84 --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/weak-no-recovery.cfg @@ -0,0 +1,7 @@ +SPECIFICATION Spec +CONSTANTS + MaxEpoch = 2 + Ids = {1, 2, 3, 4} + Rules = "oldRecover" +INVARIANTS TypeOK ChainHeld NoLeak NoStrandedStaleFailure +CHECK_DEADLOCK FALSE diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/weak-stale-held.cfg b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/weak-stale-held.cfg new file mode 100644 index 0000000000..c639519752 --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/weak-stale-held.cfg @@ -0,0 +1,7 @@ +SPECIFICATION Spec +CONSTANTS + MaxEpoch = 2 + Ids = {1, 2, 3, 4} + Rules = "oldLeak" +INVARIANTS TypeOK ChainHeld NoLeak NoStrandedStaleFailure +CHECK_DEADLOCK FALSE diff --git a/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/weak-stale-prefix-retires.cfg b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/weak-stale-prefix-retires.cfg new file mode 100644 index 0000000000..a793395587 --- /dev/null +++ b/docs/contributing/oracle-reviews/ordered-loader-ledger-tla/weak-stale-prefix-retires.cfg @@ -0,0 +1,7 @@ +SPECIFICATION Spec +CONSTANTS + MaxEpoch = 2 + Ids = {1, 2, 3, 4} + Rules = "oldRetire" +INVARIANTS TypeOK ChainHeld NoLeak NoStrandedStaleFailure +CHECK_DEADLOCK FALSE diff --git a/docs/contributing/oracle-reviews/perf-in-constant-set.md b/docs/contributing/oracle-reviews/perf-in-constant-set.md new file mode 100644 index 0000000000..f8d3fcc6a8 --- /dev/null +++ b/docs/contributing/oracle-reviews/perf-in-constant-set.md @@ -0,0 +1,99 @@ +# `inArray` constant-list Set oracle review + +Reviewed source head: the commit that adds this record on `perf-in-set`. The +production change is `18ea072b5`. This record adds the audit only. + +## Contract and evidence + +`inArray(value, list)` is SQL `IN` under three-valued logic. A null or +undefined value is UNKNOWN (`null`). A list that is not an array is FALSE. The +result is TRUE when some non-null list item equals the value under the +evaluator's equality, and FALSE otherwise. + +The authority is the evaluator contract in +`packages/db/src/query/compiler/evaluators.ts`: `valuesEqual` treats `NaN` and +an invalid Date as equal to each other and to nothing else. +`normalizeEqualityOperand` compares a valid Date as its millisecond timestamp, +compares byte arrays by content, and compares Temporal values by kind and +string. Every other pair compares with `===`. The existing `in` tests in +`evaluators-oracle.test.ts` and the WHERE publication oracle state the same +rules for `eq`. + +The change keeps this result and changes its cost. Join demand +(`packages/db/src/query/live/subset-demand-controller.ts`, `createSegment`) +sends one constant `inArray` list. Before the change, each row was compared +with every item, so a cold join over 10,000 rows with no source index took +about 1.1 s. Now a constant list becomes a Set of normalized keys. Byte arrays +stay out of the Set and keep their content comparison. + +The oracle is +`packages/db/tests/query/compiler/in-evaluator-oracle.property.test.ts`. Its +model, `referenceIn`, walks the list and applies `referenceEqual`, which +restates the rules above without importing any production comparison or +normalization helper. + +## Findings during the review + +- **A Buffer from another realm.** The first version of `referenceEqual` + checked bytes with `instanceof Uint8Array`. Under jsdom, Node's `Buffer` comes + from another realm, so that check returned false for a Buffer, and the + reference called a Buffer and an equal `Uint8Array` unequal. Production was + correct. The reference now checks `ArrayBuffer.isView` and the + `[object Uint8Array]` tag. +- **The byte-array work law.** The first version of the fix put every list item + in the Set through `normalizeValue`, and looked up each row value the same + way. `normalizeValue` encodes a byte array as a string, one character per + byte. The existing law in + `packages/db/tests/query/compiler/binary-equality-work.test.ts` (`compares + bytes with 'in'/... without encoding strings`) counts calls to + `String.fromCharCode` during evaluation. All 24 of its `in` cases with 129 + or 65,536 bytes failed, and so did `compares a MiB using in without caching + mutable bytes`. The + fixed version compares a byte value only with the byte items in the list, by + content, and does not encode it. + +## Mutant results + +The production source was restored after each run. + +| Mutant | Outcome | +| --- | --- | +| The Set holds raw items, not normalized keys | Assertion failure: 4 of 11 tests. | +| The row value is not normalized before the lookup | Assertion failure: 2 of 11 tests. | +| A byte value compares with byte items by reference | Assertion failure: 3 of 11 tests. | +| A byte value is looked up in the Set | Assertion failure: 3 of 11 tests. | +| Byte items are also put in the Set | Equivalent within the tested domain. A byte value never reaches the Set, so no result changes. The only difference is encoding at compile time, which the work law does not count: it passed 76 of 76. | +| (First fix) every value goes through `normalizeValue` | Caught by the byte-array work law: 25 failures. The `in` oracle passed, because the results were correct. | + +## ORC-012 requirement audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Applicable. The law and its authority are above. The claim is limited to the boolean result of `in`. WHERE publication, indexes and subset loading have other owners, which the oracle prose names. | +| ORC-002 | Applicable. `referenceIn` and `referenceEqual` are a linear walk over the stated rules. They do not import `valuesEqual`, `normalizeValue`, `isUint8Array` or any other production helper. | +| ORC-003 | Applicable. The opening prose states the contract, the model, the value domain, the production driver, the checkpoint and the limits. | +| ORC-004 | Applicable. The domain is a fixed pool of 25 values that holds the boundary of each rule. Lists have zero to six items and may repeat items. The list comes from a constant or from a row field. Pinned cases reconstruct each boundary: a Date and its timestamp, `NaN` and an invalid Date, `-0` and `0`, a BigInt and a Number, equal bytes, bytes and their internal key, a structural copy, and an empty list. The calibration test removes normalization, and the checker rejects it. | +| ORC-005 | Applicable. The driver compiles the public `in` IR with `compileExpression` and evaluates it per row, as a WHERE clause does. It evaluates one other row first, so a cached list that should not be reused is also checked. | +| ORC-006 | Applicable. The calibration test and the mutants above reach the comparison and fail with an assertion. Each outcome is classified in the table. | +| ORC-007 | Applicable. The same property and budget run twice: once with fixed seed `0x2029`, and once with an unseeded or replay seed through `oraclePropertyOptions`. The property is registered as `evaluators.in`, so `TANSTACK_DB_ORACLE_SEED`, `TANSTACK_DB_ORACLE_PATH` and `TANSTACK_DB_ORACLE_PROPERTY` replay a reported failure directly. The property does not use `fc.commands`. | +| ORC-008 | Inapplicable. The model is a stateless function of the value and the list. | +| ORC-009 | Applicable. The terms match production: "UNKNOWN" is the `null` result, and "normalized key" is the value that `normalizeValue` returns. | +| ORC-010 | Applicable. The check throws one error that names the expected and actual results and the case. It has no cleanup step, so no cleanup error can replace it. fast-check reports the shrunk counterexample with its seed and path. | +| ORC-011 | Applicable and satisfied. The byte-array work law is a second, different formulation. It measures the work of `in` and not its result, and it caught the first fix that the result oracle accepted. | +| ORC-013 | Inapplicable. The oracle protects no threshold or range law. | +| ORC-014 | Inapplicable. No controlled provider supplies a premise. | + +## Performance evidence + +The repo benchmark (`scripts/bench/incremental-update.ts`) ran three +interleaved times on `main` and on the branch. These are median cold hydrate +times with no source index: + +| Query, 10,000 rows | `main` | Branch | +| --- | --- | --- | +| list + author | 1,101–1,221 ms | 79–87 ms | +| list + comment count | 1,024–1,102 ms | 99–112 ms | +| list + 3 recent comments | 1,004–1,019 ms | 91–94 ms | + +Median write times did not change beyond noise. Two of the three comparisons +were 1.00× and 0.98×, and a same-code comparison was 0.96×. diff --git a/docs/contributing/oracle-reviews/perf-lazy-bucket-rollback-copy.md b/docs/contributing/oracle-reviews/perf-lazy-bucket-rollback-copy.md new file mode 100644 index 0000000000..ee2b549768 --- /dev/null +++ b/docs/contributing/oracle-reviews/perf-lazy-bucket-rollback-copy.md @@ -0,0 +1,370 @@ +# Lazy bucket rollback copy review + +Evidence by revision, on `perf-lazy-bucket-snapshot` from `main` at +`2c98b4992`: the RED results ran on `main` with the new oracle. The GREEN +results, mutants and measurements ran on the branch commit that adds this +record. The layout law, the ORC-010 harness and the second mutant run were +added in a follow-up commit; "Follow-up: layout and cleanup" gives their +results. + +## Contract and evidence + +`BucketFacadeAdapter.flush()` writes child-facade rows through ordinary +Collection transactions and defers their events. "Coherent publication" in +`packages/db/src/query/live/ARCHITECTURE.md` requires the flush to be atomic: +if a facade write or the root commit fails, every facade returns to its rows, +order and key mapping from before the flush, and no facade publishes. + +Before this change, each flush copied the stored rows of every facade, so that +a rollback could restore them. The rollback read only the copies of the +facades the flush wrote. Every successful flush paid for the other copies. A +change to one child row in a list of 50 parents with 3 rows each copied 150 +rows. + +The change copies a facade's rows when the flush first defers that facade's +publication. Both write paths defer before they open a sync transaction, so +the copy holds the facade's rows from before the flush. The production diff in +`bucket-facade-adapter.ts` is +19/−29. + +The new owner is +`packages/db/tests/query/bucket-facade-rollback-oracle.property.test.ts`. Its +model maps each bucket to its ordered rows and keeps the operations sent since +the last successful flush. It checks two laws after every flush: + +1. **Flush atomicity.** After a thrown facade commit or a rollback after + `prepare()`, each facade shows its published rows in order, each row's key + resolves through `getKeyFromItem`, and no facade published an event. A + thrown flush keeps its graph output pending, and the next successful flush + applies it. +2. **Bounded rollback work.** During a flush, the stored rows of no facade + outside the written buckets are read. +3. **Layout.** A facade is a Collection-valued include, so an order-only + change of a row it shows is a layout change ("Inline modes" in the + architecture document). After a successful flush, a facade's layout + revision advances once when a row it showed before and after the flush + changed order and its key sequence changed, and stays put otherwise. A + failed flush leaves the revision unchanged. + +## Results + +| Check | `main` | Branch | +| --- | --- | --- | +| Fixed campaign (seed 2081) | Fails: "step 1: reads of untouched b0: expected 1 to be +0" | Passes | +| Random campaign | Fails with the same counterexample | Passes | +| Pinned wide list (50 buckets × 3 rows, one insert) | Fails: 49 untouched facades read | Passes | +| Pinned retried failure (two written buckets and a retire) | Passes | Passes | + +The generated histories reached 448 publishes, 188 rollbacks after `prepare()` +and 84 thrown facade commits. + +Rows copied per change, measured with a counter on the copy: + +| Shape | `main` | Branch | +| --- | --- | --- | +| Issue detail, 10 / 100 / 1,000 comments | 19.5 / 109.5 / 1,009.5 | 19.5 / 109.5 / 1,009.5 | +| List of 50 issues, 3 recent comments each | 150 | 3 | +| All issues, 100 × 10 comments | 1,009.5 | 19.5 | +| All issues, 1,000 × 10 comments | 10,009.5 | 19.5 | + +The detail view does not improve, because its one bucket is the bucket the +flush writes. An undo log of changed keys would cover that case. + +Local `scripts/bench` timings could not distinguish the change: a same-code +comparison gave 1.08× for writes, and reversing the run order moved the +branch from 1.03–1.14× to 0.83×. + +## Mutants + +| Mutant | Outcome | +| --- | --- | +| Copy the rows after the first write | Assertion failure: the new oracle's fixed and random campaigns, and the existing "restores facade state without public effects when a flush fails" test | +| Skip the copy for buckets created in the flush | Equivalent within the tested domain: `restore()` restores only facades that existed before the flush | +| Retire copies after its deletes | Equivalent within legal histories: the graph retracts a retired bucket's rows in the same flush, so the row phase defers and copies the facade first | +| Do not restore `currentOrder` | Assertion failure in the new oracle (fixed and random campaigns, and the pinned reorder retry) and in the existing `bucket-facade-adapter.test.ts` rollback test | + +## ORC-012 requirement audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Applicable. The atomicity law comes from "Coherent publication". The work law is this change's promise. The claim is limited to one ordered edge at the adapter boundary. | +| ORC-002 | Applicable. The model is a map of ordered rows and a list of pending operations. It does not import the adapter, its snapshot or its order maps. | +| ORC-003 | Applicable. The opening prose states both laws, the model, the grammar, the driver, the checkpoints and the limits. | +| ORC-004 | Applicable. The grammar covers activate, insert, update, delete and retire on buckets `b0`..`b3` and ids 1..6, with publish, thrown commit and rollback outcomes. Operations are legal against the published rows plus pending operations. The outcome counts above show each outcome was reached. | +| ORC-005 | Applicable. The driver runs the real adapter over a D2 graph and compares public facade rows, keys, events and each facade's layout revision, which the live-query observer compares to detect a reorder. The work counter wraps each facade's stored-row iterators during the flush. | +| ORC-006 | Applicable. The mutants above, with outcome classes. | +| ORC-007 | Applicable. A fixed-seed campaign and a random or replayed campaign run the same property and budget, registered as `bucket-facade.rollback-history`. | +| ORC-008 | Applicable. The model keeps published rows and pending operations. Pending operations are needed because a thrown flush keeps its graph output for the next flush. | +| ORC-009 | Applicable. "Written buckets" means buckets with a pending row operation or a retire since the last successful flush. | +| ORC-010 | Applicable. `withCleanup` keeps a history's failure as the primary error: when cleanup also throws, it throws an `AggregateError` whose `cause` is the history's failure and whose `errors` list that failure first. A calibration test forces both failures; a harness mutant that rethrows the cleanup error fails it. | +| ORC-011 | Inapplicable. No shared fault between the model and the adapter was named. | +| ORC-013 | Inapplicable. No threshold law. | +| ORC-014 | Inapplicable. No controlled provider. | + +## Unresolved + +- Nested facades and facade indexes are covered only by the existing tests in + `bucket-facade-adapter.test.ts` and `includes-collection-oracle.property.test.ts`. +- The layout law observes the layout revision, not layout-only listener + calls. A layout listener runs only for a publication without row events, and + an order-only update publishes an update event. +- Dense single-bucket views still copy the whole written bucket. + +## Follow-up: layout and cleanup + +The first version of the oracle did not observe a reorder. `toArray` sorts +through the facade comparator, so the rows came back in order even when the +adapter kept stale order state after a failed flush. The stale state stops +the retried flush from marking a layout change, which the live-query observer +reads through the layout revision. + +A first attempt observed layout listener calls and failed on the fixed adapter +(`step 1: layout publications of b0: expected +0 to be 1`): an order-only +update publishes an update event, and layout listeners run only for a +publication without row events. The revision is the observation the observer +uses, so the law observes it. + +| Check | Result | +| --- | --- | +| Fixed adapter | 6 of 6 pass, no type errors | +| Do not restore `currentOrder` | Assertion failures in both campaigns and the pinned reorder retry. The fixed campaign also found a row-order failure after a second thrown flush: `[1, 3]` instead of `[3, 1]`. | +| Copy the rows after the first write | Assertion failures, 3 of 6 tests | +| Skip the copy for new buckets; retire copies after its deletes | Still equivalent, as above | +| Harness mutant: rethrow the cleanup error | The ORC-010 calibration fails | + +## Follow-up: medium code review + +Reviewed source: the commit that adds this section, on top of `3b9b74ba2`. +The review raised 10 findings. The evaluation ledger is outside the +repository; its verdicts are below. + +**Laws.** The oracle now states three laws. Atomicity also requires that a +failed flush removes the facades it created. Bounded work now covers every +read path of the stored rows. The layout law is now derived from the rows a +reader sees, not from production's classifier: a revision is required when +the rows shown before and after appear in a different relative order, +forbidden when the shown key sequence is identical, and allowed otherwise. + +| Finding | Verdict | Change | +| --- | --- | --- | +| 1. Work counter did not see `forEach` | Confirmed | The counter also wraps `forEach`, `get` and `has`. | +| 2. Layout model copied the production classifier | Confirmed | Three-part layout law above. | +| 3. Nested-facade rollback was claimed covered but was not | Confirmed | Pinned case: the child edge commits, the parent edge throws. The child facade is restored, the parent keeps its reference to it, and the child facade created by the flush is removed. | +| 4. Missing failure cuts | Confirmed | The grammar adds a throw from a facade's first write (open sync transaction) and a throw from the commit of a facade the flush creates. The nested case covers a second edge after the first commits. A retire on a non-empty facade cannot occur in legal histories (see mutants). | +| 5. `check()` skipped buckets the model lacks | Confirmed | `check()` compares the adapter's facade set with the model's buckets. | +| 6. A rollback after `prepare()` ended the history | Confirmed | The history continues after a rollback; a pinned case sends a rolled-back reorder again. | +| 7. `?? []` could restore a facade to empty | Confirmed | `restore()` iterates the copied rows, so a missing copy cannot occur. | +| 8. `deferredEntries` duplicated the copied rows' keys | Confirmed | Removed. | +| 9. Copy-before-write was a convention | Confirmed | One `write()` helper copies, defers, begins and commits for both write paths. | +| 10. `snapshot()` still copies the edge and bucket maps | Confirmed, scoped | Not changed. A lazy copy would need a copy-on-write hook across the window from `flush()` to `publish()`/`rollback()`, including `resolve()` calls after `flush()` returns. The changeset now says the bucket bookkeeping still grows with the number of buckets. | + +**Mutants** (ORC-006), run against this oracle and against the previous one: + +| Mutant | This oracle | Previous oracle | +| --- | --- | --- | +| Eager copy through `forEach` | Assertion failure | Survived | +| Layout revision without a key change | Assertion failure | Assertion failure | +| No layout revision when membership also changes | Assertion failure | Assertion failure | +| Layout from common-row relative order only | Survives: the contract allows it | Assertion failure: the old model over-specified | +| Entries map not restored | Assertion failure | Assertion failure | +| Stale order state after a rollback | Assertion failure | Survived | +| Rows copied after the first write | Assertion failure | Assertion failure | +| Retired facade copied after its deletes | Equivalent: a retired bucket's rows retract in the same flush, so retire finds an empty facade | Equivalent | + +**Production delta:** `bucket-facade-adapter.ts` is now +41/−54 against +`main`, net −13. + +**Unresolved:** the random campaign rarely sends a rolled-back reorder again; +the pinned case covers it. The edge and bucket map copy is not measured. + +## Follow-up: child rows lost after a failed root commit + +Found while evaluating the medium review, and confirmed on `main` +(`f6d65eace`) and on this branch before the fix. + +**Law.** After a failed root commit, the next successful flush publishes every +change that was pending at the failed flush, child rows included, exactly once. +The live-query builder keeps its pending root changes when the root commit +fails. Before this fix, the facade adapter had already cleared its pending +child rows and activations, so `rollback()` restored the facades but lost +their pending changes. + +**Witness.** A live query with a Collection-valued include. One write updates +the parent and the child, and the root commit throws. A retry writes only a +further parent change. On `main` and on the unfixed branch, the parent shows +the retry while the child keeps `value: 1` and publishes no event, although the +source holds `value: 2`. With the fix, the child shows `value: 2` and publishes +one event. + +**Oracle.** The rollback outcome now keeps the model's operations pending, as +a thrown flush does. RED before the fix: both campaigns failed with "facades +held by the adapter: expected [] to deeply equal [ 'b0' ]" (a lost +activation), and a pinned case failed with "rows of b0: expected [ { id: 1, v: +1 } ] to deeply equal [ { id: 1, v: 2 } ]". The includes owner gained the +public witness above. + +**Fix.** `flush()` keeps the pending rows and activations it consumed; +`rollback()` puts them back. No graph output arrives between a flush and its +rollback, so the maps are empty when it does. + +**Mutant.** Dropping the pending rows on rollback fails both campaigns, the +pinned case, and the includes witness (assertion failures). + +**Not covered by the law.** When no further write reaches the live query after +a failed root commit, no flush runs, so the root and the child both keep their +rows from before the failure until the next write. Whether a failed commit +should schedule its own retry is a design question; this change does not add +one. + +## Recorded limits (maintainer decision, 2026-10-08) + +**Bucket bookkeeping copy (finding 10).** `snapshot()` still copies each edge's +`entries` map and `activeBuckets` set on every flush. The maintainer accepted +this as a documented limit. Measured per comment write, with a scratch variant +that skips the copy (an upper bound, because that variant cannot roll back): + +| Unlimited include | Entries copied | Copy share of a write | +| --- | ---: | ---: | +| 10 parents | 20 | about 2% | +| 100 parents | 200 | about 6% | +| 1,000 parents | 2,000 | about 31% | +| 10,000 parents | 20,000 | about 83% | + +A limited query ("list + 3 recent comments", limit 50) copies no entries, +because only the parents in its window hold facades. A lazy copy for each edge +does not help, because one edge holds all the buckets. The follow-up, if large +unlimited include queries matter, is a per-bucket undo log (about +20/−15 +lines plus oracle cases for activate, retire and restore of a new entry). Raw +data: `~/.cw-perf/bucket-f10/RESULTS.md` (local). + +**Retry after a failed root commit.** The maintainer recorded this as a design +question for later. A retry needs a public contract, a backoff and a stop +condition before it can earn its code weight. + +## Follow-up: second medium code review (2026-10-08) + +Reviewed head `bf6caffb1`. The review had nine findings. The evidence for each one is below. + +| Finding | Verdict | Change | +| --- | --- | --- | +| 1. Successful flushes do not check events | Confirmed | Law 1 now checks events on each successful flush. The subscriber starts from the facade's current rows (`includeInitialState`). Its events name only rows that a pending operation touched, at most once each. A replay of the events on the previous rows gives the shown rows. | +| 2. `ARCHITECTURE.md` says the adapter buffers no deltas | Confirmed | The adapter paragraph and "Coherent publication" state the retention law and the rollback invariant. | +| 3. `rollback()` can overwrite new pending deltas | Confirmed as an unchecked invariant | `rollback()` throws (code 235) when graph output arrived after the flush. The flush runs inside the graph run, so this cannot occur in a legal history. A pinned witness covers it. | +| 4. Per-flush bookkeeping copy | Accepted design | The maintainer decision is recorded below. | +| 5. Per-flush `write` closure | Confirmed | Removed. The copy, the deferral and the write are at the one write site. | +| 6. The retire write branch is unreachable | Confirmed, then refuted in the third review | The branch was removed, and a retire that found rows threw (code 231). A probe that threw on that branch ran the full `@tanstack/db` suite (11,267 tests) and reached it zero times. The third review showed that the probe measured a blind spot shared by every suite, not the contract. The branch is restored. | +| 7. Pending maps are copied, then cleared | Confirmed | The flush swaps the map references, and the rollback swaps them back. | +| 8. The oracle reads adapter internals | Confirmed, documented | The Limits section gives the reason: `resolve` creates facades, so the facade set and the new-facade failure need the private map and factory. Rows, events and layout revisions are public Collection state. | +| 9. The "only way to write a facade" comment is wrong | Confirmed | The comment now says this is the only place where a flush applies graph deltas. | + +An earlier draft of the event law subscribed without initial state. It reported missing delete events after a thrown flush. That was a wrong observation, not a product bug. A subscriber without initial state does not receive events for rows that it was never sent, and it receives an insert for an update to such a row. The same history gives the same events on `main`. + +Mutants, against the extended oracle (with the includes oracle and the adapter tests) and against the oracle at `bf6caffb1`: + +| Mutant | Extended oracle | Previous oracle | +| --- | --- | --- | +| Apply the consumed deltas again after publish | Assertion failure (20 tests) | Assertion failure (4 tests), through the work law | +| Drop the events of a delete-only facade write | Assertion failure at the successful flush (step 1) | Assertion failure only at a later rollback | +| Rollback restores over new pending deltas | Assertion failure (witness) | Survives | +| Retire deletes rows and does not throw | Assertion failure (witness) | Survives | +| Copy rows after the first write | Assertion failure (12 tests) | Assertion failure (7 tests) | +| Apply each change twice | Equivalent: the repeated identical update publishes no event | Equivalent | + +Production: `bucket-facade-adapter.ts` is +54/−64 against `main` (net −10). + +## Follow-up: third, targeted code review (2026-10-08) + +Reviewed head `00ef3ea07`. The review had ten findings. + +**Regression.** Round 2 made retirement throw (code 231) when a facade still +had rows. It assumed that a retired facade is empty in legal histories. That +holds for synced rows only. A facade is a public Collection, so a user +transaction can show an optimistic row in it, and a persisting transaction can +hold its sync commits. `main` retracted every visible key. The oracle's grammar +had no step that writes a facade outside the graph, so no generated history +could refute the assumption. The reach probe counted only what the existing +suites generate. + +**Law.** Retirement deletes through sync every key the facade still shows. It +is a facade write of the flush, so a failed flush restores it. The +architecture text says so and explains why a non-empty retirement is legal. + +| Finding | Verdict | Change | +| --- | --- | --- | +| 1. Retire throws on a facade with optimistic or held rows | Confirmed regression | The grammar gained a step that inserts an optimistic row into a held facade, and the oracle checks that the row stays visible. RED: both campaigns, plus pinned optimistic-row and held-commit cases, threw code 231. GREEN after retirement went back to retracting through the shared copy and deferral path. Code 231 is removed. | +| 2. Created-facade disposal is not observed | Confirmed | After a failed flush, every facade it created must report `status === 'cleaned-up'`. | +| 3. The rollback invariant wedges event delivery | Confirmed | The rollback restores the facades and discards the deferred events before it throws. The witness checks the rows and one event on the next flush. | +| 4. Rollback after cleanup restores disposed facades | Confirmed | Rollback after `cleanup()` does nothing. Pinned witness. | +| 5. The architecture text does not match the retire throw | Confirmed | Updated, as above. Coherent publication also states the rollback ordering and the cleanup no-op. | +| 6. The layout law over-specifies | Open law question | Proposed to the maintainer. | +| 7. No liveness after a deterministic root-commit failure | Recorded limit | See the recorded limits above. | +| 8. `currentOrder.clear()` in restore is redundant | Refuted | Removing it fails the adapter unit test. Only a private-state assertion catches it, so its public consequence is an open witness. | +| 9. `retiredEntries.clear()` on rollback is redundant | Equivalent | Removing it passes all 4,033 query tests. Restore puts each retired entry back, so the reference retains nothing. Kept, because it states that a rolled-back flush retires nothing. | +| 10. Misplaced comments | Confirmed | Moved. | + +On merge, `main` (#2074) had taken codes 230–232, so the rollback invariant is +now code 235. (#2080 holds 233; it later dropped 234.) + +Mutants, against the rollback oracle, the includes oracle and the adapter +tests: + +| Mutant | Result | +| --- | --- | +| Retire throws when rows remain | Assertion failures (6 tests, both campaigns) | +| Created facades are not disposed | Assertion failures (both campaigns) | +| Rollback after cleanup restores | Assertion failure (witness) | +| The invariant check runs before restore and discard | Assertion failure (witness) | +| Copy again on each write | Assertion failures (both campaigns) | +| Retire skips the rollback copy | Assertion failure (pinned witness only) | +| Rollback drops the consumed pending changes | Assertion failures (4 tests, includes oracle too) | + +**Limits.** Held commits and synced rows left at retirement have pinned +witnesses only. The second is a contradictory graph signal. Disposal between a +flush and its rollback is pinned, not generated. + +## Follow-up: fourth medium code review (2026-10-09) + +Reviewed head `c69662d3b`. The review had ten findings. Each was checked with +an executable probe on that head before any change. + +**Falsified law.** Flush atomicity: after a failed flush, every facade shows +its rows from before the flush until the next successful flush. A persisting +user transaction on a facade holds its sync commits. The failed flush's insert +of a new key is then not in the stored rows, so `restore()` sent no delete for +it. When the transaction settled, the row appeared and published an insert +event, before any retry. `main` has the same restore loop. Updates, deletes and +retirement under a hold were already restored. + +**Why the oracle missed it.** The grammar's optimistic rows came from a +transaction that stays pending, which does not hold sync commits. Held commits +had pinned witnesses only, and the held-commit witness did not check the +facade after the transaction settled. The new `hold` step field holds one +shown facade's commits during a flush and settles before the comparison. + +RED on `c69662d3b`: both campaigns fail at `step 1 (rollback): rows of b0` +(fixed seed) and `step 2 (throwNew): rows of b0` (random seed). GREEN after +the fix. + +| Finding | Verdict | Change | +| --- | --- | --- | +| 1. Rollback misses held sync commits | Confirmed, narrower than stated | The leak is a transient before the retry; a retry writes the key again or deletes it. `restore()` now also deletes every key the flush wrote. | +| 2. Retirement misses held rows | Refuted on legal histories | The graph retracts every row it sent, including held ones, and those deletes are held in order. A probe of retire under a hold, settled after publish, ends empty. Rows the graph never retracted are finding 7. | +| 3. The code-235 error masks the root error and drops deltas | Accepted | It fires only on an invariant violation, which fails loud by design (AGENTS.md fail-fast). Precise errors for such failures are not required (maintainer decision, 2026-10-09). | +| 4. Abort is not exception-safe | Confirmed | One `abort()` restores, then discards every deferral in a `finally`. Pinned witness: a restore whose commit throws, then a later flush publishes. | +| 5. Readiness survives rollback | Confirmed state, low reach | A facade that `resolve()` created before its bucket activates stays ready and empty after a failed flush. In the builder, only an old `previousValue` can create such a facade. Open design question. | +| 6. One throwing subscriber stops other facades | Confirmed | `publish()` publishes every facade, then rethrows the first error. Pinned witness. | +| 7. Leftover synced rows at retirement are retracted silently | Open design question | Already on the maintainer's decision list. | +| 8. Every flush copies the bookkeeping maps | Accepted limit, plus a fix | The per-flush copy is a recorded limit. A flush with no facade changes now returns before it copies anything. Pinned work witness. | +| 9. The abort sequence is duplicated | Confirmed | Merged into `abort()`, as in finding 4. | +| 10. Restore deletes by visible keys | Confirmed | Under a hold, the delete of the transaction's optimistic key published a layout revision after a failed flush. `restore()` now reads the stored rows. | + +| Mutant | Result | +| --- | --- | +| Restore ignores the keys the flush wrote | Both campaigns fail | +| `abort()` without `finally` | Restore-throws witness fails | +| `publish()` stops at the first throwing facade | Subscriber witness fails | +| Snapshot on every flush | Work witness fails | +| Restore deletes by visible keys | Both campaigns fail (layout revision after rollback) | +| Restore picks insert or update by visible rows | Survives: no generated history deletes a synced row optimistically | + +**Limits.** One hold per step, settled before the comparison. A hold that +stays open across several flushes is not generated. diff --git a/docs/contributing/oracle-reviews/perf-ordered-window-repair.md b/docs/contributing/oracle-reviews/perf-ordered-window-repair.md new file mode 100644 index 0000000000..55df56661e --- /dev/null +++ b/docs/contributing/oracle-reviews/perf-ordered-window-repair.md @@ -0,0 +1,196 @@ +# Eager indexed ordered-window repair oracle review + +Reviewed source head: `ff731ad1c` on `perf-ordered-loader`, with the perf fix +in `44c90e0ad` and the null-ordering fix in `ff731ad1c`. The base is +`65992aacd` (`main` with #1650). + +## Contract and evidence + +An ordered, limited live query over an eager source with an index on its +leading order term must show the first `limit` rows after `offset` of the +eligible source rows after every change. The authority is +`packages/db/src/query/live/ARCHITECTURE.md`, "Ordered requests, continuation, +and recovery" and "Atomic window publication", and the documented order in +`docs/guides/live-queries.md`: nulls first by default, NaN greater than every +non-null value, ties by key. + +Work is a separate law. With an index, the source delivers at most +`2 * (offset + limit) + tied rows + 1` rows per change. That is one reacquired +prefix of the window, the tie group at its boundary, one refill of at most the +window, and the changed row. The bound does not grow with source size. Each +generated source holds 200 eligible filler rows outside every window, so a +read that resends every matching row exceeds it. A NaN or null boundary cannot +be expressed as a cursor (`canExpressCursorOrder`), so the documented +full-source fallback applies and only the rows law is checked while such a +rank is present. An unindexed source also falls back to full source. + +The owner is the "eager indexed ordered windows" section of +`packages/db/tests/query/ordered-work-oracle.property.test.ts`. Its model is a +plain array sorted by the documented order. It shares no code with the loader, +the comparator, or the index. + +### Original failure + +On `65992aacd`, a visible delete or an eligibility change delivered 202 to 206 +rows against a bound of 5 to 8: "rows delivered by step 1: expected 202 to be +less than or equal to 5". The ordered-prefix repair's local read was +predicate-only, so it resent every matching row. In the rindle "list: newest +50 open" case this cost 1.107 ms per insert/delete pair at 10,000 issues. + +### Repair + +- `requestSnapshot` reads only the first `limit` matching local rows when the + subscription has an order index, the request has `orderBy` and `limit`, and + the source is eager. An on-demand source keeps the full delivery that its + repair chain relies on. +- An eager source's repair may settle synchronously, so its tie and refill + steps finish inside one graph run. Without this, widening a window after a + repair published twice: once after the tie request and again after the + refill. That broke two pagination-oracle laws ("keeps a NaN window coherent + through equal and changed source ranks" and "rebuilds the full boundary when + widening after an out-of-window rank update"). + +### Alternative (b), considered and rejected + +Moving the bounded read into `loadOrderedPrefixRepair` through +`requestLimitedSnapshot` gave 2.28 ms per pair, slower than `main`. The +profile showed `ascComparator` at 27%, `getPairOrNextLower` at 25%, and the +BTree index's take loop at 18.5% self time. The cause is semantic, not per-call +setup: `requestLimitedSnapshot` skips keys already sent to the query, so after +a repair it walks past the whole visible window, one tree descent per step, +and loads the next `limit` rows instead of resending the prefix. That also +grows the query's private state on every change. + +### Null ordering + +`findIndexForField` served a descending, nulls-first query with an ascending, +nulls-first index wrapped in `ReverseIndex`. Reversing the index moved its null +group to the end. On `44c90e0ad` the pinned case failed with "expected [ 1000 ] +to deeply equal [ +0 ]", and both campaigns failed. `ReverseIndex` now takes the +requested null placement and reads the null group from that end. + +## Mutant results + +| Mutant | Outcome | +| --------------------------------------------------- | ---------------------------------------------------------------------------- | +| Original predicate-only repair read (`65992aacd`) | Assertion failure, work law: 202 > 5. | +| Ordered read without its limit (M3) | Assertion failure, work law: 202 > 5. | +| Bounded read for on-demand sources too (M4) | Assertion failure in 2 pagination-oracle cases. | +| Never repair after a visible change (M5) | Assertion failure, rows law: `expected [ 2 ] to deeply equal [ 1 ]`. | +| Read `limit + 1` rows | Assertion failure, work law: 4 > 3. | +| Read `limit - 1` rows (M1) | Survives. Equivalent at the public observation, see below. | +| Read in the reversed direction (M2) | Survives. Equivalent at the public observation, see below. | +| Eager repair stays asynchronous | Assertion failure in the same 2 pagination-oracle cases (split publication). | +| `ReverseIndex` ignores the requested null placement | Assertion failure in 3 of 4 tests, rows law. | + +M1 and M2 deliver no more rows than the bound allows, and the refill and +tie-boundary chain still reaches the correct window. A tighter work bound +cannot reject them, because they deliver the same number of rows or fewer. +The extra refill request they cause is an internal request count, not a public +observation of this law. Both are equivalent within the tested domain. + +## Benchmark + +`scripts/bench/incremental-update.ts` gains a "list: newest 50 open" case with +a visible issue delete. With an index at 10,000 issues, its median per-write +latency is 0.63–0.70 ms on `main` and 0.17–0.18 ms with the fix. Without an +index it stays near 4 ms, because the full-source path is unchanged. Overall +geomean is 0.95–0.96× against `main`, with a same-code noise run of 1.00×. + +## ORC-012 requirement audit + +| Requirement | Outcome | +| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| ORC-001 | Applicable. The rows and work laws and their authority are above. The claim covers eager sources only. On-demand providers keep their own work laws in this owner. | +| ORC-002 | Applicable. The model sorts plain rows by the documented order with its own comparator. It does not import the loader, the index, or `makeComparator`. | +| ORC-003 | Applicable. The section's opening prose states both laws, the bound's derivation, the grammar, the production path, the checkpoint and the limits. | +| ORC-004 | Applicable. The grammar crosses sync and local-only writes, index present or absent, both directions, one or two order terms, limit 1–3, offset 0–1, and upserts and deletes over ids 0–29 with ranks 0–15, NaN and null. The pinned delete case and the pinned null case reconstruct the reported traces. The work law skips only steps whose boundary value is NaN or null; it asserts on 88.5% of indexed single-term steps. The reversed-index oracle covers `nulls: 'last'`. Excluded: on-demand sources and custom collation. | +| ORC-005 | Applicable. The driver runs a real live-query Collection over a real eager Collection. It counts rows a snapshot returns from `currentStateAsChanges`, rows the source delivers to the query's subscription (which include index refills), and rows `entries()` yields. | +| ORC-006 | Applicable. The original code and the mutants above reach the checkpoint. Each outcome is classified. | +| ORC-007 | Applicable. Each campaign runs with fixed seed 2044 and with a random or replay seed through `oracleRandomParameters`, property `ordered-work.eager-indexed-window`. | +| ORC-008 | Applicable. The model keeps only the current rows. Order is derived from them. | +| ORC-009 | Applicable. "Read rows", "delivered rows" and "scanned rows" are work observations, not production states. | +| ORC-010 | Applicable, with a gap. Cleanup runs in a `finally` block, so a cleanup error after an assertion failure would replace it. No run showed a cleanup failure. | +| ORC-011 | Inapplicable. No reviewer named a fault shared by the model and production. | +| ORC-012 | This record. | +| ORC-013 | Applicable. The work bound is a threshold law. The `limit + 1` mutant exceeds it by one row (4 > 3). | +| ORC-014 | Inapplicable. No controlled provider supplies a premise. | + +## Unresolved + +- Per-change work for on-demand sources is not bounded by this owner. +- A reversed read gathers the whole nullish group on each read: it builds a + set of the nullish keys, sorts them, and calls the filter once on each. So a + read costs O(m log m) in the nullish group size m, even when it returns no + nullish key. The work law bounds filter calls on non-null keys by `n` and + allows one filter call per nullish key. Large nullish groups are rare in + ordered windows, so the reader keeps this cost for simpler code. The + bounded nullish read of `d2bb4c3f0` was replaced by this simpler reader in + the follow-up commit; a later index-API change that removed per-read group + sorts was reverted (`e0ab7e0f5`). Reads of equal non-null values still sort + each group, as on `main`. +- An initial full-source ordered load over an eager source stays asynchronous, + as on `main`. #1896 states a general rule for synchronous results, so this + is a likely gap; it is the next change, not this one. + +## Review follow-up (2026-10-06) + +A code review of this branch found eight issues. The probes, the RED output +and the mutant runs are in the PR evidence; this section records the outcomes. + +| Finding | Outcome | +| ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| R1: a reversed index sorted and filtered every nullish key on each read | Fixed. The new reversed-index oracle (`reverse-index-oracle.property.test.ts`) bounds filter calls by the position of the n-th accepted key. RED on the branch: 5,000 filter calls for n = 50 among 5,000 nullish keys. A follow-up simplified the reader: each read now gathers the nullish group, and the law allows one filter call per nullish key while still bounding non-null filter calls by `n` (see Unresolved). A follow-up (`afe19e442`) also removed the per-read sort of an equal-value group, through a new index API and write-path upkeep. It was reverted in `e0ab7e0f5`: that sort is old behavior on `main` and not part of this regression, so the change did not earn its code weight. See Unresolved. | +| R2: a second order term took the bounded read into a full in-memory sort | Fixed. The bounded read applies only to one order term. The eager-window oracle gains a second order term and a scan law (one pass per change). RED: 400 rows scanned against a bound of 201. Removing the gate fails it. | +| R3: ARCHITECTURE.md contradicted the synchronous repair | Fixed. Both passages now name the eager ordered-prefix exception. | +| R4: the synchronous gate also matched full-source requests | Fixed by narrowing the gate. A lifecycle witness checks that an eager `fn.where` ordered window is `loading` at creation; it fails on the widened gate and passes on `main`. | +| R5: index refills were not counted | Fixed. The oracle also counts rows delivered to the subscription and bounds the initial load. The unbounded-refill mutant now fails (200 > 3); before, the refill ran only during the initial load, which the law did not bound. | +| R6: the work law skipped every history with a NaN or null rank | Fixed. It skips only an inexpressible boundary and counts tie groups at the boundary, over a larger domain. A mutant that resends about 20 domain rows fails (23 > 3). | +| R7: the reader returns two tie-key orders | Documented, not changed. The query breaks ties by ascending key, so the reviewer's fix would put nullish keys in the wrong order. The reversed-index oracle states the reader's order. | +| R8: `sourceHoldsAllRows` was optional and always set | Fixed. It is required. | +| R10: a `null` cursor in a nulls-first reversed read | Refuted. A cursor at `null` is past the nullish group, so the non-null keys are the correct result. The order law passed on the original code at 20× runs. | + +After these fixes, the original predicate-only read, `limit + 1`, the +resend-domain mutant and the unbounded refill all fail. `limit - 1` (M1) +still survives, as above. + +## Second review follow-up (2026-10-06) + +A high-effort code review of `308bb12b9` raised nine findings. Each was +checked with a probe on this branch and on `main`, or with a mutant. + +| Finding | Outcome | +| --------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1: a synchronous prefix repair holds no publication gate, so a later asynchronous step could publish a partial window | Refuted for both named paths. A repair whose boundary enters a NaN tie group falls back to a full-source read (204 rows delivered) and publishes `[61,62]` once, as on `main`. A blocked LEFT-joined filter publishes nothing while blocked and `[2,3]` once after release, as on `main`. The eager-window oracle now asserts one publication per change whose window equals the model, and pins the NaN tie-group repair. A mutant with `main`'s asynchronous gate also passes this law: single-change histories publish once under both designs, and the pagination laws own window moves. | +| 2: `ReverseIndex` requires `nullsFirst` | Low. `ReverseIndex` is a read-time wrapper that `findIndexForField` builds; no caller outside this package constructs it. The parameter then defaulted to `false`; the external review below showed that default was wrong for a nulls-last index, and omission now means plain reversal. | +| 3: a bounded read skips stale rows kept after a failed truncate replay | Refuted. An eager collection never calls `loadSubset`, so it has no replay demand to fail (the probe recorded no calls). Source cleanup puts every dependent live query in its terminal error, on this branch and on `main`. | +| 4: two-term and unindexed eager repairs settled synchronously | Fixed. The loader oracle pins that an eager prefix repair settles synchronously only for a bounded read. RED on `308bb12b9`: the two-term and unindexed cases settled before `loadMore` returned. The gate now uses the same bounded-read fact as the subscription; the old gate fails 2 cases. | +| 5: the source-holds-all-rows rule was coded twice | Fixed. The loader computes the bounded-read fact once and passes it to `requestSnapshot`, which no longer checks the sync mode, the index or the term count. A subscription that ignores the flag reads 206 rows where the bound is 5. | +| 6: the bounded read repeats index discovery and could fall back to a full sort | Partly confirmed. Index discovery per repair costs one scan of the collection's index list and is kept. A silent fall back to a full sort was not caught: the scan law allowed one pass. A bounded step must now scan no more rows than its work bound; a mutant that disables the index read in `getOrderedKeys` fails (406 > 204). | +| 7: each reversed read gathers the nullish group | Accepted design (see Unresolved). | +| 8: two tie-key orders cut through a NaN tie group | Duplicate of R7. A NaN boundary cannot be a cursor, so the loader reads the full source whatever the tie order, and the window publishes once with the right rows. | +| 9: `sourceHoldsAllRows` defaulted to true for an unknown source | Fixed. It reads the resolved source collection, which is never undefined there. | + +## External review follow-up (2026-10-07) + +An external review of `3dc581394` raised three findings, each with a +reproduction against `9e8ed9978`. All three were confirmed and fixed. + +| Finding | Outcome | +| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| 1 (P1): a bounded repair kept a distant joined row instead of the next eligible row | Confirmed, a regression of this change. The law is the eager-window rows law: the window equals the first `limit` eligible rows. A full window is evidence of that only when every row the source sends passes the filter; a LEFT-joined filter drops rows after the source's `limit`, and a row that an earlier live update sent can fill the window. The eager-window oracle gained a filter dimension (source predicate, function filter, LEFT-joined marker), a label-only `touch` command, a `delete-visible` command, and a campaign fixed to the joined premise (`ordered-work.eager-joined-window`). RED on `3dc581394` for the pinned history (root ranks 1..10, markers on even ids, limit 1, touch 10, delete 2): rows `[10]` where `[4]` is expected; `9e8ed9978` gives `[4]`. Fix: a plan with a joined filter keeps the full local read, so it also keeps `main`'s asynchronous repair. Re-admitting joined plans fails the pinned case on both sync paths and the fixed-seed joined campaign (an assertion failure); the random joined campaign catches it on about a third of seeds. A function filter, residual predicates, inner joins, `groupBy`, `distinct` and custom collations already require the full source, so they never reach the bounded read; the function filter is in the grammar and passes. | +| 2 (P2): a reversed read required an `eq` lookup that admission does not check | Confirmed. The reader now gathers the nullish group with `equalityLookup`, which every index implements. A range-only index witness reads in both null placements, and a public witness orders a descending snapshot and a limited live query through it. RED on `3dc581394`: `Unsupported operation: eq` from every read; the public witness passes on `9e8ed9978`. A mutant that calls `lookup('eq')` again fails 3 cases with that runtime failure. | +| 3 (P2): the `nullsFirst = false` default changed plain reversal over a nulls-last index | Confirmed. Omitting the argument now reads the original index's reversed walk, as earlier releases did; `findIndexForField` always passes it. A legacy witness pins the earlier release's keys for BTree and Basic indexes (`takeFromStart(3)` is `[1, 3, 2]`, `take(3, null)` is `[3, 2]`); RED on `3dc581394` returned `[3, 2, 1]`. The old default fails both cases. | + +Two observations made while extending the oracle are outside this change: + +- An unindexed or indexed joined plan reads its full source twice per change + (402 rows scanned where the source holds 201), on `9e8ed9978` too. The scan + law now covers source-filter plans only. +- When rows tie on every `orderBy` term, a joined query returns them in the + joined result key's order, not the source key's: for ties at rank 11 among + ids 2 and 10 with limit 1, both commits return `[10]`. The live-queries guide + promises order only by `orderBy` terms, so the joined window is compared as + any valid first `limit` rows. The source-filter window still compares exact + ids, which assumes ties by source key; that assumption is unsupported by the + guide and is open. diff --git a/docs/contributing/oracle-reviews/pr-1302-vue-publication.md b/docs/contributing/oracle-reviews/pr-1302-vue-publication.md new file mode 100644 index 0000000000..9bfa1ed565 --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-1302-vue-publication.md @@ -0,0 +1,75 @@ +# PR #1302: Vue publication review + +- Raw source: [PR #1302](https://github.com/TanStack/db/pull/1302), head `de5f28d6fe9ee6f89dcf81a9d39fab90d51c4e99`. Its body contains eight proposed changes and detailed claims; GitHub returned no human issue or inline comments. The changeset bot comment is recorded below. +- Baseline reviewed: `ae2eb3fbf3a314a7f043f0d963fd336d8c1f1ec8` (`origin/main` on 2026-09-29). +- Repaired executable tree: `7a67d6a2` on `codex/pr-1302-vue-publication`. +- Executable owner: [`useLiveQuery-publication-oracle.test.ts`](https://github.com/TanStack/db/blob/main/packages/vue-db/tests/useLiveQuery-publication-oracle.test.ts). The shared conformance suite checks after Vue settles; this owner checks synchronous watcher observations during one source transaction. + +## Contract, evidence, and decision + +One source sync transaction must not expose an intermediate empty Vue `data` array when the source Collection has already published nonempty final rows. A synchronous `watch(..., { flush: 'sync' })` is a public Vue observation. The oracle copies source rows into a plain array and compares each hook callback with the old or final public snapshot at that callback. It requires one callback containing the final rows. Its bounded histories cross insert, update, and nonterminal delete with a supplied Collection and an identity live query. Empty final results, multi-change transactions, query recompilation, and other framework schedulers remain outside this owner. + +The six oracle cells all failed on baseline main. Every cell recorded `[]` while the source already contained the final nonempty rows. The checker rejects that forbidden value before it checks the callback count. Baseline emits two callbacks, with `[]` then the final rows. The same six passed after replacing `internalData.length = 0; internalData.push(...rows)` with one `ref.value = Array.from(rows)` assignment. The baseline implementation is the hostile wrong design: it reached the intended watcher checkpoint and failed by assertion, not by timeout or setup failure. The repaired cells each observed exactly one final `data` callback. + +A separate focused RED found that a user query throwing `Error('__DISABLED_QUERY__')` was misclassified as disabled. Replacing the message match with a unique Symbol sentinel made that test GREEN. Null and undefined disabled queries still pass. The change also removes stack allocation for ordinary disable, while retaining a caught throw. It does not claim to fix debugger behavior for caught exceptions. + +The array fix uses Vue `ref`, rather than the PR's proposed `shallowRef`. A temporary compatibility probe found that baseline `reactive` and repaired `ref` both expose deep reactive arrays, rows, and nested values; an in-place nested edit triggered a synchronous watcher in both. This behavior is not asserted as a supported Collection mutation path, but preserving it avoids a separate observable change. The old array spread is gone; the new `Array.from` assignment has no function-argument limit. Production diff at `7a67d6a2`: 9 additions, 19 deletions, net **−10** lines. Test and coverage text weight is separate. + +A baseline diagnostic also established that `markReady()` changed the Vue hook status to `ready` before returning and that stopping a standalone `effectScope` prevented a later source insert from changing hook data. The shared Vue `no-updates-after-unmount` conformance scenario reaches the same disposal path. These results do not prove every delayed callback schedule. + +## Lossless PR claim ledger + +| ID | Original claim or suggestion | Verdict and disposition | Durable destination | +| --- | --- | --- | --- | +| V01 | Broad Vue primitive refactor, no public API break | `design-decision`: two narrow defects confirmed; full refactor compatibility unproved | PR #1302 | +| V02 | Existing 30 tests, 18 new tests, shared utils, ~83→87% coverage | `stale`: historical PR counts; current suite differs | PR #1302 test plan | +| V03 | Deep Map proxying has O(N×M) cost and no utility | `deferred`: proxy wrapping is real; claimed cost unmeasured | PR #1302 performance proposal | +| V04 | Array clear+push exposes empty state; use `shallowRef` | `fixed-now`: six RED→GREEN cells; used deep `ref` to preserve behavior | Vue publication oracle | +| V05 | Sentinel throw/catch has stack, debugger, and string-match costs; use builder probe | `fixed-now` for collision and allocation via unique Symbol; debugger concern remains V16 | Focused Vue regression | +| V06 | `getCurrentInstance`/`onUnmounted` misses standalone scope cleanup and leaks | `refuted` on current main: scope stop disposes `watchEffect` observer | Vue conformance disposal scenario | +| V07 | Default GC retains ephemeral hooks; `gcTime:1` is preferable | `design-decision`: no leak witness or approved lifetime policy | PR #1302 | +| V08 | Shared duck typing risks false positives; Vue should use `instanceof` | `design-decision`: no false-positive witness; a Vue-only identity rule diverges from shared helper | `packages/db/src/live-query-adapter.ts`, PR #1302 | +| V09 | `nextTick` leaves one-tick ready/loading mismatch | `already-fixed`: current observer path updates status synchronously in diagnostic | Vue readiness conformance | +| V10 | Add `isEnabled` computed field | `deferred`: additive convenience API, not reproduced defect | PR #1302 API proposal | +| V11 | Keep computed wrappers for API stability | `already-fixed`: preserved | Vue public types | +| V12 | Keep manual initialization plus initial-state subscription as safety net | `already-fixed` as preservation claim; rationale not independently proved | Vue startup conformance, PR #1302 | +| V13 | Test plan repeats counts and coverage claim | `duplicate` of V02 | PR #1302 | +| V14 | Shallow Map tracks operations without deep-proxying values | `duplicate` of V03; behavior true, benefit unmeasured | PR #1302 | +| V15 | Array reference assignment gives one notification and avoids deep proxies | `duplicate` of V04; one callback verified; deep proxy removal intentionally omitted | Vue publication oracle | +| V16 | Caught sentinel causes debugger stops and middleware fragility | `deferred`: no wrapped-error path or measured debugger impact; Symbol still throws | PR #1302 | +| V17 | Standalone scopes, SSR, and utilities need `onScopeDispose` | `duplicate` of V06; scope disposal already works in tested path | Vue conformance | +| V18 | Config `gcTime` should override default; precreated collections preserve theirs | `duplicate` of V07 | PR #1302 | +| V19 | Cross-realm `instanceof` caveat can be revisited | `duplicate` of V08; cross-copy interoperability is unsupported by current AGENTS.md | Shared helper, PR #1302 | +| V20 | Synchronous status avoids skeleton frame; stale callback guard needed | `deferred`: sync part already fixed; stale callback schedule not supplied | PR #1302 | +| V21 | `isEnabled` completes status predicates | `duplicate` of V10 | PR #1302 | +| V22 | Force `startSync:true` after config spread | `already-fixed` on current main | Existing Vue config tests | +| V23 | Patch changeset exists | `already-fixed` in PR head; bot metadata, not a product finding | PR #1302 changeset | + +**New finding S01 (outside the 23 raw claims):** An application error with the internal disabled-marker text was swallowed. Baseline focused regression failed because `useLiveQuery` did not throw; the same regression passed with the unique Symbol sentinel. Its durable owner is `packages/vue-db/tests/useLiveQuery.test.ts`. + +## Oracle-guide audit + +| Requirement | Outcome for the Vue publication oracle | +| --- | --- | +| ORC-001 | The law is the hook's coherent public `data` publication at a synchronous Vue watcher cut, motivated by the PR's explicit behavioral report. Six finite histories bound the claim; the omitted histories are named above and in the coverage map. | +| ORC-002 | Expected rows come from copied source Collection values, not the hook's array update, Vue ref, or observer classifier. | +| ORC-003 | The file names contract, model, grammar, production driver, and refinement check separately. | +| ORC-004 | Not triggered: fixed bounded matrix, no generated-history coverage claim. | +| ORC-005 | The driver invokes the real `useLiveQuery` overloads and source sync transaction; a synchronous public Vue watcher records every `data` callback and confirms source final state at each callback. | +| ORC-006 | Baseline clear+push is the hostile mutant. All six cells failed at the synchronous watcher content assertion on `[]`; repaired cells passed. | +| ORC-007 | Not triggered: no important generated property or random campaign. | +| ORC-008 | Not triggered: the independent model is stateless copied rows. | +| ORC-009 | `source Collection`, `sync transaction`, and public snapshot follow the glossary; plain copied rows are a test-only projection. | +| ORC-010 | The driver captures the primary assertion failure, attempts all cleanup steps, and aggregates separate cleanup errors without erasing the primary cause. No cleanup fault was injected. | +| ORC-011 | No plausible shared semantic fault between copied source values and hook array notification was identified. The alternate supplied-Collection and identity-query paths exercise different hook inputs, but are not called independent semantic formulations. | +| ORC-012 | This versioned record ties the executable evidence to `7a67d6a2` and states contract × history × path × observation limits. The six-cell fix does not claim closure for untested Vue histories. | + +## Verification and reviewer assessment + +- Baseline `ae2eb3fb`: publication oracle **6/6 RED**; sentinel regression **RED**; same-turn readiness and scope-disposal diagnostic **GREEN**. +- Candidate `7a67d6a2`: publication oracle **6/6 GREEN** with focused test typecheck; sentinel and existing disabled tests **GREEN**; all Vue runtime tests **106/106 GREEN**; production TypeScript check and formatting **GREEN**. +- Package-wide Vitest typecheck reports 149 existing cross-package `rootDir` errors from shared conformance imports. The runtime assertions pass; the focused oracle typecheck and production TypeScript check pass. + +The PR author identified a real, high-signal Vue publication defect and offered a repair with the right atomic shape. Their other proposals mix API design, unmeasured performance claims, and claims already addressed on current main. A narrow `ref` assignment preserves more behavior than the proposed `shallowRef`. As a reviewer sample, this artifact alone is **insufficient to recommend hiring**: the core observation is strong, but prioritization and evidence for the broader refactor are weak. + +Loss audit: **23 raw items = 2 fixed-now + 6 already-fixed/stale + 1 refuted + 4 deferred + 3 design-decision + 7 duplicates**. S01 is one additional, fixed-now finding. No raw item or bot comment was dropped. The deferred items have destinations in #1302; V20 specifically needs a held stale-callback schedule before any guard is justified. diff --git a/docs/contributing/oracle-reviews/pr-1509-angular-required-input.md b/docs/contributing/oracle-reviews/pr-1509-angular-required-input.md new file mode 100644 index 0000000000..922fc11072 --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-1509-angular-required-input.md @@ -0,0 +1,79 @@ +# PR #1509: Angular required input review + +Reviewed source: [PR #1509](https://github.com/TanStack/db/pull/1509) body, +diff, and inline comments (none). Main baseline: +`ae2eb3fbf3a314a7f043f0d963fd336d8c1f1ec8`. Independent repair commit: +`9ed03a10ce2aec780dbf4072fa16e5eaad75db82`. The repair was tested in an +isolated worktree; the PR branch was not changed. + +## Reviewer assessment + +The author found a real Angular lifecycle bug and supplied the right component +shape to expose it. The proposal also combined a test-runner replacement, +cleanup rewrite, and timing-helper rewrite with that bug. `linkedSignal` would +exclude the package's declared Angular 16 peer range. Accuracy on the central +finding is strong; fix scope and compatibility judgment are mixed. One PR is +insufficient evidence for a hiring recommendation. + +## Lossless finding ledger + +| ID | Original claim or proposal | Evidence and verdict | Action and durable destination | +| --- | --- | --- | --- | +| P1509-01 | Eager derived-collection read breaks required signal inputs. | **Confirmed.** The original code threw NG0950 during component field initialization, before input assignment. | **Fixed now.** Lazy status read in the repair commit. | +| P1509-02 | Compiled Angular test setup is needed for a component witness. | A JIT component with the AOT-equivalent signal-input flag reaches the same runtime boundary; AOT compilation remains untested. | **Deferred.** The larger compiler/test-runner setup stays in PR #1509 as a separate test-infrastructure proposal. | +| P1509-03 | A required-input regression test should prove initial and reactive results. | **Confirmed.** The final test throws NG0950 on baseline; after repair, initial input 30 renders one row (`b`) and changed input 10 renders two (`a`,`b`). | **Fixed now.** Angular conformance driver and coverage map. | +| P1509-04 | Replace status with `linkedSignal` reading `untracked(collection)`. | It may defer the read on Angular 20, but `linkedSignal` is unavailable at the declared Angular 16 minimum. The two collection reads are unnecessary. | **Refuted as a compatible fix.** The repair uses supported `signal` and `computed` primitives. | +| P1509-05 | Remove external cleanup and rely only on effect cleanup. | No cleanup failure was reported or reproduced. Existing unsubscribe cleanup clears its handle before repeated calls; unmount conformance remains green. | **Deferred.** Separate simplification candidate in PR #1509. | +| P1509-06 | Replace timeout waits with Angular stability checks. | The existing helper waits 0 and 50 ms; the proposal is plausible, but no failing schedule was supplied. | **Deferred.** Angular test-helper proposal in PR #1509. | +| P1509-07 | The original PR passed `pnpm test` locally. | Historical claim not replayed at that PR head. Current repair passes the full Angular suite, type checks, and package build. | **Deferred.** Historical evidence remains with PR #1509; no current-main inference. | + +Loss audit: seven claims in the raw body and no inline review comments. Totals: +two fixed now, four deferred, one refuted. All seven have a destination. No +behavioral claim from the PR diff or checklist is silently omitted. + +## Reproduction and calibration + +The production driver is `TestBed.createComponent` with an `input.required()` +field and the public `injectLiveQuery({ params, query })` call. The JIT test +environment does not infer `input()` metadata, so the test supplies Angular's +signal-input flag and still uses `componentRef.setInput` for value assignment. +The baseline failed at `src/index.ts:219`, where status initialization called +`collection()` and reached the unassigned input. This was an assertion failure +at construction, not a timeout or setup failure. The repair defers the read. + +A simpler `status = signal('idle')` repair changed immediate disabled-query +status to `idle`. A deliberate mutant that removed the lazy disabled fallback +failed the new synchronous-status check with `expected 'idle' to be 'disabled'`. +After restoring the repair, both focused checks passed. The full Angular run +reported 61 passing runtime tests, one existing todo, and no type errors; the +Angular package build and ESLint passed. Production code changed by +7/-5 +lines, net +2. The extra derived status preserves immediate disabled readiness. + +## Oracle guide audit and limits + +- ORC-001: Angular assigns required inputs after construction; the public + reactive-query API must defer reading them. The test also preserves existing + synchronous disabled status. The coverage map names AOT and runtime limits. +- ORC-002 and ORC-003: Expected IDs come from a two-row array filter, separate + from query machinery. The test comment, fixed input history, TestBed driver, + and post-change-detection comparison keep all five responsibilities visible. +- ORC-004 and ORC-007: Inapplicable; this is a fixed two-step history, not a + generated property or a claim about a broader grammar. +- ORC-005 and ORC-006: The public component path and exact rendered/selected + observations ran. Baseline code and the disabled-status mutant failed at the + intended checkpoints. +- ORC-008 and ORC-009: Inapplicable; no stateful reference model or alternate + subsystem vocabulary was introduced. +- ORC-010: The component fixture is destroyed in `finally`; no cleanup error + occurred. A cleanup failure could replace an earlier assertion error, so this + fixed witness does not claim failure-preserving teardown under injected faults. +- ORC-011: No plausible shared semantic fault requires a second query + formulation for the construction-timing law. The selected-row model checks + values only within this bounded fixture. +- ORC-012: This record ties the claims, RED/GREEN results, guide audit, and + bounded closure statement to the exact repair commit above. + +This witness covers required input assignment before initial change detection +and one later value change in Angular 20's JIT runtime. It does not prove the +AOT compiler path, Angular 16 through 19 execution, every option overload, +or every scheduler ordering. Those are separate cells, not claims closed here. diff --git a/docs/contributing/oracle-reviews/pr-1543-sortedmap-bulk.md b/docs/contributing/oracle-reviews/pr-1543-sortedmap-bulk.md new file mode 100644 index 0000000000..44518802ac --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-1543-sortedmap-bulk.md @@ -0,0 +1,91 @@ +# Bulk SortedMap ordering review + +Reviewed product head: `c87f3e0b8fa46f90f8023f09e09a24fb67764e7f` (the +SortedMap change merged with `origin/main` at `bef12e24227aa993cea03c7b1cdeeda233cce739`). +Source: external PR #1543 and its CodeRabbit review. A nine-item source ledger +was maintained during evaluation; this record keeps the verdict-critical +oracle evidence in the repository. + +## Contract and scope + +`SortedMap` keeps native Map key ownership and exposes keys, values, entries, +and `forEach` in key order or comparator order with key tie-breaking. This is +the established contract in `packages/db/tests/SortedMap.test.ts`. Collection +sync transactions that publish together expose the same final order at their +publication boundary. A bulk publication with at least 512 operations may +defer ordered-key maintenance until all its writes have applied. Point reads +remain current while order is deferred; the ordered view is restored before +publication. Smaller publications retain incremental ordering. + +The work claim is bounded to one bulk publication. It does not promise fast +repeated one-row commits or a general comparator-mutation policy. Runtime keys +outside `string | number` retain the old binary-insert path because +`compareKeys` is not a total order for nullish keys. + +## RED, GREEN, and cost evidence + +- On main before the fix, 2,048 descending direct inserts displaced 2,096,128 + ordered-array slots; the oracle bound was 131,072. The same direct law is + green with deferred insertion. +- A hostile mutant that ignores deferred ordering fails the public Collection + work assertions at the intended commit/drain checkpoint: 4,096 inserts move + 8,386,560 slots against a bound of 262,144; sixteen queued 64-row sync + transactions move 523,776 slots against a bound of 65,536. The candidate + passes both laws. The queued transactions are held behind a persisting + optimistic mutation, then applied in one drain. +- The Map/full-sort oracle checks key ownership, exact order and ties, point + reads, all iterators, and `forEach` after fixed and generated histories. + Generated actions now vary deferred writes, immediate writes, clear, and + observation checkpoints under the same fixed and random campaigns. Fixed + tree-sized histories cover 256 keys and comparator reorders. Public tests + check comparator-order publication, a pending iterator, and 64 incremental + updates in a 10,000-row Collection. Direct controls cover mutable comparator + fields before the batch checkpoint and runtime nullish/numeric keys. +- In independent 40,000-key random-insert probes, main took 77.6–85.1 ms and + deferred sorting took 11.7–12.6 ms. Twenty full scans took 7.8 ms on main + and 7.7–8.2 ms on the candidate. Five thousand alternating write/full-scan + loops took 265–308 ms versus 255–278 ms. Retained heap after GC was about + 1.84 MiB in both cases. These measurements do not reproduce PR #1543's + one-million-key figures or certify other hardware. +- For a 100,000-row retained map with 1,024 descending front inserts, eager + updates took 15.3–15.5 ms and deferred sorting 2.1–4.2 ms in the same + harness. With only 256 front inserts and a comparator, eager updates were + faster (3.9 versus 7.3 ms). The fixed 512-operation cutoff avoids sorting + the whole retained map for that smaller publication. + +## Oracle guide audit + +| Requirement | Outcome at reviewed product head | +| --- | --- | +| ORC-001 | The established Map/order law and bulk-publication work boundary are stated above. Post-publication object mutation and repeated small commits are outside the claim. | +| ORC-002 | Expected order is a fresh sort of an independent native Map. Displaced-slot work is counted at `Array.splice`, independently of elapsed time. | +| ORC-003 | `SortedMap.test.ts` states the contract, uses Map/full-sort as model, generates action histories, drives real SortedMap and Collection sync APIs, and compares public observations after actions or publication. | +| ORC-004 | Fixed examples reconstruct deferred set/delete, immediate set, clear, and observations. The generated grammar varies action, key/value, deferral, and observation. Removing deferral loses the new path; removing checkpoints loses intermediate observations. Keys/values are bounded to -3..3, histories to 40 actions; runtime nullish keys and 256-key histories have separate fixed controls. Invalid comparator inputs and arbitrary mutation timing are excluded. | +| ORC-005 | Direct SortedMap methods are observed after each eager or chosen deferred checkpoint and at history end. Public Collection `keys()` is observed at commit and subscription publication; the queued work counter spans the drain after the held mutation settles. | +| ORC-006 | Disabling Collection deferral is rejected by both public work assertions, with exact values above. The existing wrong-order, dropped-tie, and stale-overwrite controls reject those answer classes. The mutant produced assertion failures, not setup failures or timeouts. | +| ORC-007 | The existing `sorted-map.key`, `sorted-map.ascending`, and `sorted-map.descending` owners run identical fixed-seed and unseeded properties and retain the registry's seed/path replay interface. The new actions use those same campaigns. | +| ORC-008 | No reference-model state was added. Native Map remains the whole model state. | +| ORC-009 | `deferOrder` is an implementation choice inside a sync publication; the model's `observe` action is a model-only checkpoint. Collection, sync transaction, publication, and row retain glossary meanings. | +| ORC-010 | The generated property uses the existing fast-check replay path. Public queued receipts get rejection handlers when created; cleanup releases the held mutation, settles receipts, and cleans the Collection. | +| ORC-011 | No plausible semantic fault shared by native Map/full-sort and production insertion was named. Public Collection publication is a second production boundary, not a second semantic model. | +| ORC-012 | This record provides each applicable outcome and limits the repair claim to the declared contract, history, path, and observation. | + +## Remaining review items + +CodeRabbit correctly identified that using live mutable values as BTree keys +would invalidate that PR's tree ordering. Its proposed generic snapshot did +not establish a safe identity-preserving comparator token, and the author +reported ORDER BY regressions from an attempted snapshot. The new fix keeps the +array and does not decide whether comparator-observed fields may change after +publication. The SortedMap and collection-state-retention oracle owners must +receive that law when the product contract is decided. Exact one-million-row +timings and the discarded snapshot candidate remain evidence gaps in the +external PR, not claims of this repair. The original review did not assess +scan, memory, or runtime-key effects; this record covers them. + +Loss audit: nine raw items = one fixed now (quadratic bulk work), one refuted +(broad compatibility of the BTree proposal), one already fixed (ordinary +reorder/tie coverage), four deferred (BTree design, original benchmark, +discarded snapshot, release-impact checklist), one design decision +(post-publication comparator mutation), and one duplicate walkthrough. No +source claim is omitted. diff --git a/docs/contributing/oracle-reviews/pr-1592-error-boundary-review.md b/docs/contributing/oracle-reviews/pr-1592-error-boundary-review.md new file mode 100644 index 0000000000..e47b205775 --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-1592-error-boundary-review.md @@ -0,0 +1,76 @@ +# PR #1592: retry error boundary review + +## Authority and scope + +The reviewed PR head was `1bf6a0bc47eb0021a926994b82af27cc326403f6`. +The primary executable owner is +`packages/offline-transactions/tests/transaction-settlement-oracle.property.test.ts`. +The README promises that `shouldRetry` receives the named mutation function's +same `Error` instance. Public manual outbox removal also acknowledges deletion +while a provider can still be running. If that provider then fails, its caller +must settle from that failure and a retry cannot recreate the removed row. + +The reviewer separately observed that a hook fault loses the provider error. +The maintainer chose the exact hook error as the caller-facing and stored error; +the provider error remains in the warning. The serialized format stores +`name`, `message`, and `stack`, so arbitrary error prototypes and custom fields +are not restored after an offline executor restart. The documented invalid +Promise result is observed for rejection; arbitrary third-party thenables are +outside the current contract and Node-hosted oracle evidence. + +## Oracle-first experiments + +The cross-realm history throws a `node:vm` Error with `status = 401` from the +real named mutation function. The hook must receive that object by identity, +persist a first retry record, and later fulfill a public `commit()` after a +successful second attempt. A local Error and a plain status-bearing object are +neighboring existing controls: the former keeps identity, and the latter is +converted before the hook. This case fails on the reviewed executor at the +hook-identity assertion: it receives a wrapper with no `status`. + +The removal history holds the public provider call, acknowledges either +`removeFromOutbox` or `clearOutbox`, then releases a network failure under the +default retry decision or a 401 under a true hook answer. Before release, the +row is absent and both caller promises are pending. After release, both must +reject with the provider Error, the row must stay absent, and a later peer must +fulfill in the same executor. On the reviewed executor, both +`removeFromOutbox` cases fail the caller-outcome assertion with pending +promises. The two `clearOutbox` cases time out at the caller-settlement +checkpoint because that API clears the scheduler's running count before the +provider returns; those timeouts are progress failures, not assertion kills. +The existing successful-provider removal witness is the nearby fulfillment +control. A narrower missing-row branch in the retry-persistence catch repairs +the reported failure; unrelated storage errors still stop the batch. + +On the repaired working tree, all 60 settlement-oracle cases passed. The new +cross-realm case, four removal cases, and the existing successful-provider +removal cases passed in focused runs. The package typecheck and edited-file +ESLint passed. The prior Preview CI failure was a 404 from the external preview +publisher; rerunning only Preview succeeded, and the current published head's +other CI jobs passed. These CI results predate the follow-up source commit. + +## Guide audit + +| Requirement | Evidence or limit | +| --- | --- | +| ORC-001 authority | The README's same-instance hook rule and acknowledged public removal supply the two laws; provider and storage are controlled. | +| ORC-002 independence | Expected identity, rejection, absence, and peer progress come from those public contracts, not the executor's classifier or scheduler. | +| ORC-003 literate responsibilities | The oracle opening states the laws and limits. Local prose gives the legal histories, real public driver, observations, and checkpoints beside each case. | +| ORC-004 grammar controls | The fixed removal matrix reconstructs two APIs crossed with default network and hook 401 retry. Each axis changes the reached path. A removal after retry persistence begins and an unacknowledged removal are excluded. The cross-realm Error is distinguished from a local Error and a plain object. | +| ORC-005 path and observation | Public `commit()`, `isPersisted`, `peekOutbox`, provider calls, and later peer settlement are observed at held-provider, acknowledged-removal, rejection, and later-peer cuts. | +| ORC-006 calibration | The reviewed production code fails the cross-realm identity assertion and both `removeFromOutbox` caller assertions. `clearOutbox` reports two classified timeouts. The repair passes the same cases. | +| ORC-007 campaigns | The new histories are fixed cases, so the campaign trigger does not apply. The existing generated retry decision property still runs fixed-seed and seedless campaigns. | +| ORC-008 model state | No reference-model state was added or merged. | +| ORC-009 vocabulary | Outbox row, named mutation function, optimistic state, and offline executor restart follow the glossary. | +| ORC-010 cleanup | Held providers are released and pending callers are rejected in `finally`; cleanup keeps the primary failure distinct. | +| ORC-011 second formulation | The same-instance law is checked by direct object identity and by its `status`-based retry consequence. The removal law has the success-after-removal neighboring history. | +| ORC-012 review evidence | This record identifies the reviewed head, exact RED checkpoints, GREEN runs, and limits. The repaired revision is the commit containing this record. | +| ORC-013 boundary witness | A 401 Error from another realm distinguishes conversion from identity; default network and overridden 401 retry distinguish the missing-row consequence. | +| ORC-014 provider handoff | No live server or native-storage claim is made. The fake adapter and controlled provider establish only the executor boundary. | + +The bounded removal witness covers deletion acknowledged before the provider +settles. A manual removal overlapping the read and write inside `outbox.update` +could resurrect a row; that interleaving has not been exercised. The settlement +owner needs a controlled witness and an atomicity decision for it. The coverage +map records this open boundary. A browser-host receiving witness would be needed to claim +cross-realm behavior through an actual iframe or worker rather than `node:vm`. diff --git a/docs/contributing/oracle-reviews/pr-1592-rejected-hook-promise.md b/docs/contributing/oracle-reviews/pr-1592-rejected-hook-promise.md new file mode 100644 index 0000000000..9baf9857c9 --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-1592-rejected-hook-promise.md @@ -0,0 +1,95 @@ +# PR #1592: rejected retry-hook Promise review + +## Scope and authority + +- Independent oracle-review challenge: PR head + `1ec6a92d76a0b71727d1c30998b804b53bc11ed7`. The reviewer read the + oracle, public contract, glossary, coverage map, and instruments, ran the + 51-case settlement oracle and a direct replay, and probed the executor. +- Oracle-first repair: `057daa796fc079080fdfcf25cd15ba33dffe658f`. +- Production repair reviewed here: + `9f257e03b5ce83202f76580b482e9f54b81d7003`. +- Primary owner: + `packages/offline-transactions/tests/transaction-settlement-oracle.property.test.ts`. + +The README, guide, and package skill promise that `shouldRetry` returns a +synchronous decision. A Promise is invalid and fails only its outbox row. After +acknowledged deletion, queued rows continue. A Promise rejected by an async +hook must therefore not escape as an unhandled process rejection. The same +sources promise that a non-`Error` named mutation rejection is converted to an +`Error` before the hook runs. These are bounded contract claims, not a new +async retry API or a promise that the original non-`Error` fields survive. + +## Finding, witness, and repair + +At the challenged head, a direct Node 24 probe called the real executor with +an async hook returning a rejected Promise. The row was removed and its caller +received the invalid-return `TypeError`, but an `unhandledRejection` listener +received the async error. Without that listener, the Node process exited 1 +after removal. The old oracle used only `Promise.resolve(true)`, so its history +grammar could not produce this failure or observe the process event. + +The repaired oracle crosses a fulfilling and immediately rejecting Promise at +the public `commit()` and restart path. A second case returns a pending Promise, +holds deletion, admits two peers, completes them in FIFO order, then rejects the +Promise. Its Node process listener must remain empty after a macrotask. The +public commit, outbox, provider-call, and Collection checks still judge the +row-local failure at their existing cuts. Two neighboring histories reject a +string or status-bearing object from the named mutation function and check the +hook's converted `Error`, retry count zero, terminal caller identity, outbox +deletion, and optimistic rollback. + +The production repair observes any invalid returned value with +`Promise.resolve(decision).catch(...)` before throwing the invalid-return +`TypeError`. It does not await that value or change the terminal decision. The +one-line operation also assimilates thenables, though the executable witnesses +use native Promises. + +## Executed calibration and checks + +| Subject | History and result | Classification | +| --- | --- | --- | +| Original executor at `1ec6a92d7` with immediate rejected-Promise oracle | Public caller and deletion checks passed; `unhandled` contained the async error at the asserted cut. | Assertion failure at intended observation. | +| Temporary removal of the rejection observer from the repaired executor | Immediate and held-late Promise cases both found the async error in `unhandled`; the late case failed after queued peers settled. The edit was restored. | Assertion failures at intended observations. | +| Temporary bypass of `toError` at the named mutation rejection boundary | Both non-`Error` cases reached the hook and failed its `Error` assertion. The edit was restored. | Assertion failures at intended observation. | +| Temporary `await` of the hook return | The held Promise prevented terminal deletion until the oracle checkpoint timed out; cleanup released that test-owned Promise. The edit was restored. | Timeout exposing a progress violation, not an assertion kill. | +| Repaired executor at `9f257e03b` | Settlement oracle 55/55; package tests 221/221 across 17 files excluding the separate uncommitted leadership draft; typecheck, ESLint, Prettier, and diff checks passed. | GREEN within stated scope. | + +The independent reviewer then challenged the new tests twice. The first pass +found missing pending-waiter cleanup in the non-`Error` case and an unsupported +claim about late rejection. The repair records and rejects a pending waiter in +`finally`, awaits its observed promise, and uses a held Promise released only +after peer settlement. The second pass found that an await-the-hook mutant +could leave that held Promise unresolved after a timeout. Cleanup now attaches +a cleanup-only rejection handler and releases it while preserving the primary +checkpoint failure. The reviewer made no source edits. + +## Oracle guide audit + +| Requirement | Result | +| --- | --- | +| ORC-001 authority and limits | Pass. Public retry docs authorize synchronous, row-local failure and conversion of non-`Error` rejection. Node process observation is the claimed host boundary. | +| ORC-002 independent judgment | Pass. Expected outcomes follow from the public row-local contract and Error input rule; neither calls the executor's retry classifier. | +| ORC-003 literate responsibilities | Pass. Opening law and local prose identify the model relation, legal immediate/delayed histories, real public driver, observations, and checkpoints. | +| ORC-004 grammar controls | Pass for the finite extension: fulfilled versus rejected native Promise, immediate versus held-late rejection, and primitive versus status-bearing object provider rejection. Generic thenables remain outside these fixtures. | +| ORC-005 path and observation | Pass. Public `commit()` drives the executor and outbox. The listener observes Node's process event; the held case checks FIFO peers before releasing the Promise. | +| ORC-006 calibration | Pass. Removing rejection observation and bypassing Error conversion fail their intended assertions. Awaiting the held Promise times out and is classified separately. | +| ORC-007 fixed/random replay | The new cases are fixed histories, so this trigger does not apply to them. The unchanged generated retry matrix still runs fixed-seed and seedless campaigns with direct replay support. | +| ORC-008 model-state minimality | Not triggered: no reference-model state was added or removed. | +| ORC-009 vocabulary | Pass. Outbox row, queued peer, offline executor restart, and observation checkpoint retain glossary meanings; `unhandled` is a test observation list. | +| ORC-010 failure fidelity | Pass. The non-`Error` case settles a pending waiter during cleanup. The held case releases storage gates and a pending test-owned Promise after a primary failure; cleanup diagnostics remain separate. | +| ORC-011 second formulation | Not triggered: no plausible fault shared by both a model and production classifier was identified for this process-event law. | +| ORC-012 review evidence | This record names reviewed executable revisions, required outcomes, hostile controls, and scope limits. | +| ORC-013 boundary witness | Pass for the bounded law. Immediate rejection and rejection after peer settlement both distinguish observed from escaped Promise failure. Converted-error cases distinguish hook input from raw non-`Error` values. | +| ORC-014 controlled-premise handoff | No browser-host claim is made. Node's process event is observed directly; storage and provider effects remain controlled fixtures. | + +## Remaining limits + +The rejected-Promise cases cover a native Promise that rejects immediately or +after two queued peers settle in a Node-hosted Vitest run. They do not establish +browser `unhandledrejection` behavior, every third-party thenable, a rejection +after executor disposal, or native power-loss durability. The settlement owner +can add those histories if a broader cross-host or thenable claim is needed. +The status-bearing object case checks conversion to `Error`, not preservation +or removal of its custom fields. No reachable in-scope counterexample remains +for the bounded histories above. diff --git a/docs/contributing/oracle-reviews/pr-1592-retry-decision-grammar.md b/docs/contributing/oracle-reviews/pr-1592-retry-decision-grammar.md new file mode 100644 index 0000000000..9be03712d6 --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-1592-retry-decision-grammar.md @@ -0,0 +1,103 @@ +# PR #1592: retry decision grammar review + +## Reviewed executable state and law + +- Reviewed executable commit: `d283256048628e8a8aabd6e0b0dac0f9d8108638`. +- Production subject at that commit: `760e54204541f260be34f0e93087e9598cb769b6`; the two intervening commits change only the settlement oracle and coverage map. +- Primary owner: `packages/offline-transactions/tests/transaction-settlement-oracle.property.test.ts`. +- Review method: self-review under the oracle guide and mutant gap hunt. No independent reviewer is claimed. + +The accepted `shouldRetry` law has three valid answers. `true` retries, `false` +terminates, and `undefined` delegates to the existing default decision. An +omitted hook also delegates. `NonRetriableError` bypasses the hook. A thrown +hook error or any other return value fails that outbox row. The maintainer's +earlier row-local decision authorizes terminal removal; this repair does not +change that product policy. The default policy's current AbortError and message +classifications are preservation controls, not a general HTTP status policy. + +The old seven-case table already included ordinary error plus `false`, so it +could reject a false-as-delegation mutant. It omitted the opposite override, +AbortError, several default message classes, and Promise results. A null result +already had public `commit()` and cleanup witnesses. The first expansion still +missed later fixed examples because fast-check counted examples against its +12-run budget. Status mutants for 400, 403, 422, and 404 survived that first +expansion. The executable repair reserves 33 fixed runs before 12 generated +runs in each normal campaign, then asserts that all 32 error/answer cells ran. + +## Executed controls + +The final model crosses eight fixture error kinds with four hook answers. It +checks the first stored retry record or acknowledged terminal deletion, exact +outbox IDs and retry counts, caller promises, provider calls, and Collection +rows. A separate public hook-fault driver crosses throw, null, zero, and +Promise results and checks the first terminal marker against a retry record. +The fixed matrix uses one FIFO peer, one additional auth case uses three, and +the generated runs vary peer count from one to three. A later-failure witness +checks retry count one. The controlled storage does not establish native +durability, real timer accuracy, or provider idempotency. + +On the reviewed executable commit, the settlement oracle passed 51 of 51 +tests; package typecheck, test-file ESLint, Prettier, and `git diff --check` +passed. The following temporary mutants were restored after their runs: + +| Mutant | Distinguishing history | Outcome | +| --- | --- | --- | +| Treat `false` as `undefined` | ordinary error, explicit terminal answer | Assertion failure: stored `retry` versus expected `terminal`. | +| Await a Promise hook result | 401 error, Promise of `true` | Assertion failure: stored `retry` versus expected `terminal`. | +| Retry AbortError by default | AbortError, delegated answer | Assertion failure at stored decision. | +| Retry 400 by default | 400 message, delegated answer | Assertion failure at stored decision. | +| Retry 403 by default | 403 message, delegated answer | Assertion failure at stored decision. | +| Retry 422 by default | 422 message, delegated answer | Assertion failure at stored decision. | +| Terminate 404 by default | 404 message, delegated answer | Assertion failure at stored decision. | +| Restore the old 12-run budget | Normal campaign | Reach assertion failure: 12 observed cells versus 32 declared cells. | + +The first run of the 400/403/422/404 mutants **survived** before the budget +repair. They are not counted as kills from that version. All reported kills +above reached assertions after the final budget repair; none was a timeout, +setup failure, or unreached edit. Direct replay of the false-as-delegation +mutant with seed `-1799994628` and path `5` reproduced the same ordinary/false +comparison on the reviewed executable commit. + +## Oracle guide audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 authority and limits | Pass. The README, type contract, approved row-local decision, and existing default behavior support the bounded law above. HTTP status meaning, native storage, timers, and provider effects remain outside it. | +| ORC-002 independent judgment | Pass. The expected decision is a declarative truth table and three-answer rule. It does not call the production retry classifier. The provider-error helper only constructs fixture inputs. | +| ORC-003 distinguishable responsibilities | Pass. Opening prose states the law; local prose accompanies the model, 32-cell grammar, public driver, and stored-decision comparison. | +| ORC-004 grammar controls | Pass. Fixed examples reconstruct every error/answer cell; the reach assertion fails if the campaign budget excludes one. The two explicit answers, delegation, omission, permanent bypass, error classes, and peer-count variation each have a stated role. The nearby invalid null, zero, and Promise answers belong to the separate fault grammar; duplicate IDs and storage failures remain separate owners/cuts. | +| ORC-005 path and observation | Pass. Public transactions call `commit()` through the real executor, outbox, scheduler, and Collection. The first durable decision is compared before later peer progress can hide it. | +| ORC-006 checker calibration | Pass. The temporary wrong designs in the table fail at their intended stored-decision or reach assertion. Initial survivors are recorded separately. | +| ORC-007 fixed/random replay | Pass. Both normal campaigns run the same property for 45 cases, one fixed seed and one seedless. Seed/path replay reproduced the false-as-delegation comparison directly. | +| ORC-008 state minimality | Not triggered. The new decision table is stateless; no reference-model state was added or merged. | +| ORC-009 vocabulary mapping | Pass. Fixture-only error-kind labels select Error instances; `storedDecision` projects the retry-record or terminal-deletion observation and is not a production state. Outbox phases retain glossary terms. | +| ORC-010 failure fidelity | Pass. The new comparison fails before peer waits on a wrong decision. Existing `finally` cleanup releases provider gates and preserves the primary assertion; direct replay reproduced it. | +| ORC-011 second formulation | Not triggered for the optional hook law: no distinct shared semantic fault was established. The generic 404 policy remains a separate design question, not an expected result inferred from this hook oracle. | +| ORC-012 review evidence | This versioned record names the executable commit, scope, controls, applicable outcomes, and remaining limits. It follows the executable commits. | +| ORC-013 boundary witness | Pass. Ordinary/false distinguishes terminal override from delegation, auth/true distinguishes retry override from the default, and 404/defer distinguishes the default retry branch from the adjacent terminal message cases. The Promise witness distinguishes synchronous validation from awaiting. | +| ORC-014 controlled-premise handoff | No native-host claim is made. Fake storage controls the durable decision cut; real IndexedDB completion remains separate receiving work. | + +## Remaining scope + +The table is exhaustive only for its eight error fixtures and four hook +answers. It does not prove every string matching edge case in the default +policy, every retry count, or every interleaving. The existing second-error +witness covers count one; the row-local cleanup matrices cover peer admission +and storage failures. The coverage map keeps native storage and broader offline +recovery limits with their owners and RFC #1659. + +## Final production follow-up + +The production and documentation follow-up executable commit is +`c4cd2dd8de085a8cc1680f7eb8bc8a67b740428e`. It keeps the retry decision +law and the oracle unchanged. The settlement oracle passed 51/51 cases. A +package run excluding the separate uncommitted leadership/FIFO experiment +passed 217 tests in 17 files; package typecheck, source and oracle ESLint, +Prettier, and diff checks passed. + +The false-as-delegation, Promise-await, AbortError, 400, 403, 422, and 404 +temporary mutants were rerun on that executable commit. Each failed at the +stored-decision assertion. Direct seed/path replay again reached the +ordinary/false assertion. The temporary source edits were restored exactly; +none of these results relies on a timeout or a setup error. No later executable +change is part of this record. diff --git a/docs/contributing/oracle-reviews/pr-1592-row-local-retry-hook.md b/docs/contributing/oracle-reviews/pr-1592-row-local-retry-hook.md new file mode 100644 index 0000000000..fe21cd87ae --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-1592-row-local-retry-hook.md @@ -0,0 +1,89 @@ +# PR #1592: retry-hook failure stays with its row + +## Reviewed executable state and law + +- Reviewed executable commit: `20a75a144bc268ebf452e045b157903fd6f72e51`. +- Primary owner: `packages/offline-transactions/tests/transaction-settlement-oracle.property.test.ts`. +- Contract: `packages/offline-transactions/README.md` and `docs/guides/offline-transactions.md` at that commit. The maintainer chose row-local handling for a throwing `shouldRetry` hook, replacing the earlier fail-stop policy in this PR. + +If `shouldRetry` throws or returns a value other than `true`, `false`, or +`undefined`, the executor records terminal rejection for that transaction, +removes its outbox row, and rejects its caller with the hook failure. After +acknowledged deletion it continues queued work in the same executor. A commit +started during cleanup may join the queue, but no later provider starts before +the head is removed. A peer whose outbox write is visible but unacknowledged +cannot run. If the terminal marker or deletion fails, the executor still stops +with the storage error and holds queued work; the head caller retains the hook +error. These statements concern controlled storage acknowledgement, not native +power-loss durability or exactly-once provider effects. + +The old fail-stop rule predicted that a post-fault `commit()` would reject +before storage and a previously durable peer would wait for restart. The new +rule predicts a durable, pending post-fault peer during held cleanup and its +same-executor fulfillment afterward. The six marker-read, marker-write, and +delete cuts for thrown and invalid hooks distinguish those predictions. + +## Executed review and checker controls + +The original fail-stop implementation failed all six post-fault admission +histories at the intended `durable` versus `settled` assertion: it rejected the +new caller before storing its row. The direct queued-peer witness also rejected +its batch with the hook error instead of resolving after the peer ran. These +were assertion failures. Its started-peer progress cases timed out, which is +liveness evidence but is **not** counted as a checker-calibration kill. + +With the row-local implementation, 22 focused hook, admission, started-write, +and storage-failure cases passed. The package run excluding the separate, +uncommitted equal-time FIFO experiment passed 215 tests in 17 files. Package +typecheck, production-source ESLint, changed-file formatting, and diff checks +passed. The pre-commit `pnpm` wrapper failed because its registry returned 403; +its `lint-staged` executable ran directly and passed before the executable +commit. The ordinary commit hook was then disabled for that commit only. + +Two hostile controls tested the new checker: + +1. A repair that rescanned visible outbox rows after head deletion passed the + earlier matrix. It failed both `visible-across-cleanup` histories at the + provider-call comparison: it ran a peer whose storage write was visible but + not acknowledged. The unchanged row-local implementation passed those cuts. +2. A terminal-cleanup catch that rejected the queued peer ID with the head's + hook error passed the initial storage-failure matrix. Recording both + transaction ID and error made all three storage-failure cells reject that + mutant at the caller-identity assertion. The unchanged implementation passed. + +The failed marker and deletion cells preserve the old storage-stop law while +the successful-cleanup cells replace hook fail-stop. They compare exact outbox +IDs and phases, head error, peer provider calls, and whether the executor +accepts another batch. The public histories also compare `commit()`, +`isPersisted.promise`, and Collection rows. Cleanup gates and observed promises +are released in `finally`; `atOracleCheckpoint` bounds waits without replacing +the primary assertion failure. + +## Oracle guide audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 authority and limits | Pass. The maintainer's row-local decision replaces the old hook-fault policy. The README, guide, type comment, package skill, changeset, oracle prose, and coverage map now agree. Controlled-storage and restart limits are explicit. | +| ORC-002 independent judgment | Pass. `expectedHookFaultPeerAtHeldCleanup` and `expectedStartedPeer` derive pending, visibility, and provider eligibility from the contract's acknowledgement and FIFO premises. Neither imports executor classifiers or queue state. | +| ORC-003 distinguishable responsibilities | Pass. The opening contract, local relational models, finite history grammar, public driver, and cut-specific comparisons are described beside their code. | +| ORC-004 generated grammar controls | Not triggered by the new finite matrices. The owner's existing generated campaigns and replay controls are unchanged. The matrices enumerate throw/invalid, three cleanup cuts, and the write visibility and acknowledgement cuts stated above. | +| ORC-005 path and observation | Pass. Public transactions run through `persistTransaction`, real outbox storage, scheduler, provider, and caller promises. Direct executor witnesses isolate queue progress and terminal storage failure. | +| ORC-006 checker calibration | Pass for the old fail-stop assertion and both hostile mutants at their intended comparisons. Started-peer timeouts are reported separately. | +| ORC-007 fixed/random replay | Not triggered by these finite additions; the owner's existing fixed and random properties retain their replay inputs. | +| ORC-008 stateful-model minimality | Not triggered: the new relation has no mutable reference-model state. Visibility, acknowledgement, and head removal are separate only because legal next events distinguish them. | +| ORC-009 vocabulary mapping | Pass. `outbox replay`, `offline executor restart`, `rejection-pending`, acknowledgement, and settlement use glossary meanings. The test-only `StartedWriteCut` combines storage visibility and write acknowledgement, not a production state enum. | +| ORC-010 failure fidelity and cleanup | Pass. Gates force the stated events, checkpoints bound hangs, and cleanup preserves primary failures. The mutant kills were assertion failures rather than setup or cleanup failures. | +| ORC-011 independent second formulation | Not triggered: no shared reference classifier or equivalent model pair was introduced. The direct queue witness is a second production boundary, not a copied model. | +| ORC-012 review evidence | This versioned record names the exact executable commit, applicable outcomes, controls, and limits. It follows the executable commit; no executable file is part of this record change. | +| ORC-013 distinguishing boundary witness | Pass. A visible, unacknowledged peer across head deletion is the nearby case that rejects the rescan mutant; acknowledged peers run after deletion. The storage-failure cells reject an unconditional “continue after hook fault” design. | +| ORC-014 controlled-premise handoff | No native-host claim is made. Fake storage controls event order; native adapter completion and power-loss behavior remain separate receiving work. | + +## Remaining scope + +The controlled adapter does not cover a `set()` that mutates storage and then +rejects its acknowledgement. A replacement executor that finishes startup +before an old executor's held write acknowledges can miss that later row; the +started-write oracle promises same-executor progress after successful cleanup, +not a cross-executor handoff in that history. The coverage map keeps these +limits with the offline settlement and leadership owners. General offline +recovery policy remains with RFC #1659. diff --git a/docs/contributing/oracle-reviews/pr-1879-visible-replay.md b/docs/contributing/oracle-reviews/pr-1879-visible-replay.md new file mode 100644 index 0000000000..a8e41904cb --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-1879-visible-replay.md @@ -0,0 +1,45 @@ +# Visible web replay under a false online hint + +Baseline: `ae2eb3fbf3a314a7f043f0d963fd336d8c1f1ec8` (`origin/main`, +2026-09-29). Review source: [PR #1879](https://github.com/TanStack/db/pull/1879) +at `accea99a8d4fd860d4daf9b05e248290bea54a7f`. This record describes the +focused oracle added in the PR follow-up. It does not establish real browser +connectivity. + +The contract is eligibility to **attempt** a provider call. A visible elected +web executor can try its durable outbox despite a false `navigator.onLine` hint. +A hidden executor with the same hint waits. Provider success removes the outbox +row and settles the transaction. Real request failure retains the normal retry +policy; visibility does not guarantee connectivity. + +The focused oracle admits a durable write while hidden with a false hint, +then changes visibility and signals either `visibilitychange` or explicit +`notifyOnline()`. It compares provider calls, public `peekOutbox()` contents, +and caller settlement at both checkpoints. On baseline `main`, both histories +failed at the visible checkpoint: expected one provider call, observed zero. +With the detector change, both pass. The pre-fix detector is the hostile control +for this observation; both failures were assertion failures at the intended +checkpoint, not setup failures or timeouts. + +Guide audit: + +| Requirement | Evidence | +| --- | --- | +| ORC-001 | The policy above is the PR's proposed public behavior, grounded in the package's documented automatic retry contract. The fixture judges attempts only when the controlled provider can succeed. | +| ORC-002 | Expected zero/one provider calls and durable-row retention/removal come from that policy and the successful provider response, not the detector predicate or queue implementation. | +| ORC-003 | The oracle header identifies the contract, independent observation model, two-event history grammar, real production driver, and checkpoint comparison. | +| ORC-004 | Not triggered: the two histories are fixed, not generated. | +| ORC-005 | The driver uses real `OfflineExecutor`, `WebOnlineDetector`, transaction commit, and storage. The provider callback and public outbox/caller observations are reached. | +| ORC-006 | Baseline `main` is a hostile implementation: both cases fail with zero provider calls at the intended visible checkpoint. | +| ORC-007 | Not triggered: this is a fixed regression, not an important generated property. | +| ORC-008 | Not triggered: no stateful reference model changed. | +| ORC-009 | The model's hidden/visible cuts map directly to browser visibility; the durable row is the outbox entry. No model-only state is introduced. | +| ORC-010 | The test bounds persistence and commit observations, rejects any unfinished caller in cleanup, and releases executor, Collection, and global stubs. | +| ORC-011 | No different semantic formulation was needed for a named shared model/production fault. The direct browser detector truth table is an adjacent check. | +| ORC-012 | This record ties the RED observation to the exact baseline. The test and coverage map state its contract, scope, and limits. | + +The test uses controlled browser globals and fake storage. It does not exercise +Chromium's actual network hint, native storage, genuine offline retry timing, +React Native, or leadership transfer. Existing leadership and retry owners cover +their respective rules. The complete offline package suite passed after the +change: 199 tests in 16 files, plus package typecheck. diff --git a/docs/contributing/oracle-reviews/pr-1902-change-event-history.md b/docs/contributing/oracle-reviews/pr-1902-change-event-history.md new file mode 100644 index 0000000000..d240de8c5b --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-1902-change-event-history.md @@ -0,0 +1,288 @@ +# PR #1902 change-event and queued-admission oracle review + +## Reviewed state + +- Base commit: `f473a36201abe9af43d36a83492a2649668756d8`. +- Original PR head: `8c763b8c531297ec615e157c3669a907a4b6eb59`. +- Corrected implementation head: + `78786469a0a49db3d9349ac378cb2bce96d09afb`. +- Reviewed change: PR #1902 plus the queue-admission and cancellation repair at + that corrected implementation head. +- Primary executable owners: + `packages/db/tests/change-event-history-oracle.test.ts` and + `packages/db/tests/collection-state-retention-oracle.property.test.ts`. + +This record is committed after the implementation it audits so it can name that +immutable tree directly. + +## Claim and limits + +Issue #1901 and the public change-message contract require a mirror that applies +every settled insert, update, and delete to agree with its Collection. The +bounded change-event oracle owns legal one-key local mutation histories of +length one through four from an absent row. It crosses same-turn batching with +sequential settlement and compares the Collection and mirror at applied +settlement. + +Queued source admission is a separate refinement. Duplicate-key validation +uses the authoritative state produced by retained source rows plus earlier +queued sync transactions. If cancellation removes a queued delete, every +remaining transaction is revalidated before application. An insert that has +become a duplicate rejects and cannot replace the retained row. The same rule +rejects a different insert behind an earlier queued insert and a repeated +insert in one sync transaction. + +The bounded local history does not establish initially present rows, multiple +keys, persistence failure, callback batch shape, or intermediate publication. +The queued refinement uses controlled in-memory persistence and sync actions. +It does not establish provider transport cancellation, unbounded queue size, or +adapter-specific persistence behavior. + +## ORC-001 through ORC-011 + +| Requirement | Outcome | +| --- | --- | +| ORC-001: contract authority and limits | Pass. Issue #1901 records the settled mirror-agreement bug and expected public result. The Collection duplicate-key contract already rejects a different insert for an authoritative key. The executable openings and this record state the limits. | +| ORC-002: independent judgment | Pass. The bounded oracle uses one optional value and a change-message Map. The queued refinement uses retained-key membership and transaction order. Neither imports Collection state classifiers or duplicate-detection helpers. | +| ORC-003: distinguishable responsibilities | Pass. The change-event opening names its contract, optional-value model, bounded grammar, local-only production driver, applied-settlement checkpoint, and omissions. The state-retention owner already separates its source Map, lifecycle driver, and row/event checks. | +| ORC-004: generated-history grammar controls | Pass for the bounded enumeration. The issue witness `insert -> delete -> insert` is reconstructed. Removing batched execution loses the reported same-turn path; sequential execution is the control. The range is one key and lengths one through four. Insert-while-present and update/delete-while-absent are excluded. The queued additions are fixed controlled histories and make no generated-history claim. | +| ORC-005: production path and observation | Pass. The bounded driver calls public local-only mutations and `subscribeChanges`, awaits persistence, and checks public rows plus the mirror. The queued driver uses real sync actions, a held mutation, abort signals, applied receipts, retained source state, and public reads. | +| ORC-006: checker calibration | Pass. On the base, four batched histories failed with `DuplicateKeySyncError`; the PR made all 22 history cells green. A dropped-reinsertion mirror is rejected explicitly. On the original PR head, the controlled abort produced `delete=aborted`, `insert=fulfilled`, and retained/public value `2` instead of `0`. The queued-insert and same-transaction duplicate controls also failed before the follow-up. | +| ORC-007: fixed/random campaigns and replay | Not applicable to the bounded enumeration and fixed controlled histories. The state-retention owner's existing important generated properties retain their fixed/random campaigns and replay interface. | +| ORC-008: stateful-model minimality | Pass. Presence plus the optional row value determines the legal next local action and final expected value. Queued admission additionally retains transaction boundaries because cancellation can remove one transaction atomically. | +| ORC-009: vocabulary mapping | Pass. A sync transaction is the work between `begin()` and `commit()`. Application makes its writes visible. Applied receipts, optimistic transactions, public rows, and change messages retain the glossary meanings. | +| ORC-010: failure fidelity and cleanup | Pass. The RED comparison recorded receipt outcomes and both retained and public values in one observation. Controlled gates release in `finally`; subscriptions and Collections clean up even after assertion failure. | +| ORC-011: independent second formulation | Not applicable. No remaining shared-fault hypothesis requires another formulation. The mirror and direct public read are complementary observations, not claimed as two independent semantic implementations. | + +ORC-012 is satisfied by this versioned record, its exact corrected +implementation head, and its link from `docs/contributing/oracle-coverage.md`. + +## External-review reconciliation + +CodeRabbit correctly found the canceled-delete admission bug. Its proposal to +revalidate the queue was directionally correct. The review did not test two +adjacent regimes: a predecessor that was itself queued, and duplicate inserts +against queued or same-transaction state. The refined oracle preserves those +cases with the original retained-row witness. + +The review's security wording was appropriately conditional. The evidence +establishes Collection-local integrity impact for callers able to provide sync +operations and abort timing. It does not establish an authentication bypass, +cross-service exposure, or an untrusted actor with those capabilities. + +The vendor docstring warning has no repository-configured 80% threshold and +does not identify a missing product contract. It is not a merge-blocking +finding for this repository. + +A follow-up prep review found three additional defects in the first corrected +implementation. Deterministic RED probes measured 2,144 queued-operation +inspections for a 64-row snapshot with a 128-inspection linear allowance, +showed cancellation rejecting both a valid identical echo and a hydration +replacement, and showed abort-before-commit making the active transaction +unaddressable. The corrected implementation uses an incremental queued +projection, replays cancellation through the normal insert classifier, and +retains invalidated active transactions until `commit()` returns their rejected +receipt. + +## Verification boundary + +The corrected implementation head passed the two primary executable owners and +five adjacent lifecycle, metadata, reconciliation, hydration, and load-subset +owners: 223 tests. The complete DB oracle campaign passed 41 files and 2,197 +tests. Package build, TypeScript, changed-file ESLint and Prettier, and +`git diff --check` also passed. + +## Follow-up review: mirror-agreement class coverage + +- Reviewed test head: `bbde36962534f55d1e0515224ef5bb070ce5aad3`. +- Production remains at the corrected implementation above. This follow-up + changes only oracle tests, their replay registration, and documentation. +- The change-event owner now executes 366 bounded history cells: all legal + operation-kind/key histories of lengths one through four for one key from + absence, and lengths one through three for two keys from each initial-presence + state. Values are deterministic and distinct by step. Each history runs in + same-turn and sequential modes. Two 100-run campaigns also + explore up to twenty legal actions over four keys. One uses seed `1902`; the + other has no seed unless a direct seed-and-path replay is requested. +- The queued-sync fixture builds its initial mirror from its declared source + rows. It checks that mirror against complete public rows at every delivered + callback and after the queued work settles, including canceled predecessors. + +| Requirement | Follow-up outcome | +| --- | --- | +| ORC-001: contract authority and limits | Pass. Issue #1901 and the public change-message contract authorize mirror/public agreement. The executable owner limits its claim to local-only persistence and named queued-sync cases; it does not claim arbitrary adapters or unbounded histories. | +| ORC-002: independent judgment | Pass. A plain keyed Map computes expected complete rows from legal actions. A second Map applies delivered change messages. Neither imports Collection merge or change-composition rules. The queued fixture derives its initial mirror from declared input, not from production output. | +| ORC-003: distinguishable responsibilities | Pass. The change-event owner names the contract, keyed Map model, bounded and generated grammar, local-only Collection driver, and callback/settlement comparisons. The queued owner keeps its source model and controlled driver separate. | +| ORC-004: generated-history grammar controls | Pass. The `insert -> delete -> insert` witness is reconstructed. Removing same-turn mode loses the original failure; removing sequential mode loses settled-prefix checks. Initial presence and two keys expose replacement against retained rows and independent-key interactions. Bounded limits are one key/four actions and two keys/three actions. The generated range is four keys/twenty actions. The grammar rejects insert of a present key and update or delete of an absent key. These are local mutation histories, not arbitrary sync-adapter histories. | +| ORC-005: production path and observation | Pass. The driver calls public `insert`, `update`, `delete`, `subscribeChanges`, and public Collection reads. It compares complete keyed row values with the model after persistence, at every sequential prefix, and against the event mirror at every delivered callback. The queued driver checks the same relation at its controlled publication cuts. | +| ORC-006: checker calibration | Pass for the reported fault. A temporary production mutant omitted the absent-key pre-sync value; bounded case `one-key-batched-8` failed at mirror agreement with `[]` against the expected row for key 1. The mutant was removed. Missing, extra, and wrong mirrored rows also fail the comparison control. A separate tentative change-composition mutant survived this witness; branch reach was not established, so it is not counted as a kill. | +| ORC-007: fixed/random campaigns and replay | Pass. The new generated owner runs the identical arbitrary, production driver, recorder, comparison, and 100-run budget with fixed seed `1902` and a seedless campaign. Direct guarded replay with seed `1902` and path `0` selected exactly the named property and recorded one completed witness. The retention owner now runs matching fixed and seedless 100-run campaigns; its direct replay also passed. The optimistic-history fixed campaign now matches its 100-run random budget. | +| ORC-008: stateful-model minimality | Pass. Per-key presence determines legal next actions; per-key row value determines the promised observation. Two states that disagree on either can be distinguished by a legal next action or the next public-row check. The model needs no production queue or publication state. | +| ORC-009: vocabulary mapping | Pass. The local driver waits for optimistic-transaction persistence, not an applied sync receipt. It calls delivered inserts, updates, and deletes change messages and compares the public Collection rows. The Map is a reference model, not a production Collection state. | +| ORC-010: failure fidelity and cleanup | Pass. Both changed drivers retain the primary law failure, collect separate cleanup failures, and release their subscriptions and Collections. The queued driver also releases the held persistence gate. The bounded and generated histories retain their input and checkpoint in the failing test name or fast-check replay. | +| ORC-011: independent second formulation | Not triggered. No identified semantic fault is plausible in both the plain Map transition and the change-message fold. The three-way comparison of model, public rows, and event mirror is complementary evidence, not a claim that two production paths are independent. | + +ORC-012 is satisfied for the follow-up by this append-only entry and its exact +reviewed test head. The original review and its older head remain intact. + +The two primary owners passed 434 tests. The complete DB oracle campaign passed +41 files and 2,544 tests. TypeScript, changed-file ESLint and Prettier, guarded +seed-and-path replay, and `git diff --check` passed. These checks give bounded +and sampled class-level protection, not a proof over every future schedule or +provider implementation. + +## Follow-up review: five queued-sync findings + +- Reviewed implementation and test head: + `d1a38b59b88ee914a212e4fd2581c8b9ac902ed5`. +- Starting head for the external review: + `d0d76e3bb4645ee1d33faadf40eaf4289d777504`. +- This append-only entry covers all five findings in the user's review. The + primary queued-sync owner remains + `packages/db/tests/collection-state-retention-oracle.property.test.ts`. + Focused `packages/db/tests/db-client.test.ts` cases drive the separate + DbClient hydration boundary. + +| Finding | RED evidence on starting head | GREEN boundary on reviewed head | +| --- | --- | --- | +| R01: refresh recovery | A synthetic committed-invalid queue entry remained queued with its applied receipt pending, and replay returned before assigning the rebuilt projection. Current production refresh callers cannot form this state. | Refresh rejects and retires the invalid entry while retaining a valid queued sibling. A later duplicate classification sees that sibling in the rebuilt projection. This is internal hardening, not a claimed public reach witness. | +| R02: doomed open write | Canceling a queued delete invalidated an open insert. A later insert threw `DuplicateKeySyncError` synchronously from `write()`. | The later write cannot revive the doomed transaction; `commit()` returns the rejected applied receipt. Cancellation reaches the problematic state, even though the review named commit-time refresh. | +| R03: bulk hydration | A late hydration chunk overwrote a queued adapter insert and resurrected a queued adapter delete while a local mutation held application. | Both controlled histories retain adapter authority in source and public rows after applied settlement. `initialData` runs before sync starts, so the review's initial-data variant is not a reachable queued path. Projection-aware row classification alone would not prevent stale hydration from winning; the seed is excluded for keys with committed adapter work. | +| R04: cancellation work | A queue-entry counter saw 2,275 indexed reads for 64 dependent inserts, above the 512-read bound. Counting only operation iteration had falsely passed because each rebuild stopped at the first invalid transaction. | One replay retires all invalid committed dependents. The probe also includes preceding writes inside those dependents and a valid queued sibling; it checks receipts and complete public rows. The changed-key undo avoids cloning the whole projection for each transaction. The counter bounds queue inspection, not every cost of optimistic recomputation. | +| R05: impossible guard | Source control flow returned an invalid transaction only when `committed` was true. The refresh check for `!committed` was unreachable. | The guard is gone; direct source review and static checks validate the cleanup. No artificial product RED is claimed for dead code. | + +The missing test dimensions were, respectively: a committed-invalid refresh +recovery state; a write after open-transaction invalidation; hydration while +same-key adapter work was queued; cancellation work rather than admission work; +and a direct control-flow check. The new fixed histories preserve the RED +witnesses. The existing generated histories were not widened by this follow-up. + +| Requirement | Follow-up outcome | +| --- | --- | +| ORC-001: contract authority and limits | Pass. The established duplicate-key and applied-receipt contracts govern R01 and R02. The existing late-hydration adapter-authority test governs R03. The queued-work bound protects the PR's incremental-admission design. The synthetic recovery probe is not offered as a current public path, and no test here proves arbitrary provider schedules or total cancellation runtime. | +| ORC-002: independent judgment | Pass. The queued owner derives expected rows from declared source rows and delivered change messages. Focused hydration expectations come from adapter-over-seed authority, not the production classifier. The queue-read counter measures production work but does not compute expected rows. | +| ORC-003: distinguishable responsibilities | Pass. The state-retention owner already states its source Map, legal sync histories, controlled driver, public observations, and applied-settlement checkpoint. The hydration cases are focused boundary regressions rather than a new oracle model. | +| ORC-004: generated-history grammar controls | Not triggered for this follow-up: it adds fixed controlled histories and makes no new generated-grammar claim. The owner's existing generated grammar is unchanged. | +| ORC-005: production path and observation | Pass. The driver calls real sync `begin`/`write`/`commit`, aborts a real signal, and checks applied receipts, public rows, retained source data, and the event mirror. Hydration calls `DbClient.applyCollectionChunk`. R01 is explicitly an internal synthetic-state probe. | +| ORC-006: checker calibration | Pass for the claimed repairs. Each behavioral probe failed on the starting implementation and passed on the reviewed head. The R04 counter killed the repeated-rebuild design at a named work checkpoint; the corrected probe distinguishes queue traversal from early operation-loop termination. R05 uses source evidence, not a product mutant. | +| ORC-007: fixed/random campaigns and replay | Not triggered by the new fixed controls. The existing state-retention fixed and seedless campaigns ran in the package oracle campaign. The direct replay interface is unchanged; its prior checked replay is recorded above. | +| ORC-008: stateful-model minimality | Not triggered: this follow-up does not add or remove reference-model state. The new controlled histories distinguish dependent transactions, valid siblings, and a retained source row without copying the production queue into expected results. | +| ORC-009: vocabulary mapping | Pass. The new cases distinguish sync transactions, optimistic transactions, applied receipts, source rows, hydration seeds, change messages, and public rows. No new model-only production synonym is introduced. | +| ORC-010: failure fidelity and cleanup | Pass. The queued oracle helper preserves the primary mismatch and separate cleanup failures. The synthetic refresh and focused hydration probes now do the same, release their held gates or receipts, and clean up Collections. | +| ORC-011: independent second formulation | Not triggered. The review did not identify a semantic fault plausibly shared by the source Map and change-message mirror for the queued law. Hydration authority is tested against both retained and public rows; these are complementary observations, not claimed independent formulations. | + +ORC-012 is satisfied by this append-only review entry, the exact reviewed code +head above, and the coverage-map link. The two edited test files passed 108 +tests. The DB oracle campaign passed 41 files and 2,547 tests. Package build, +TypeScript, changed-file ESLint and Prettier, and `git diff --check` passed. + +## Follow-up review: applied adapter authority over hydration + +- Reviewed implementation and test head: + `95f5a77cb648f9961bf850f0983dd957c3fbe478`. +- Starting production head: `cc12dea59ce1b81faec4e1b01f5b651e6c0272f3`. +- Primary executable owner: + `packages/db/tests/db-client-hydration-authority-oracle.test.ts`. + +The previous queued-hydration controls did not cross an adapter delete or +truncate **after its applied receipt settled**. A late chunk then restored the +deleted row or the entire truncated snapshot. The new oracle states the +adapter-over-seed law independently of the queued projection: for each key it +recomputes the last applied adapter decision, or uses the latest hydration seed +when no adapter decision exists. It compares retained source rows, complete +public rows, and a change-message mirror after every completed action, plus +mirror/public agreement in every delivered callback. + +On the starting production, four of thirteen bounded cases failed: hydration +after applied delete or truncate, both with and without an earlier chunk. +The delete witness retained an unwanted `after-target` row; the truncate +witness restored all three stale rows. The corrected implementation retains +adapter delete authority per key and global truncate authority for DbClient +Collections. The final fifteen history cells plus one reachability check pass. +The extra cells cover delete/reinsert, insert/delete, and truncate/reinsert. + +| Requirement | Follow-up outcome | +| --- | --- | +| ORC-001: contract authority and limits | Pass. The existing late-hydration adapter-authority contract forbids seeds from superseding adapter work. The executable opening limits the claim to one or two immediately applied transactions, three keys, and up to two chunks. Queued work remains with the state-retention owner. | +| ORC-002: independent judgment | Pass. The reference scans a declared action history per key; it does not import or copy Collection's queued projection, authority lookup, or row classifier. | +| ORC-003: distinguishable responsibilities | Pass. The opening states the law and limits; the per-key scan is the model; the bounded cases are the grammar; DbClient hydration and sync actions are the driver; retained, public, and mirrored rows are the refinement check. | +| ORC-004: grammar controls | Pass for the bounded enumeration. The original applied-delete witness is reconstructed. Removing after-receipt hydration loses all four RED cases; removing truncate loses global-absence coverage; removing the untouched peer loses the selective-admission control. The domain crosses four one-transaction decisions with three hydration positions, plus three two-transaction histories. A different duplicate insert behind an authoritative insert is excluded by the existing duplicate-key contract. | +| ORC-005: production path and observation | Pass. The driver calls `DbClient.collection` with initial data, real sync `begin`/`write`/`truncate`/`commit`, and `applyCollectionChunk`. It checks after applied receipts and hydration calls, and captures callback-time mirror/public rows. | +| ORC-006: checker calibration | Pass. The unchanged production failed at the intended retained-row comparison in four cells, including the named delete and truncate mistakes. None failed during setup or cleanup. The corrected implementation passes all cells. | +| ORC-007: fixed/random campaigns and replay | Not triggered. This is a finite bounded enumeration, not an important generated property. | +| ORC-008: stateful-model minimality | Not triggered. The reference recomputes each expected row from the immutable declared history rather than maintaining a transition model. | +| ORC-009: vocabulary mapping | Pass. A model adapter decision maps to one sync transaction after its applied receipt; hydration seed, source row, public row, and change message retain their glossary meanings. | +| ORC-010: failure fidelity and cleanup | Pass. The driver retains the primary comparison failure, records separate unsubscribe and client-cleanup failures, and releases both resources. | +| ORC-011: independent second formulation | Not triggered. No plausible fault shared by the declarative last-authority scan and production's incremental admission was identified. The source/public/mirror comparison supplies complementary observations, not a second semantic formulation. | + +Under ORC-012's class-closure condition, the bounded **applied** authority +class above is closed; the broader hydration authority class is not claimed +closed. The coverage map names the queued and applied owners and their limits. +Arbitrary stream orders, a later authoritative hydration epoch, and unbounded +retention remain outside this oracle's claim. With no hydration-completion +boundary, DbClient retains one tombstone per distinct adapter-deleted key until +Collection cleanup; non-DbClient Collections do not allocate that set. + +The complete DB oracle campaign passed 42 files and 2,563 tests. The four +focused owners passed 493 tests. Package build, TypeScript, changed-file +ESLint and Prettier, and `git diff --check` passed. + +## Follow-up review: queued partial-update dependency + +- Reviewed implementation and test head: + `0d580682cce96e829cb0152d9f8403d94904d81d`. +- Starting head: `9f719cfcac6fb53c7b2ac9952fa8999e302f0e2a`. +- Primary executable owner: + `packages/db/tests/collection-state-retention-oracle.property.test.ts`. + +A partial update admitted while a queued source row exists depends on that row. +If cancellation removes its only earlier source, the update's whole sync +transaction rejects with `AbortError`; downstream transactions that depended +on its writes reject as well. An update admitted against an absent key remains +an independent upsert, and a full-row update can replace an absent row. This +preserves the persisted-source partial-upsert contract rather than changing all +missing-key updates into errors. A surviving identical insert echo can become +the source row after an earlier insert is canceled, so its later update remains +valid. + +The fixed witness was RED on the starting head: canceling the queued insert +left both committed and open dependent updates fulfilled and materialized a +row. The corrected head passes those witnesses, a same-transaction insert and +update control, independent-upsert controls, a surviving-echo control, and an +atomic multi-key/transitive-cancellation control. The generated driver crosses +three source origins, two update modes, cancellation, open versus committed +child receipts, and bounded row values. All 24 structural combinations have a +fixed witness; matching 100-run fixed-seed and seedless campaigns vary values. + +| Requirement | Follow-up outcome | +| --- | --- | +| ORC-001: contract authority and limits | Pass. The user-approved dependency rule governs cancellation; existing persisted-source tests establish that absent partial updates may upsert. The claim covers direct Collection sync transactions parked before application, not arbitrary provider or persistence-wrapper scheduling. | +| ORC-002: independent judgment | Pass. The expected row and receipt outcomes are derived from the declared retained/queued source history and update mode. The reference does not read the pending projection, operation flags, or production classifier. | +| ORC-003: distinguishable responsibilities | Pass. The queued-update opening states the law and limits; the scenario's source history determines the expected row and receipt outcomes; its axes are the grammar; real sync actions plus a held mutation are the driver; applied receipts, retained rows, public rows, and the callback mirror are compared after the drain. | +| ORC-004: grammar controls | Pass. `queued-insert -> update -> cancel` reconstructs the reported case; queued upsert, retained row, and surviving echo distinguish source ownership. Removing the source-origin or cancellation axis loses the dependency witness; removing update mode loses the full-row independence control; removing receipt phase loses the open-transaction cut. The bounded domain is one key, two transactions, values -2 through 2, plus focused same-transaction and transitive/multi-key cases. Conflicting inserts and arbitrary nested `begin` calls are excluded. | +| ORC-005: production path and observation | Pass. The driver calls real `begin`, `write`, `commit`, and abort signals. It observes receipt outcome, retained and public complete rows after application, and mirror/public agreement at every delivered callback. | +| ORC-006: checker calibration | Pass. Temporarily removing the dependency revalidation check made the fixed-seed property fail by assertion after six tests. It shrank to queued insert, committed partial update, then cancellation with seed `190201` and path `5:0:1:1:1`. The mutant was restored; this was neither timeout nor setup failure. The original focused witness also failed at receipt and row comparison before the fix. | +| ORC-007: fixed/random campaigns and replay | Pass. Both campaigns use the same arbitrary, driver, observations, and 100-run budget. The fixed seed is `190201`; the other is seedless unless replayed. Direct guarded replay of seed `190201` and path `5:0:1:1:1` recorded the named property, the same path, two completed replay runs, and `failed:false` on the corrected head. | +| ORC-008: stateful-model minimality | Not triggered. The expected result is recomputed from each immutable scenario rather than maintained in a stateful reference model. | +| ORC-009: vocabulary mapping | Pass. The scenario's `source` arm identifies the retained source row, a queued adapter insert, or an independent queued upsert. `updatePhase` maps to an open or committed sync transaction; the applied receipt and public row retain their glossary meanings. | +| ORC-010: failure fidelity and cleanup | Pass. The queued harness preserves the primary mismatch separately from cleanup failures, releases the held mutation, and cleans up the subscription and Collection. Fast-check retains the failing input, seed, and shrink path. | +| ORC-011: independent second formulation | Not triggered. The reviewer did not name a fault plausibly shared by the source-history reference and production's queued projection. Existing persisted-source upsert tests and the full-row/independent-upsert controls reject the initial overbroad design. | + +Under ORC-012, the bounded direct-sync dependency class above has no known +reachable counterexample. The original cancellation witness and adjacent +upsert, full-row, echo, open-transaction, and transitive witnesses are owned by +the state-retention oracle. Persistence-wrapper replay remains owned by the +SQLite persisted histories, and Electric stream completeness remains owned by +the Electric oracle; neither is claimed closed by this Collection test. The +coverage map names these owners and their limits. This append-only record names +the exact reviewed implementation head; it is committed afterward. + +The complete DB oracle campaign passed 42 files and 2,575 tests; the focused +owner passed all 80 tests. The full DB runtime suite passed 186 files and +6,543 tests with Vitest's background typecheck disabled. Electric runtime +oracles passed 211 tests and persisted-source tests passed 437 tests. The DB +package build, direct `tsc --noEmit`, changed-file ESLint and Prettier, and +`git diff --check` passed. An earlier full Vitest run with background typecheck +enabled passed its runtime assertions but exited nonzero: two new test +type-narrowing errors were then corrected, while separate diagnostics in the +untouched subset-error test file remain outside this change. diff --git a/docs/contributing/oracle-reviews/pr-1907-accepted-delete-ownership.md b/docs/contributing/oracle-reviews/pr-1907-accepted-delete-ownership.md new file mode 100644 index 0000000000..73477741ee --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-1907-accepted-delete-ownership.md @@ -0,0 +1,75 @@ +# PR #1907 accepted-delete ownership oracle review + +## Reviewed state and law + +- Starting head: `e1cfd38761c9901e693568bc4394219e05b2e42c`. +- Test-only RED commit: `99d1ed93cf3c2447a7b55d7f8f000e68324b31bb`. +- Corrected implementation head: `f7c13f3689341c320ccf373fb4ec2d9dfbfc8591`. +- Primary executable owner: + `packages/db/tests/collection-state-retention-oracle.property.test.ts`. +- Independent model and production driver: + `packages/db/tests/optimistic-history-oracle.ts`. + +This record is committed after the implementation it reviews so it can name +that immutable code commit. An accepted delete remains independently owned +under a later reinsert and its dependent edit. If the reinsert fails, the +dependent edit disappears and the accepted delete still hides the old source +row. If the reinsert succeeds, the accepted edit stays visible. The order in +which the delete and edit settle does not change either result. + +The bounded matrix starts with one source row. It authors a direct delete, +reinsert, and dependent edit on the same key. It crosses both delete/edit +settlement orders, both reinsert outcomes, and truncate versus no truncate +before the reinsert outcome. The driver compares complete public Collection +rows, change-message publications, and a downstream live-query Collection +after every step. It asserts that all three local operations, the dependency, +all three settlements, and the selected truncate actually occurred. + +## RED, repair, and GREEN + +On the starting head, the new matrix failed exactly one of eight cells: +delete settled before edit, reinsert rejected, no truncate. At the reinsert +rollback checkpoint, the event replica published the old source row +`{ id: 1, a: 0, b: 0, c: 0 }` while the independent model required no row. +The failure was an assertion at the intended public publication checkpoint, +not a setup error or timeout. A truncate in that order already removed the +row, so its green result did not excuse the non-truncate failure. + +The completed dependent update had been clearing the pending accepted delete +and its direct-retention marker merely because it was processed later in +creation order. The corrected implementation keeps that delete contribution +while the update has an unconfirmed insert dependency. The active insert still +overlays the delete. Insert acceptance removes the delete; insert failure +removes the dependent upsert and exposes the delete. This changes ownership of +the accepted contributions, not public mutation policy or the reference model. + +The corrected head passed all eight matrix cells and all 88 state-retention +tests. The complete DB oracle campaign passed 42 files and 2,601 tests. The +full DB runtime suite passed 186 files and 6,588 tests with Vitest background +typecheck disabled; direct TypeScript checking passed separately. The DB build, +changed-file ESLint, Prettier, and `git diff --check` also passed. + +## ORC-001 through ORC-011 + +| Requirement | Outcome | +| --- | --- | +| ORC-001: contract authority and limits | Pass. The established whole-row optimistic intent and insert-dependency law governs the fallback. The matrix claims the eight stated direct-operation histories, not arbitrary later source acknowledgement or every order of reinsert outcome versus sibling settlement. | +| ORC-002: independent judgment | Pass. The model folds authored intents over a plain source Map in settlement order. It does not read CollectionState's pending maps or repeat their retention classifier. | +| ORC-003: distinguishable responsibilities | Pass. The owner states retained-state law and names the optimistic companion. That companion separates the intent model, step grammar, real Collection driver, and per-step public refinement check. | +| ORC-004: grammar controls | The new matrix is bounded enumeration, not a new generated property. It reconstructs the reported delete-first rollback. Removing settlement order loses the RED cell; removing reinsert outcome loses the fallback law; removing truncate loses the captured-ownership control. The range is one key and three local operations. A dependent edit without a reinsert is excluded by construction. | +| ORC-005: production path and observation | Pass. The driver uses public `delete`, `insert`, and `update`, controlled mutation handlers, and real sync truncate. It compares public rows, callback-time event state, and downstream rows after each action. The count assertions prove the chosen path ran. | +| ORC-006: checker calibration | Pass. The unchanged starting implementation failed at the intended publication assertion in the delete-first/rejected/no-truncate cell. The corrected head passes that cell and the seven controls. | +| ORC-007: fixed/random campaigns and replay | Not triggered by this bounded matrix. The owner's existing important generated optimistic-history properties retain their matching fixed-seed and seedless campaigns and guarded seed-plus-path replay. Both campaigns ran in the DB oracle suite. | +| ORC-008: stateful-model minimality | No reference-model state changed. The existing dependency distinction is necessary: reinsert failure removes a dependent edit but cannot remove the independently accepted delete. | +| ORC-009: vocabulary mapping | Pass. The model's intent list represents optimistic transactions' authored snapshots. Source rows, public rows, change messages, settlement, and truncate replay retain the project glossary meanings. | +| ORC-010: failure fidelity and cleanup | Pass. `withHistoryCleanup` preserves the primary model/public mismatch, records separate cleanup errors, and releases held operations, subscription, downstream Collection, and source Collection. | +| ORC-011: independent second formulation | Not triggered. No plausible semantic fault shared by the plain intent model and production's pending-layer bookkeeping was identified. Public reads, callback-time events, and downstream rows are complementary observations. | + +ORC-012 is satisfied by this versioned record, its corrected implementation +head, and the coverage-map entry. The bounded settlement-order × outcome × +truncate class has no known reachable counterexample at its checked cuts. +This is not a proof over all future schedules. The matrix does not run the +local-only adapter, does not place reinsert settlement before both siblings, +and does not check arbitrary later source acknowledgements. The separate +external-review ledger also records an unquantified full-Transaction retention +concern; this repair does not claim to resolve it. diff --git a/docs/contributing/oracle-reviews/pr-1909-code-weight-loss-audit.md b/docs/contributing/oracle-reviews/pr-1909-code-weight-loss-audit.md new file mode 100644 index 0000000000..82794c4d36 --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-1909-code-weight-loss-audit.md @@ -0,0 +1,42 @@ +# PR #1909 code-weight loss audit + +Frozen reduction: Retire internal `Scheduler.onClear` notifications and pending-aware dependency objects because production has no callers. Dependencies then wait only for queued jobs. Replace synthetic tests with queued-job cancellation, exact failure identity, and recovery checks. Remove Effect's retained `pipeline` and `tracksPublishedRows` fields because the first is used once and the second duplicates the ordered-loader map. Remove unused `scheduleGraphRun` job ID and dependency overrides. + +The before source is commit `80421c8b`. The after source is code commit `b69c189c`. Two agents scanned the source versions separately after reading the field-lab loss-audit card and live-query architecture. The entries below are source-supported omissions from the frozen reduction. They do not decide whether to restore an item or whether a behavior regressed. + +| Source | Recovered distinction and pointer | Where dropped | Rule | +| --- | --- | --- | --- | +| Before scheduler | A dependency could block through `hasPendingGraphRun(contextId)` even without a queued job. Otherwise an absent dependency was satisfied (`80421c8b:scheduler.ts:126-144`). | Queued-only clause | Explicit rejection | +| Before scheduler | Rescheduling replaced a callback without moving its queue position; omitted dependencies retained the prior set. A running job left the map before reentrant work (`scheduler.ts:84-101,147-152`). | Cancellation clause | Compression | +| Before scheduler | `clear()` deleted the context before notifying a listener snapshot; `onClear()` returned an unsubscribe callback (`scheduler.ts:171-183`). | Retire `onClear` clause | Explicit rejection | +| Before scheduler | Nested publications shared a context. Recorded listener failure preceded later graph failure; clearing could not replace the primary error (`scheduler.ts:188-245`). | Failure-identity clause | Compression | +| Before Effect | The compiler's pipeline received its output operator before finalization and was cleared with graph/input references on disposal (`effect.ts:386-389,467-503,1096-1101`). | One-use field clause | Compression | +| Before Effect | `tracksPublishedRows` was set before the ordered loader started and selected callback-visible membership classification rather than immediate delta classification (`effect.ts:571-664,948-968,1156-1197`). | Duplicate-state clause | Explicit rejection | +| Before Effect | Ordered repair held callback delivery across participants and could transfer that hold to truncate replay. `publishedRows` retained the prior visible values (`effect.ts:409-428,819-861,924-968`). | Field-removal clause | Category mismatch | +| Before Effect | Source live-query builders became graph dependencies during subscription and were passed to the shared scheduler (`effect.ts:548-553,803-817`). | Queued-only clause | Compression | +| Before builder | `contextId` remained a live override beside the unused job ID and dependency overrides. The queued callback was fenced by sync-run generation (`collection-config-builder.ts:651-691`). | Override clause | Compression | +| Before builder | Window moves, demand settlement, ordered-load completion, initial sync, and source recovery each scheduled graph work (`collection-config-builder.ts:336-339,406-416,491-528,762-766,1091-1105`). | Override clause | Low salience | +| Before builder | Even an initially empty graph turn checked loaders, processed synchronous writes through D2, flushed, then evaluated readiness (`collection-config-builder.ts:571-645`). | Queue clause | Category mismatch | +| Before builder | Ordered-load settlement checked sync-run identity before changing failure state. Its final success scheduled a publication run (`collection-config-builder.ts:491-528`). | Recovery clause | Compression | +| Before scheduler test | The dependency matrix crossed source order, pending-aware state, and requeue. It required the dependent to see the final source run (`scheduler.test.ts:132-179`). | Synthetic-test replacement clause | Category mismatch | +| Before scheduler test | The clear-listener test checked listener snapshot mutation, multiple failures, first-error retention, and the next clear (`scheduler.test.ts:280-313`). | Retire `onClear` clause | Explicit rejection | +| Before scheduler test | Publication tests checked nested timing, discarded jobs, first-error precedence, thrown `undefined`, later delivery, and coherent dependent state (`scheduler.test.ts:182-278,579-727`). | Failure-identity clause | Compression | +| Before scheduler test | Collection/Effect cases crossed shared and derived sources plus write order; diamond and hybrid joins required one coherent dependent run (`scheduler.test.ts:1059-1367`). | Synthetic-test replacement clause | Category mismatch | +| Before scheduler test | Restart cases separated obsolete resolve/reject from replacement state; loader cases checked writes during graph turns (`scheduler.test.ts:1443-1631`). | Recovery clause | Compression | +| Before scheduler test | Repeated lexical aliases attempted every loader and kept the exact first falsy failure (`scheduler.test.ts:1633-1771`). | Failure-identity clause | Compression | +| After scheduler | Rescheduling replaces a callback while retaining queue position; omission of new dependencies retains the old set (`scheduler.ts:70-94`). | Queued-only clause | Compression | +| After scheduler | A running prerequisite leaves the map before its callback; requeueing it blocks its dependent. A no-progress pass throws (`scheduler.ts:98-145`). | Queued-only clause | Compression | +| After scheduler | Nested publications share the outer context. Graph work flushes afterward; the first listener error has precedence, and failure clears jobs/context (`scheduler.ts:173-226`). | Failure/recovery clause | Compression | +| After Effect | Compiler closures retain mutable subscriptions. Startup buffers changes until subscriptions exist, drains them to D2, then runs the graph (`effect.ts:389,519-750`). | One-use pipeline clause | Category mismatch | +| After Effect | The ordered-loader map supplies the tracking condition; the separate `publishedRows` map retains callback-visible membership and values (`effect.ts:405,939-959,1150`). | Duplicate-state clause | Compression | +| After Effect | Ordered repair and inherited replay holds still gate callbacks across participants. Final success flushes; failure keeps private deltas hidden (`effect.ts:814-861`). | Field-removal clause | Low salience | +| After builder | `contextId`, builder dependencies, and sync-run fencing remain in `scheduleGraphRun` (`collection-config-builder.ts:651-685`). | Override clause | Compression | +| After builder | Empty-start graph turns still check loaders and process synchronous writes before publication (`collection-config-builder.ts:571-645`). | Queue clause | Category mismatch | +| After builder | Ordered-load settlement is sync-run-fenced. Window failures, replay, ordered loads, and joined demand still gate publication (`collection-config-builder.ts:491-528,908-936`). | Recovery clause | Compression | +| After builder | Child facades prepare before root commit; failure discards them, while root and facade notifications follow state installation (`collection-config-builder.ts:930-981`). | Scheduler-focused reduction | Low salience | +| After scheduler test | Source/dependent order and prerequisite requeue form a four-case matrix that checks the final source run (`scheduler.test.ts:132-165`). | Cancellation clause | Compression | +| After scheduler test | Listener snapshot, first-error, later delivery, and falsy-failure matrices remain in public publication-path tests (`scheduler.test.ts:388-727`). | Failure/recovery clause | Compression | +| After scheduler test | Collection/Effect, shared/derived, write-order, diamond, and hybrid cases check coherent dependent publication (`scheduler.test.ts:1035-1367`). | Synthetic-test replacement clause | Category mismatch | +| After scheduler test | Restart and repeated-alias cases retain stale-settlement and exact first-failure distinctions (`scheduler.test.ts:1419-1771`). | Recovery clause | Compression | + +The before-only scan cannot establish repository-wide caller absence. The separate repository search found no production call to `onClear()` or implementation of `hasPendingGraphRun()` at the code target. The after-only scan found no explicit rejection in the frozen summary. Each scan returned its own source pointers before collation. diff --git a/docs/contributing/oracle-reviews/pr-1909-external-review.md b/docs/contributing/oracle-reviews/pr-1909-external-review.md new file mode 100644 index 0000000000..5eb99d0424 --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-1909-external-review.md @@ -0,0 +1,314 @@ +# PR #1909 external review + +Review target: `0ba54cd50bca908348940eb2020ec4f244450dc9`. The supplied +medium-effort review named four findings, one test gap, and one broader +cross-PR observation. This record separates the code fact from the proposed +behavioral consequence. + +| ID | Disposition | Evidence and remaining question | +| --- | --- | --- | +| R1 | refuted | `waitForJoinedDemand` does run on custom-collation full-source and unindexed fallback paths. A held child-demand fixture reaches both paths for Collections and Effects, then publishes the correct `none` window. Setting the gate to `false` makes all four cells publish a matched parent that should be absent. The full-source path still needs the publication gate for an anti-join. The indexed-only restriction proposed by the review is unsafe. | +| R2 | confirmed-open | Ordered Effects track published rows for every ordered source, and the classifier reads that map even outside a held repair or joined-filter window. The suggested duplicate insert for an already published key has no valid public history yet. An equal-value source update produced no D2 delta with either classifier. A second joined contributor received a distinct composite output key. A temporary comparison found no event-type divergence through 2,048 valid plain ordered Effect mutations; their public rows matched an independent window model. Provide a valid query and source sequence that sends an insert for a callback-visible key without its matching delete before changing this branch. | +| R3 | confirmed-open | The builder tracks completion of one `DemandUpdate.ready` per plan; the Effect checks the controller's pending acquisition segments. These can differ for a microtask after segment settlement. A controlled shared-source fixture held child demand for both consumers, observed neither publish before settlement, and then observed each publish the correct anti-join row. Collection callbacks arrived before the Effect callback, so reads of both consumers differed briefly between independent callbacks. No incorrect row or stuck gate appeared. A claimed gate error still needs a sequence that separates it from ordinary callback order. | +| R4 | fixed-now | Removed the redundant `hasJoinedFilterWindow()` scan from the builder's flush guard. The remaining `.some()` still requires `waitForJoinedDemand` and pending joined work. | +| R5 | fixed-now | Added four oracle cells crossing Collection/Effect with custom collation/unindexed root loading and held child demand. The custom path checks one unbounded request; the unindexed path checks prefix then full-source fallback. Both check complete anti-join publication. Disabling the gate kills all four cells. | +| R6 | deferred | The broader #1907 pattern is outside this review's code target. This PR now covers the two fallback regimes named here. The [#1907 review record](pr-1907-accepted-delete-ownership.md) remains the destination for its own guard and oracle claims. | + +The review identified a real test gap and a redundant guard. Its proposed +restriction of the joined-demand gate would introduce a visible anti-join bug. +The Effect classifier and split demand bookkeeping remain evidence gaps rather +than confirmed product failures. + +## Follow-up probes + +For R2, a temporary test compared `classifyImmediateDelta` and +`classifyHeldDelta` at every ordinary ordered Effect flush and failed if their +event types differed. It ran 64 deterministic histories of 32 valid source +mutations each. The histories crossed insertion, deletion, replacement, filter +membership, order changes, and top-two displacement over five keys. After +every mutation, the Effect's accumulated public rows matched an independent +filter/sort/window recomputation. No classifier divergence occurred. The +instrumented ordered-work, Effect, Effect-disposal, and pagination owners also +passed 426 tests without a divergent event type. The temporary instrumentation +was removed. These are sampled histories, not a proof for every query shape. + +For R3, a temporary fixture gave one on-demand root and child source to a +live-query Collection and an Effect. It held the child's request while both +consumers evaluated a custom-collation `none` filter. Both stayed empty before +release and ended with only the unmatched root. The callback sequence was +Collection, Collection, Effect; each callback's own result was correct. A read +of both consumers during the first Collection callback saw the Effect's earlier +state. Their callback schedules are separate, so that observation alone does +not show that either pending-work definition released at an invalid point. +The temporary fixture was removed; no product change was justified by it. + +## Follow-up review of demand scope and wake-up + +The later review was checked against `2a6b119f0dd85370d9f179b8a942fc163f08ea13`. +It contained two new product failures and several repeat or speculative claims. + +| ID | Disposition | Evidence and destination | +| --- | --- | --- | +| A1 | fixed-now | A direct LEFT-join anti-join stopped after its first root page while a separate include demand was held. Collection and Effect both made the next cursor request before that unrelated demand settled after the gate was scoped to the joined source and its plans. Two ordered-work oracle cells retain the RED/GREEN trace. | +| A2 | refuted | A zero-change source callback can schedule another loader pass, but the loader's last page, prefix, and tie-boundary latches prevent the stated no-progress request loop. Existing underfilled joined-window oracle cells finish with bounded, distinct requests. Disabling the last-page latch makes all 20 underfilled-source cells fail at the provider-request cap. | +| A3 | design-decision | Collection still reads plan settlement; Effect still reads controller segment state. Both now use one source-scoped pending-work predicate. The distinct state owners have no demonstrated incorrect release; the earlier shared-source probe remains in R3 above. | +| A4 | refuted | Full-source custom collation still needs the joined-demand publication gate. The earlier hostile mutant made four Collection/Effect fallback cells publish an incorrect anti-join row; the custom cell checks one full-source request. | +| A5 | duplicate of A1/A3 | The repeated scope concern led to the A1 fixture. The remaining state-owner question is A3. | +| B1 | fixed-now | The source/plan/status gate is now one helper. The old methods were similar but had different state queries, so they were not verbatim copies. A further refactor removed Collection's per-run loader callback registry and redundant scheduler state; the resulting PR is net-negative in production lines. | +| B2 | duplicate of A4 | Disabling the full-source gate is the killed mutant, not a safe fix. | +| B3 | confirmed-open | The ordinary ordered Effect classifier concern is R2 above. The prior 2,048-step differential found no legal event-type difference; a minimal divergent source history remains needed. | +| B4 | duplicate of A2 | The second no-progress-loop claim adds no new path. | +| B5 | fixed-now | A held empty joined truncate replay produced no replacement delta to wake Effect. A root update stayed at its old callback-visible value after replay settled. Listening for the joined subscription's `ready` transition released the delta. The ordered-work oracle retains the RED/GREEN case. Collection already has a replay-success wake-up. | +| B6 | duplicate of B1 | A shared predicate removes one scope definition. The Collection scheduler refactor supplies the net-negative code reduction; Collection and Effect still own separate public publication paths. | + +The second review supplied the missing unrelated-demand and empty-replay +histories. The alleged infinite loop did not survive the no-progress check. + +## Code-weight refactor + +The existing D2 graph and `OrderedSourceLoader` now drive ordered continuation +on every Collection graph turn, matching the Effect runner's existing schedule. +The Collection builder keeps one loader set per sync run and fences queued jobs +by sync generation. The transaction scheduler already deduplicates and clears +jobs, so the builder's second per-context callback registry and clear listener +were removed. Collection and Effect also use one dependency-graph scheduling +helper rather than separate transaction scheduling implementations. The source +loader's fixed-point latches still bound requests when a graph turn receives +no new rows. Effect's immediate and held delta classification now share one +classifier; only ordered Effects pass callback-visible row state. A single +monotonically increasing demand generation replaces per-plan generation +bookkeeping. + +Against merge base `afbeb44eef48d92b6e30ae5fd2843b938ee9163c`, the seven +changed production TypeScript files contain 215 added and 331 deleted lines +(net **−116 raw lines**). Counting nonblank, noncomment diff lines gives 197 +added and 221 deleted (net **−24 executable lines**). This measure excludes +architecture prose, tests, and review records. It is a line-count check, not a +claim that each old state was redundant: the joined-source demand and public +publication gates remain necessary for correctness. + +The full `packages/db/tests` run passed 6,603 tests across 186 files after the +production refactor. The ordered-work oracle passed 93 cells, including the +unrelated-demand and empty-replay repairs. The scheduler test exercised 81 +cases, including repeated-alias failure ownership across falsy failures. + +## Ordered continuation and window follow-up + +Code target: `0cbc7ae0` (production change `450b9d3c`). The later CodeRabbit comment at +`https://github.com/TanStack/db/pull/1909#discussion_r4124008382` identified +three additional failure mechanisms. Its suggestion to run the local CodeRabbit +CLI is separate from those findings. + +| ID | Disposition | Public or state-level witness | +| --- | --- | --- | +| CR2-1 | fixed-now | A bounded ordered repair paused for joined demand and then stopped before it refilled the public window. The ordered-work oracle failed on the missing third row before the fix. An explicit retry also lost its window generation at the same gate. The loader-state test failed on the missing retry request before the fix. Both tests pass with retained continuation and retry intent. | +| CR2-2 | fixed-now | `setWindow()` settled while an existing joined demand still held a required root continuation. The public ordered-work oracle failed before the fix. It now holds joined and root work separately. Its resolve, failure, retirement, and stale-rejection cells pass. | +| CR2-3 | fixed-now | Cursor invalidation erased an authoritative repair continuation. A later public-row deletion exposed an underfilled window before the fix. The oracle now observes the refill after joined settlement. Reset and request failure still clear the continuation. | +| CR2-4 | deferred | The `coderabbit` executable is unavailable locally. The PR check will run on the pushed commit. | + +The follow-up also simplified Effect source ingestion and Collection graph +scheduling. Collection now checks ordered loaders inside one fixed-point loop. +A public-query oracle observes a synchronous root refill before its second +joined demand. Deleting the post-loader graph step made that oracle fail at the +second-demand assertion. This was an assertion failure, not a timeout or setup +failure. The step remains in production. The [field-lab loss audit](pr-1909-final-graph-loss-audit.md) +records source-supported distinctions that the short refactor summary omitted. +It does not treat those omissions as product regressions. + +Against merge base `afbeb44eef48d92b6e30ae5fd2843b938ee9163c`, the +seven production TypeScript files have 318 added and 431 deleted lines. Net +production size is **−113 raw lines**. Excluding blank and comment-only diff +lines gives 299 added and 303 deleted, or **−4 executable lines**. The DB +oracle campaign passed 2,627 tests across 42 files. The DB runtime suite +passed 6,609 tests across 186 files with Vitest typechecking disabled. Package +TypeScript, changed-file ESLint, Prettier, and `git diff --check` passed. + +### Oracle guide review for this follow-up + +- **ORC-001–003:** The ordered-work oracle names the ordered-window and + publication contract, uses an independent filter/sort/window reference, and + keeps its grammar, driver, and public checkpoint visible. The loader-state + case is a focused state refinement of explicit retry admission. +- **ORC-004:** This follow-up adds bounded deterministic cells and makes no new + generated-grammar claim. The existing generated grammar is unchanged. The + new cells reconstruct the named joined-demand, repair, window-move, failure, + retirement, and refill witnesses. Multiple simultaneous joined plans and a + public-query failed-retry overlap remain outside this coverage, as the + coverage map states. +- **ORC-005–006:** The driver uses public live-query Collections and Effects. + It checks rows, requests, window settlement, and callback batches at named + checkpoints. The pre-fix runs failed at those observations. The graph-step + mutant failed at a public second-demand assertion. +- **ORC-007:** The existing important generated property runs fixed and random + campaigns with direct seed/path replay. These follow-up cells are bounded + deterministic controls, so they add no separate generated campaign. +- **ORC-008–009:** The reference model gains no state or new vocabulary. + Window participants in production remain distinct from reference rows. +- **ORC-010:** Each new async control resolves its held promises during cleanup. + `withHistoryCleanup` retains the primary assertion and reports cleanup + failures separately. +- **ORC-011:** No new shared semantic fault hypothesis requires a second + formulation in this follow-up. The independent filter/sort/window reference + remains the primary result model. Remote relation hints remain a separate + feature. +- **ORC-012:** This versioned section records outcomes for ORC-001 through + ORC-011 against the code target and names the remaining in-scope witnesses. + +The repaired claim covers finite local joined-filter windows, the named +joined-demand and window-move histories, Collection and Effect public rows, +and the specified settlement checkpoints. It does not claim all legal demand +interleavings. The coverage map owns the two remaining witnesses above. + +## Shipped-byte consolidation + +Code target: `b69c189c`. Three independent scouts inspected graph scheduling, +ordered demand, Effect state, package exports, and dependency drag. They +measured proposed cuts in isolated builds before the final edit. The chosen +changes remove two unused scheduler extension paths, two redundant Effect +fields, and unused Collection scheduler overrides. The core scheduler and D2 +graph remain necessary for variable query dependencies and coherent +publication. + +`Scheduler.onClear()` had no production caller, and no production dependency +implemented `hasPendingGraphRun()`. `Scheduler` is absent from the package root +exports. The change retires those internal extension contracts. A direct +unsupported source import can no longer attach a clear listener or provide an +out-of-queue pending dependency. The scheduler test now checks queued-job +cancellation, reuse of a cleared context, prerequisite requeue, exact failure +identity, and later successful work. Existing Collection and Effect tests still +check coherent public batches, rollback, stale settlement, and loader failure. +The architecture now says that only queued jobs block scheduler dependencies. +The [second field-lab loss audit](pr-1909-code-weight-loss-audit.md) preserves +source details omitted by this short reduction. + +The byte assay built the same package path with Vite 7.3.2 `--minify`, then +bundled selected ESM exports with esbuild 0.27.7. It used gzip level 9 and +Brotli quality 11. Every row compares the identical absolute path and toolchain. +Values are bytes; negative deltas save bytes. + +| Consumer import | Change from prior PR head: min / gzip / Brotli | Final PR against merge base: min / gzip / Brotli | +| --- | ---: | ---: | +| Full `@tanstack/db` index | −484 / −112 / −44 | +93 / −75 / −330 | +| `createLiveQueryCollection` | −325 / −81 / −113 | +638 / +44 / +19 | +| `createEffect` | −457 / −97 / −244 | +318 / +104 / −71 | +| Both query exports | −489 / −106 / +8 | +89 / −58 / +118 | + +The named query imports remain slightly larger than the merge base under some +byte measures. The full import has fewer compressed bytes but 93 more minified +bytes. These receipts do not establish a universal net-negative shipped size. +`@tanstack/pacer-lite` contributes no bytes to the measured query imports, so +the scouts found no query-path dependency drag to remove. The remaining +optional scheduler dependency argument saved only 15 gzip bytes in a trial and +would retire another internal test contract; it was left intact. + +The DB oracle campaign passed 2,627 tests across 42 files with no type errors. +The full DB runtime suite passed 6,605 tests across 186 files with Vitest +typechecking disabled. Package TypeScript, changed-file ESLint, Prettier, and +diff checks passed. Against the merge base, production TypeScript is **−156 +raw lines** and **−35 nonblank, noncomment lines**. This count includes the +scheduler file; it excludes tests and documentation. + +## Xhigh review of ordered callbacks and code weight + +External review target: `80421c8`. Evaluation started at `dd10cb4`; the +verified follow-up is `b445bd13` (production fix `1a1b0952`). The raw review +named two behavior findings, one duplication suggestion, two lower-severity +findings, two oracle gaps, and broader preservation/weight claims. Its reported +fresh-worktree and architecture-reading checks are provenance, not product +evidence. The entries below retain those claims separately. + +| ID | Claim and technical verdict | Disposition, action, and durable destination | +| --- | --- | --- | +| X1 | Scheduler clear, abort, and generation fencing preserve behavior after removal of pending graph bookkeeping and `onClear`. The scheduler tests and earlier loss audit support the named paths. They do not prove every possible history or absence of an external deep import. | `already-fixed`; no further change. The scheduler tests and [code-weight loss audit](pr-1909-code-weight-loss-audit.md) retain the exact internal behavior removed. | +| X2 | Removing the `sentChanges > 0` loader guard and always checking `loadMore` is intentional. The architecture assigns fixed-point control to loader latches and requires graph work after synchronous loader writes. | `already-fixed`; the architecture and ordered-work oracle own this boundary and its killed post-loader-step mutant. | +| X3 | An ordered `skipInitial` Effect completed its initial phase while joined subset demand was pending, then emitted an initial `enter` after demand settled. A controlled child promise reproduced this on `dd10cb4`. | `fixed-now`; `canCompleteInitialLoad` now checks the same joined-work condition as callback flush. The ordered-work oracle holds child demand, asserts no callback before or after settlement, then observes a later update. | +| X4 | An unprojected order-key change allegedly stopped emitting an `update` because of the classifier merge. The exact public probe emitted **no update** with both current code and the reviewed `80421c8` Effect source. Ordered repair selected callback-visible classification in both versions. The extra `previousValue` claim has no observable event in this trace. | `refuted` for the stated regression; no classifier change. The earlier R2 entry above retains the separate question of whether another valid ordinary ordered history changes event type or `previousValue`. | +| X5 | Removed `info.sourceId === key` guards were redundant: compiler order info is stored under its own source ID. The review's claim that no other dead parameter exists is an unbounded inventory claim, not established by this check. | `already-fixed` for the guards; compiler construction and ordered-query tests are the durable evidence. No exhaustive dead-code claim is adopted. | +| X6 | The removed `loadMoreCallbackSymbol` dedup layer is covered by scheduler coalescing and ordered-loader request latches in the named tests. | `already-fixed`; scheduler and ordered-work owners retain the law. | +| X7 | The classifier merge is claimed behavior-preserving. X4 does not refute it: that alleged old event did not exist. The earlier R2 differential found no divergence in its sampled histories, but no universal equivalence follows. | `deferred`; the earlier R2 entry in this review record owns a still-missing valid divergent history, if one exists. No speculative classifier state was retained. | +| X8 | Removed `pendingGraphRuns` bookkeeping is claimed safe. This repeats X1's scheduler part. | `duplicate` of X1; its distinct callback-accumulation detail remains in the code-weight loss audit. | +| X9 | The joined-filter pending predicate is duplicated across Effect and Collection; `hasJoinedFilterWindow` contains only the source-existence half, so “triplicated verbatim” is inaccurate. Effect now reuses one local predicate for flush and initial completion. A shared exported helper increased minified and gzip bytes in the same-path consumer builds, so the cross-engine helper was removed. | `deferred`; this record preserves the remaining cross-engine duplication. Revisit only with a measured byte win or a new semantic reason to share the state-owner boundary. | +| X10 | A ready transition from any source caused an extra joined-filter graph schedule. A deterministic scheduler spy saw two calls for root readiness with the old condition and one with the source identity guard. Joined-source readiness still wakes the gate. | `fixed-now`; ordered-work oracle work-counter cell rejects the broad-condition mutant at its call-count assertion. | +| X11 | `failDemand` leaves an active plan unsettled after rejection. Source inspection confirms that state, but `transitionToError` also makes the live query terminal, and sync teardown clears the plan. Marking the plan settled alone would not guarantee future recovery because subscription status is another pending-work input. | `deferred`; [coverage map](../oracle-coverage.md) names the missing nonterminal per-demand recovery contract and its required plan/subscription reconciliation. No current public wedge was reproduced. | +| X12 | The oracle lacked `skipInitial` crossed with pending joined demand. | `fixed-now`, linked to X3; the ordered-work oracle now contains the held-demand witness. | +| X13 | The oracle lacked an unprojected order-key reorder callback check. The review's expected `update` conflicts with the reviewed Effect path and the architecture's held-repair value rule. | `design-decision`; if position-only changes should produce callbacks, define that public policy first and give the ordered-work owner an independent event law. No present regression is claimed. | + +### RED/GREEN and byte evidence + +Before the fix, the new public Effect witness received `enter` after the held +child load resolved. With `1a1b0952`, it received no initial callback and did +receive a later `update`. The ready-work witness observed two root-ready graph +schedules under a temporary broad-condition mutant; the source-specific guard +reduced that to one while the joined-ready transition still scheduled its +work. Both mutants failed assertions at the intended checkpoints. The +reorder witness failed its proposed `update` expectation on both `dd10cb4` +and the reviewed Effect source from `80421c8`; this is a refutation of the +review's old-versus-new causal claim, not proof of every callback policy. + +The final DB oracle campaign passed **2,629 tests across 42 files**, with no +Vitest type errors. The focused ordered-work, scheduler, and Effect suites +passed 250 tests. Package TypeScript, changed-file ESLint, Prettier, and diff +checks passed. The test-only cleanup follow-up retained primary assertion +failures while releasing held work. The production diff from merge base +`afbeb44e` remains **−153 raw TypeScript lines**. + +In the Vite 7.3.2 and esbuild 0.27.7 same-path trial, the proposed shared +helper cost 37 more minified and 20 more gzip bytes in the full index than +the selected local reuse. It cost 108/25 more minified/gzip bytes for the +Collection import and 38/17 more for the Effect import. It saved 108 Brotli +bytes in the full index but added 15 and 9 Brotli bytes in the two named +imports. The selected local reuse plus behavior fixes changed the measured +consumer imports against `dd10cb4` as follows: + +| Consumer import | Minified | Gzip | Brotli | +| --- | ---: | ---: | ---: | +| Full index | +41 B | +7 B | −74 B | +| `createLiveQueryCollection` | −65 B | −5 B | +83 B | +| `createEffect` | +106 B | +18 B | +107 B | + +These are incremental build differences from `dd10cb4`, not a claim that +every PR entry point is net smaller than the merge base. + +### Oracle guide review + +- **ORC-001–003:** The architecture states the `skipInitial` boundary; the + ordered-work oracle keeps the contract, plain-result model, scenario grammar, + real Collection/Effect driver, and callback checkpoint visible. The work + counter is a partial oracle for scheduling calls, not public row truth. +- **ORC-004 and ORC-007:** These are deterministic controls. The existing + generated grammar and fixed/random campaigns are unchanged and both ran in + the 2,629-test oracle campaign. +- **ORC-005–006:** `childLoads > 0` proves the held joined source was reached. + The recorded callback list can contain an illegal initial `enter`. The + pre-fix run rejected that event; the broad-condition mutant failed the + scheduler count after a real root ready transition. +- **ORC-008–009:** The reference gains no model state. The new checks use the + glossary's Collection, joined demand, readiness, and publication terms. +- **ORC-010:** `withHistoryCleanup` releases the held promise and all + Collections while preserving the primary failure and separate cleanup + diagnostics. +- **ORC-011:** No plausible shared semantic fault between the existing plain + row reference and the `skipInitial` callback law was named. The independent + callback assertion uses the established `skipInitial` contract. +- **ORC-012:** This versioned section records the two RED controls, the GREEN + run, the exact code target, the bounded claim, and the remaining cells. + Multiple simultaneous joined plans during a window move and a public-query + failed-retry overlap remain in the coverage map. Nonterminal per-demand + recovery is a separate undefined contract. + +### Final loss audit and reviewer assessment + +The raw review was reread after the fix. Its 13 recorded items reconcile as +**3 fixed-now + 4 already-fixed + 1 duplicate + 1 refuted + 3 deferred + +1 design-decision = 13**. The only unresolved behavioral evidence gap is the +earlier R2 ordinary-classifier concern, now linked from X7; the stated X4 +reorder trace does not supply it. X11 remains a future recovery design issue. + +The reviewer showed useful depth on X3 and X10, including a concrete async +sequence and a work-specific source comparison. Accuracy and prioritization +were mixed: X4's confident regression claim failed on its own old path, X9 +overstated duplication and byte savings, and X11 correctly identified a +latent state mismatch without showing a current product failure. The review +has moderate signal-to-noise and good test-gap instincts. **Hire for code +review with an executable-verification requirement for blocking behavioral +claims; do not use this review alone as a merge gate.** diff --git a/docs/contributing/oracle-reviews/pr-1909-final-graph-loss-audit.md b/docs/contributing/oracle-reviews/pr-1909-final-graph-loss-audit.md new file mode 100644 index 0000000000..cd31b37576 --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-1909-final-graph-loss-audit.md @@ -0,0 +1,38 @@ +# Field-lab loss audit: final PR #1909 graph consolidation + +Frozen reduction: Effect routes startup and live changes through one handler; Collection replaces two graph drains with one fixed-point loop; direct compiler-map lookup removes redundant accessors. The before source is `f9d2407c`. The after source is code commit `450b9d3c`. Two independent read-only source scans examined the three relevant files separately. This ledger records supported distinctions absent from that reduction. It does not label them useful, harmful, or regressions. + +| Source | Recovered distinction and pointer | Where dropped | Rule | +| --- | --- | --- | --- | +| Before | Effect buffers every source until all subscriptions exist, drains directly to D2, then runs one initial graph turn. Live callbacks schedule through `handleSourceChanges` (`effect.ts:523-535,575-598,737-792`). | “One handler” clause | Category mismatch | +| Before | Removing a startup buffer before draining makes reentrant callbacks live. Ordered batches notify the loader and split updates before D2 in both paths (`effect.ts:744-765,575-590`). | Effect clause | Compression | +| Before | Compiler callbacks capture mutable subscription objects, which must be filled before the graph runs (`effect.ts:391-405,477-489,613-614`). | Effect clause | Compression | +| Before | Effect has its own graph/loader fixed point and emits one batch after quiescence (`effect.ts:902-941`). | Collection-only graph clause | Category mismatch | +| Before | Effect holds ordered repair callbacks through prefix, tie, and refill, transfers a hold to truncate replay, and classifies against visible rows (`effect.ts:839-881,943-987,1187-1229`). | Effect clause | Category mismatch | +| Before | Collection reads where and order data by opaque source ID and normalizes the where path for the alias (`collection-subscriber.ts:54-67,418-434`). | Direct-map clause | Compression | +| Before | Initial subscription branches on ordered loading, lazy status, on-demand mode, and zero limit (`collection-subscriber.ts:113-138,273-315,333-358`). | Direct-map clause | Category mismatch | +| Before | Zero-change settlement schedules a graph turn, but revision advances only for D2 input. Ordered updates notify the loader and split (`collection-subscriber.ts:217-234,283-295`). | Graph clause | Compression | +| Before | Ordered truncate resets the cursor, keeps exact contributed rows, queues full-source loading behind replay publication, and fences success by sync run (`collection-subscriber.ts:303-330,363-385`). | Graph clause | Category mismatch | +| Before | Ordered `loadMore` waits for replacement unless a window operation is active, passes the window generation, and tracks its promise (`collection-subscriber.ts:387-416`). | Graph clause | Compression | +| Before | Collection checks sync-run identity after graph, loader, and flush steps; graph exceptions mark error; empty startup commits before readiness (`collection-config-builder.ts:559-630`). | Fixed-point clause | Compression | +| Before | Ordered load participants are sync-run-fenced; the final successful participant schedules a flush without rerunning loaders (`collection-config-builder.ts:462-499`). | Fixed-point clause | Compression | +| Before | Window failure, ordered loads, replay, and joined demand gate publication; child facades prepare before root commit and events follow state (`collection-config-builder.ts:894-982`). | “Before publication” clause | Category mismatch | +| Before | `setWindow` rejects reentry, copies options, waits for replay, runs mutation and graph work in one publication context, and settles afterward (`collection-config-builder.ts:270-369`). | Fixed-point clause | Category mismatch | +| Before | Readiness depends on all subscriptions, required sources, active route demand, and no live-query subset load (`collection-config-builder.ts:1103-1126,1163-1255`). | Fixed-point clause | Category mismatch | +| After | Effect startup removes each buffer before the shared handler drains it, then runs the graph once (`effect.ts:523,728-753`). | “One handler” clause | Compression | +| After | Effect owns subscriptions before lazy demand or ordered snapshot startup can throw, then releases acquired sources on failed startup (`effect.ts:596`). | Effect clause | Compression | +| After | The ordered Effect loader sees original changes and prior D2 rows before update splitting (`effect.ts:758`). | Effect clause | Compression | +| After | Effect retains its own graph loop and one callback batch per run (`effect.ts:882`). | Collection-only graph clause | Category mismatch | +| After | Effect holds callback deltas during ordered repair and initial `skipInitial`; aborted repair can transfer the hold to replay (`effect.ts:819`). | Collection publication clause | Category mismatch | +| After | Effect consumes a synchronous lazy-demand error only if the subscription already reported that exact error (`effect.ts:774`). | Effect clause | Category mismatch | +| After | Collection maps where/order data by source ID and normalizes where for the lexical alias (`collection-subscriber.ts:54`). | Direct-map clause | Compression | +| After | Subset-result tracking delays rejection one microtask so the subscription can mark error before loading completes (`collection-subscriber.ts:68`). | Direct-map clause | Category mismatch | +| After | Collection advances graph-input revision only for actual D2 entries but schedules a turn for zero-change settlement (`collection-subscriber.ts:219`). | Graph clause | Compression | +| After | Ordered truncate retains contributed rows, queues full-source replay, and fences success by sync run (`collection-subscriber.ts:305`). | Graph/publication clause | Compression | +| After | Builder checks current sync run after graph and loader steps; graph failure marks current query error (`collection-config-builder.ts:589`). | Fixed-point clause | Compression | +| After | Builder flushes once at quiescence, performs empty initial commit when needed, then evaluates readiness (`collection-config-builder.ts:626`). | Fixed-point clause | Compression | +| After | Graph quiescence alone does not release publication while window failure, ordered loads, replay, or joined work persists (`collection-config-builder.ts:914`). | Publication clause | Compression | +| After | Child facade state installs before root and events; preparation failure discards writes; both publication callbacks run (`collection-config-builder.ts:936`). | Publication clause | Compression | +| After | Ordered-load settlement is sync-run-fenced and final success schedules a flush without another loader run (`collection-config-builder.ts:491`). | Fixed-point clause | Compression | + +No source shows explicit rejection of these distinctions in the frozen reduction. This instrument does not decide which distinctions belong in the final explanation or whether a behavior changed. The separate hostile graph-step mutant fails at a public second-child-demand checkpoint, so the post-loader graph step remains in production. diff --git a/docs/contributing/oracle-reviews/pr-1914-immediate-reload-follow-up.md b/docs/contributing/oracle-reviews/pr-1914-immediate-reload-follow-up.md new file mode 100644 index 0000000000..e38a8a43a9 --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-1914-immediate-reload-follow-up.md @@ -0,0 +1,69 @@ +# PR #1914 immediate-reload oracle follow-up + +- Reviewed executable commit: `46b8babdf917aa5f29f6fbb7593aaf91866adbe5`. +- Comparison commit: `595697ab7da30d59ddb4d97f82626757ca2653a0`. +- Executable owner: `packages/browser-db-sqlite-persistence/e2e/electric-immediate-reload.opfs.spec.ts` with its `.opfs.ts` browser fixture. +- Source: external review supplied by the user on 2026-09-28. The lossless raw review and temporary-mutant details are in the task-local evaluation ledger; this versioned record preserves verdict-critical evidence. + +## Contract, fixed history, and checkpoints + +The fixed host history starts with an empty OPFS database and a live PostgreSQL +table. The first Chromium page inserts one row through the Collection's +`onInsert` handler. PostgreSQL commits it, and Electric acknowledges its txid. +The source row's OPFS adapter write is held before the write starts. The first +page must expose the row publicly while its durable snapshot remains empty. +The test closes that page without releasing the write. + +The second page opens the same raw OPFS database but pauses before creating a +Collection or starting Electric. Its durable snapshot must still be empty at +this `before-source-start` checkpoint. After the fixture releases that gate, +the real Electric provider must restore the exact public and durable row. The +reopened page must send zero user-insert POST requests during this recovery. +The expected row comes from the PostgreSQL insert payload and a separate +PostgreSQL row query; it is not read from Electric or SQLite to construct the +expected result. + +The test records only `POST /__issue1456/insert` requests on the reopened page, +with its listener installed before navigation. It polls public rows before +requesting complete observations, then continues checking the exact durable +row until it appears. Thus pre-publication polling does not repeatedly enqueue +full OPFS reads, while a lagging durable write still fails or remains pending. + +## Review reconciliation and RED/GREEN controls + +| Review item | Verdict and change | Calibration or limit | +| --- | --- | --- | +| #1: no reopened empty-before-Electric checkpoint | Valid missing direct causal observation. Added the raw OPFS snapshot while Collection creation and Electric startup are held. The review's specific claim that a failed first-page hold could pass was overstated: the existing first-page `durableRows: []` assertion and unresolved write gate already reject that path. | A temporary nonempty-snapshot mutant failed at the new held checkpoint, seeing `[row]` where `[]` was required. A separate wrong-expectation control failed at that exact assertion with actual `[]`, preserving the primary failure through teardown. | +| #5: no explicit no-reinsert observation | Valid. Added a reopened-page request recorder and zero-POST assertion. Rechecking the original first-page route counter would not observe reopened requests. | A temporary extra-POST mutant reached recovery and failed the zero-POST assertion with the inserted request URL. A permanent helper check covers method/path filtering. Requests after the final observation cut are outside this bounded claim. | +| #3: duplicated diagnostic branches lack self-tests | Valid maintenance gap. Added local tests for empty failure messages, terminal failure, late page errors, and closed-page diagnostics. | The sibling straddle fixture has a different startup boundary. A universal helper extraction was not needed to establish these checks. | +| #4: plain mode installs a pass-through write override | Valid cleanup. The override now exists only in hold mode. | The recovering page uses the unwrapped adapter. | +| #2: every observation poll reads the full OPFS snapshot | Valid work-law observation. Public-first polling removes full snapshots from pre-publication polls and retains durable polling afterward. | A deterministic control needs three public polls and two full snapshots, including one durable-lag poll. The review's claim of contention with a write already inside the scheduler was unsupported: the held wrapper waits before calling the original adapter writer. No latency improvement is claimed. | +| Cross-PR shared-harness proposal | Design decision deferred. Similar diagnostic mechanics do not imply identical startup and observation boundaries. | This record is the destination for reconsidering shared diagnostic plumbing if another fixture needs it; extraction alone would not add request observation or reduce OPFS reads. | + +The two temporary behavioral mutants failed at their intended assertions, not +by timeout or setup failure. They were removed. After restoration, the focused +immediate-reload file passed **7/7** tests. The full live +Chromium/Electric/OPFS lane passed **15/15** tests, including sibling fixtures. +The main live witness also passed five repeated runs during the follow-up. +Package TypeScript, changed-file ESLint, Prettier, and `git diff --check` passed. +No production behavior, schema, or API changed. + +## ORC-001 through ORC-011 + +| Requirement | Outcome | +| --- | --- | +| ORC-001: contract authority and limits | Pass for the bounded issue #1456 history. The report supplies the reload case; the PostgreSQL table supplies the expected server row. `awaitTxId` is treated only as Electric transaction evidence, not a SQLite durability acknowledgment. This test does not establish Windows/Edge, offline recovery, arbitrary later inserts, or unbounded eventuality. | +| ORC-002: independent judgment | Pass. PostgreSQL data, the initially empty database, and the absence of a user insert request determine expected observations. No production resume marker or SQLite row determines the expected row. | +| ORC-003: distinguishable responsibilities | Pass. The spec and browser fixture state the bounded law, fixed insert/hold/close/reopen history, PostgreSQL-backed expected result, real provider driver, and exact public/durable/request checks at three named cuts. A generated reference model is not claimed. | +| ORC-004: generated-history grammar controls | Not triggered. This is one fixed host history with no generated-history coverage claim. The randomized database/table suffix isolates resources; it does not generate semantic histories. | +| ORC-005: production path and observation | Pass. Real Chromium pages run the Collection, installed Electric SDK, PostgreSQL, OPFS worker, and SQLite adapter. The test observes the first-page held row, raw reopened OPFS baseline before Electric starts, recovered public and durable rows, and reopened-page POST requests. | +| ORC-006: checker calibration | Pass for the repaired checkpoints. The extra-POST and nonempty-baseline mutants reached and failed the intended assertions. A wrong-expectation control confirmed the held checkpoint's actual empty snapshot and primary-error retention. None of these controls proves every possible regression is caught. | +| ORC-007: fixed/random campaigns and replay | Not triggered. The owner is a fixed live-host history, not an important generated property. | +| ORC-008: stateful-model minimality | Not triggered. No stateful reference model was introduced or changed. | +| ORC-009: vocabulary mapping | Pass. `before-source-start` is a fixture-only observation cut after opening raw OPFS and before Collection creation; it is not a new production Collection status. Source, public, and durable rows retain their distinct meanings. | +| ORC-010: failure fidelity and cleanup | Pass for exercised paths. The existing harness keeps a primary assertion as the cause if cleanup also fails. The new gate remains closed during teardown if the held checkpoint fails; page close aborts that page rather than racing Collection creation with database close. The temporary wrong-expectation control failed at the intended equality assertion with no secondary cleanup error. Process crashes remain outside this guarantee. | +| ORC-011: independent second formulation | Not triggered. No plausible shared semantic classifier between the PostgreSQL expected row and the OPFS/Electric result was identified. The added pre-Electric raw snapshot is a distinct causal checkpoint, not a second implementation of Electric recovery. | + +ORC-012 is satisfied by this record for the exact executable commit above. The +follow-up strengthens one bounded host witness; it does not claim closure of +every host or timing regime in issue #1456. diff --git a/docs/contributing/oracle-reviews/pr-1920-collection-collation.md b/docs/contributing/oracle-reviews/pr-1920-collection-collation.md new file mode 100644 index 0000000000..3e1fd82c3e --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-1920-collection-collation.md @@ -0,0 +1,73 @@ +# PR #1920 Collection collation oracle review + +## Reviewed state and claim + +- Base: `33a194941c8d51f8f98babb999fef2987dd6ff8b` (PR merge base). +- Reviewed executable head: `778c542fa06f4a5ff3f469648a6ca265d638ca70`. +- Owner: `packages/db/tests/index-update.property.test.ts`. +- Public entry point: `Collection.currentStateAsChanges({ orderBy })`. + +The Collection's declared string collation determines the order of public rows. +The auto-index path must use a compatible index and reuse it across repeated +reads. The scan path must return the same declared order. The fixed owner cells +check lexical and numeric `en-US` defaults, plus an explicit ordinary-locale +clause overriding a numeric default. Indexed cells cross BasicIndex and +BTreeIndex; scan cells use `autoIndex: off`. A six-row scan cell also bounds +comparison-option resolution to at most two reads: one for the attempted index +path and one for the scan clause. + +The original implementation failed six indexed cells at the public read and +index-construction checks. Its numeric-locale scan also returned lexical order. +The combined two-line revert at `74a0e0dd` failed 7 of 9 fixed collation +cells, while lexical and explicit ordinary-locale scan cells passed as controls. +The later CodeRabbit follow-up removed repeated option and comparator creation +inside the scan sort callback. Reversing that follow-up failed the six-row work +cell with six Collection option reads against the bound of two. These were +assertion failures at the intended checkpoints, not timeouts or setup failures. + +## ORC-001 through ORC-011 + +| Requirement | Outcome at reviewed executable head | +| --- | --- | +| ORC-001: authority and limits | Pass for the new cells. `defaultStringCollation` is the public Collection declaration, and order clauses can supply explicit comparison options. The opening test prose and coverage map state the row-order and index-reuse law. The coverage map limits this owner to lexical and numeric `en-US` cases, an explicit ordinary-locale override, BasicIndex/BTreeIndex, fixed public reads, and a six-row scan work probe. Other locales and ordered histories are outside this owner. | +| ORC-002: independent judgment | Pass for the new cells. Expected label arrays are literal facts chosen to distinguish lexical from numeric order. The six-row expected array is literal; the option getter count is an observed work measure. None derives its expected result from `buildCompareOptions` or `makeComparator`. | +| ORC-003: visible responsibilities | Pass for the new cells. The opening comment states the law, `collationCases` and the six-row input state the bounded model, `createCollationCollection` supplies the fixture, `currentStateAsChanges` is the production driver, and row/count expectations are the refinement checks. | +| ORC-004: generated-history controls | Not triggered by the new fixed cells; they are a bounded enumeration, not an important generated property. The older direct-index `fcTest.prop` grammars in this same file predate this PR. Their ablation and exclusion evidence has not been audited to this requirement and remains an owner gap. | +| ORC-005: path and observation | Pass for the new cells. `optimizedOnly` plus a `createIndex` spy witnesses indexed reads and one construction; `autoIndex: off` plus zero indexes witnesses the scan path. At Collection readiness, the tests compare exact public row order. The work cell also checks a bounded number of Collection collation reads. | +| ORC-006: checker calibration | Pass within the stated paths. Reverting both original production lines caused 7/9 fixed cells to fail by assertion at public row/order or index-construction checkpoints. Reversing the scan-work follow-up caused six option reads against the allowed two, while row order remained correct. The current code passes both checks. | +| ORC-007: fixed/random campaigns and replay | Not triggered by the new fixed cells. The older important generated direct-index properties are not in the package `test:oracles` command and do not have paired fixed/random campaigns with direct replay; that pre-existing gap remains with this owner. The separate retired-identity replay calibration is a different property. | +| ORC-008: stateful-model minimality | Not applicable: this change adds no state to a reference model. The fixed expected arrays and work bound are stateless. | +| ORC-009: vocabulary mapping | Not applicable: no model-only state or action combines or splits production concepts. `Collection`, row, public read, and index use their project meanings. | +| ORC-010: failure fidelity and cleanup | Unresolved for the fixed cells. They await `Collection.cleanup()` in `finally`; a cleanup rejection could supersede a hard primary assertion. No cleanup-failure run was included, so this record does not claim primary/secondary diagnostic separation. | +| ORC-011: independent second formulation | Not applicable. Review named no plausible semantic fault shared by the literal expected orders and production comparator that another formulation would distinguish. The indexed and scan drivers are separate production paths, not competing reference models. | + +This versioned record supplies the ORC-012 outcome for executable head +`778c542fa06f4a5ff3f469648a6ca265d638ca70`. It does not claim that the +older generated properties meet ORC-004 or ORC-007, or that the cleanup harness +meets ORC-010. + +## CodeRabbit reconciliation and verification + +CodeRabbit reviewed `74a0e0dd` and placed one Trivial nitpick in its review +body, with no inline comments. It correctly identified per-comparison option +and comparator creation in `change-events.ts`. A public six-row scan gave a +deterministic RED count of six Collection collation reads. Hoisting one +comparator per clause gave GREEN with two reads and the same exact public order. +The one work cell failed again when the follow-up was reversed. The review's +AI-agent prompt repeated the same finding. Its large-scan merge-risk sentence +named the same performance cost, not another correctness issue. + +The review also noted that failed index construction can consume an index ID +before an index is registered. That behavior predates this PR. The review did +not identify a public failure caused by the collation change, so it remains +outside this follow-up. Its deployment-exposure discussion was explicitly +inferred and did not establish an application security boundary. Its vendor +docstring warning used an 80% threshold that this repository does not configure; +adding boilerplate to these existing functions would not repair a product +contract. No new public API or security operation was added. + +At executable head `778c542f`, the five relevant files passed 416 tests. +Package TypeScript, changed-file ESLint, Prettier, and `git diff --check` +passed. Independent prep review and simplification passes found no further +actionable issue. The published PR's original checks were green before this +follow-up; new-head CI must be checked after publication. diff --git a/docs/contributing/oracle-reviews/pr-1924-external-review.md b/docs/contributing/oracle-reviews/pr-1924-external-review.md new file mode 100644 index 0000000000..32d2db8432 --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-1924-external-review.md @@ -0,0 +1,117 @@ +# PR #1924 dotted-path review + +## Reviewed state and bounded claim + +- Original reviewed PR head: `04d8b8a87583bdf3ea9e8931f20fa9661e7c6a01`. +- Reviewed executable repair head: `8bfc800c873073bb8c06e841e69ec69d153a95a7`. +- Base: `origin/main` at `6e151b0e57d63e2535cdf8e02518690d453214bc`. +- Executable owners: `packages/db/tests/query/index-path-collision-oracle.test.ts` and the focused compiler witness in `packages/db/tests/query/compiler/lazy-targets.test.ts`. + +The PR protects complete property-path segments when a dotted scalar property +and a nested property have the same dotted display. Its bounded public oracle +checks numeric AND predicates, selected-field ordering, Collection subscription +callbacks, and live-query callbacks. The compiler witness checks that a union +source's coalesced lazy demand retains both target paths. It does not establish +all path-identity sites, a public on-demand adapter history for the lazy target, +or arbitrary path segments, nullish values, collation, and incremental updates. + +The full PR's physical production-source diff against this base is 7 added and +6 deleted lines, net **+1**. The review repair itself changed two production +lines in place, net zero. Tests, this record, the coverage map, package script, +and changeset are excluded from production weight. + +## External review ledger + +| ID | Raw review claim | Evidence at original head | Disposition and destination | +| --- | --- | --- | --- | +| 1924-01 | `lazy-targets.ts:257` still joins path segments and should use `JSON.stringify(path)`. | The actual file is `query/compiler/lazy-targets.ts`. A same-source `UnionFrom`/`coalesce` witness expected `[['a.b'], ['a', 'b']]` but received only `[['a.b']]`. This is an assertion failure at the compiler target boundary. | **fixed-now** in `224a17c2`; the focused test passes and the coverage map owns the remaining public on-demand witness. | +| 1924-02 | Serialization is duplicated at three sites; extract `serializePath`. | The repetition is real, and the review missed a fourth cache in `createRefProxyWithSelected`. That cache had a separate public failure: a selected-field order sorted by the dotted scalar twice. Reverting its one-line repair made the oracle fail at exact key order. | **deferred** helper extraction. The five one-line `JSON.stringify(path)` uses now span separate proxy, planner, and compiler boundaries. A shared import would add code without changing this behavior. This record preserves the suggestion for a future coordinated encoding change. The missed cache itself was fixed in `224a17c2`. | + +The review found one genuine remaining compiler collision and a true but weak +duplication observation. It identified a viable one-line fix for the former, +but cited the wrong directory, supplied no same-path evidence, and missed the +selected-field proxy cache. The reviewer is technically useful on localized +code reading; evidence depth and bug-class reach are limited. Hire +recommendation for independent high-stakes review: **no** on this sample. + +## Oracle guide audit at the reviewed executable head + +| Requirement | Outcome | +| --- | --- | +| ORC-001 authority and limits | The oracle header cites the public live-query guide's AND filters, selected fields, and computed ordering, plus the live-query architecture's physical-index boundary. Its known omissions and the coverage map bound the claim. | +| ORC-002 independent judgment | The model filters and sorts plain rows with direct JavaScript property reads. It imports no production predicate classifier or index logic. | +| ORC-003 visible responsibilities | The opening contract, model, bounded cases, production drivers, and exact key/order refinements remain in the executable file. The compiler boundary has its own focused witness and named limit. | +| ORC-004 generated grammar controls | Not triggered as a generated property: the oracle enumerates a fixed 3×3 row table and fixed `it.each` cases. These fixtures reconstruct the known dotted/nested collision. The values 0, 10, and 25 straddle the named bounds. No random or legal-history grammar is claimed. If the finite table is read as a generated-property claim, ablation and exclusion evidence remains open. | +| ORC-005 production path and observation | Public direct reads, Collection callbacks, and live-query results compare exact keys or exact order. Each direct indexed case spies on the installed nested BTree index's `lookup` and observes a call after index addition. The lazy-target test directly observes compiler output; it makes no public-row claim. | +| ORC-006 checker calibration | At the original head, the new lazy-target test failed with one target instead of two. After the repair, restoring the old `$selected` cache key temporarily made the public order check fail (`0-25` before `10-0`); this was an assertion failure at the named checkpoint. The original PR's grouped-range and callback proxy RED results are recorded in the coverage map. Temporary reversions were removed. | +| ORC-007 fixed/random campaigns | Not triggered: these are bounded enumerations and a focused compiler test, not important generated properties. | +| ORC-008 model minimality | Not triggered: the reference recomputes from fixed rows and adds no stateful model state. | +| ORC-009 vocabulary mapping | Not triggered: the model's flat and nested field reads are local data descriptions, not combined or split production concepts. | +| ORC-010 failure fidelity and cleanup | Pass. The harness registers each acquired resource and attempts cleanup in reverse order after success or failure. If an assertion and cleanup both fail, `AggregateError.cause` preserves the primary mismatch and `errors` retains cleanup failures. A hostile control proves both diagnostics survive and both releases run. No shrinker or capture process is used. | +| ORC-011 second formulation | Not triggered: this review named no plausible semantic fault shared by production and the direct JavaScript model that a second formulation would separate. | +| ORC-012 review evidence | This versioned record identifies outcomes and limits for ORC-001–011 against executable head `8bfc800c`. The coverage map owns the focused compiler boundary and public adapter omission. | + +The bounded claim is: complete property-path identity × the fixed numeric rows +and coalesced target pair × direct query/index, selected proxy, callbacks, and +compiler deduplication × exact public keys/order or exact compiler target paths +at the named checkpoints. The original indexed and callback failures, the +adjacent `$selected` failure, and the compiler target failure are distinguishing +witnesses. A known reachable public on-demand counterexample has not been +established by this review; that history remains an in-scope evidence gap for +the lazy-target coverage owner. The record does not claim global closure of +every path-key use in the query compiler. + +## Verification and remaining scope + +- Before repair: the compiler target test failed with one target where two were expected. The `$selected` exact-order oracle failed when its cache key was restored to dotted joining. +- After repair: 69 tests passed across the collision oracle, lazy-target unit tests, ref-proxy tests, and union-all tests. +- Changed-file ESLint, Prettier, and `git diff --check` passed. +- Package `tsc --noEmit` reported 41 worktree-wide errors, including db-ivm source files outside the db package's `rootDir`; none named changed files. This run is not a clean typecheck receipt. + +The remaining public on-demand adapter witness is assigned to the lazy-target +coverage-map row. No request to extract a shared serializer is part of this +repair. + +## Second external review: correlated reference paths + +This follow-up checked the later review against PR head `b602188f` before +editing. The review correctly noticed that the earlier repair did not cover two +builder deduplication sites. The architecture's route-context law requires +every distinct parent reference to reach the child query. A property path is a +sequence of segments, so [`a.b`] and [`a`, `b`] cannot share a deduplication +key. + +| ID | Review claim | Executable evidence | Disposition and destination | +| --- | --- | --- | --- | +| B1 | The lazy-target collision was fixed with RED/GREEN evidence. | The original reviewed head emitted only one of the two same-source targets. The existing compiler witness now requires both and passes. | **already-fixed** by `224a17c2`; keep the focused compiler owner and its public on-demand limit in the coverage map. | +| B2 | `builder/index.ts:1275` still merges dotted and nested correlated refs. | With line 1336 repaired and line 1275 restored to dotted joining, a nested public include using ancestor aliases [`p.q`, `x`] and [`p`, `q`, `x`] lost the second value (`undefined` versus `22`). | **fixed-now** by segment-safe deduplication at this site. The route-context oracle owns the public witness. | +| B3 | `builder/index.ts:1336` still merges dotted and nested parent refs. | At `b602188f`, a one-level public include returned `nested: undefined` rather than `22`. Changing only this key to `JSON.stringify(ref.path)` made the same assertion pass. | **fixed-now** at this site. The oracle also checks a parent update after initial materialization. | +| B4 | `compiler/index.ts:2241` still merges dotted and nested include result routes. | A public conditional projection with result paths [`a.b`] and [`a`, `b`] returned both child results. A temporary mutant that bypassed `getUniqueIncludesRoutingKey` lost the flat child at the exact public assertion. The same allocator is used for projected source and direct include routes at lines 649 and 676. | **refuted for the stated public path**: this key is a unique internal routing name, and the allocator disambiguates equal dotted spellings. Preserve the public route test and its parent-key/child-insert checkpoints. | +| B5 | A shared `serializePath` helper would close every remaining site. | The two true builder collisions are fixed in place. The compiler's routing names are already unique; its dotted spelling is display text for an allocator, while the builder keys represent path identity. The source also has dotted joins for warning text, not identity. | **deferred** helper extraction in this record. A coordinated path-encoding change can revisit it if further identity sites need the same rule. | +| B6 | The dotted-path bug class was not globally closed. | B2 and B3 were reachable public failures at the reviewed head. Their fixed witnesses cover one-level and nested `toArray` includes, ancestor aliases, initial results, and selected updates. B4 is a passing public route control with a killed wrong allocator. | **fixed-now for the reported live sites, with bounded coverage**. Arbitrary path segments, other recursive source forms, every materialization form, and public on-demand adapter histories remain outside these witnesses in the coverage map. | + +The follow-up changes two existing production lines, net zero. Across the full +PR relative to the reviewed `origin/main` base, physical production source is +9 added and 8 deleted lines, net **+1** (one explanatory comment). This does +not meet a net-negative production-code target; removing a useful comment to +change the count would not simplify the implementation. Tests and contract +documentation are reported separately. + +At the follow-up worktree, the includes route-context oracle, bounded indexed +path oracle, and lazy-target compiler suite passed together: 114 tests. Changed +production/test ESLint and `git diff --check` passed. The package typecheck +could not resolve `@tanstack/db` in two existing conformance contracts in this +isolated worktree; it named no changed file. The PR's CI is the remaining type +validation gate. + +After merging current `main`, the same three suites plus the newly merged +subquery user-value oracle passed together: 134 tests. The oracle campaign +keeps both this PR's indexed-path owner and `main`'s new subquery owner. + +The second reviewer found two real public bugs beyond the original local fix +and identified the correct class of collision. The compiler routing claim and +shared-helper prescription overreached: they did not account for the allocator +or distinguish identity keys from display keys. Accuracy is mixed; depth and +signal are stronger than the first review because B2 and B3 were important +omissions. Hire recommendation for independent high-stakes review remains +**no** on this sample without executable same-path checks. diff --git a/docs/contributing/oracle-reviews/pr-1940-held-error-recency.md b/docs/contributing/oracle-reviews/pr-1940-held-error-recency.md new file mode 100644 index 0000000000..94932e2872 --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-1940-held-error-recency.md @@ -0,0 +1,59 @@ +# Query Collection held-result error recency + +Reviewed PR head before this correction: `a4a8ba8d0c045dcad23f97d3b98dc7298281f1db`. +Reviewed semantic correction: `8bd3fc35`. +Primary owner: `packages/query-db-collection/tests/ownership-lifecycle.oracle.test.ts`. + +The public `lastError` and `errorCount` contract retains a completed failure +until a later successful result applies. The added two-cell history starts with +one failed Query fetch, then holds a successful retry at a mocked persisted +baseline scan. A second refetch fails before the held success applies. At the +checkpoint after releasing the scan, the later failure must remain visible and +the count must be two. One cell uses `clearError()` and distinct Error objects; +the other uses `refetch()` and repeats the same Error object in one clock tick. +The expected state follows the order of completed failures and successful +applications, without reading production error timestamps or revisions. + +On the uncorrected PR head, both cells failed: the first observed +`lastError === undefined` after the older application, and the second observed +`errorCount === 1` after two terminal failures. The correction guards error +clearing with a revision captured when application begins and distinguishes +terminal Query failures using Query Core's per-Query `errorUpdateCount`. It +retains the older successful rows when they apply; it only prevents that +application from clearing an error recorded later. + +The review's exact mutation-held sequence was also run on the uncorrected PR +head. It did not erase the newer error; that path published the earlier rows +after the handler settled while preserving `lastError` and `errorCount`. The +persisted path is the confirmed counterexample. The persisted scan is a +controlled callback, not native SQLite execution. Native persistence, +concurrent retries for multiple tracked Queries, mutation-handler fetch +settlement, and deferred result application remain separate coverage cells in +the Query DB ownership row of `oracle-coverage.md`. + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Pass. The public `lastError`/`errorCount` comments and the owner state the law; the two histories and native-persistence limit are named above. | +| ORC-002 | Pass. Expected error identity and count come from the sequence of terminal failures and applications. The test does not use production timestamps, the revision guard, or Query Core's counter to compute expectations. | +| ORC-003 | Pass. The existing ownership oracle supplies its contract and model; the two-case history and real QueryClient driver sit beside the public `lastError`/`errorCount` refinement checks. | +| ORC-004 | Not applicable. This is a bounded deterministic two-cell history, not a generated property or a claim about all schedules. | +| ORC-005 | Pass. The driver uses public `loadSubset`, `clearError`/`refetch`, and Query Collection utils. It proves each fetch and scan occurred and checks public error identity and count both before and after releasing the scan. | +| ORC-006 | Pass. Both cells failed at their intended public checkpoints on `a4a8ba8d` and pass at `8bd3fc35`. The first rejects premature clearing; the second rejects timestamp-and-object-only error deduplication. | +| ORC-007 | Not applicable. No generated property or random campaign changed. | +| ORC-008 | Not applicable. No reference-model state was added, combined, or removed. The production revision has a separate error-event purpose. | +| ORC-009 | Pass. Query fetch, result application, persisted baseline, and public error state use the glossary and owner terms; the test does not call a fetch completion a publication. | +| ORC-010 | Pass. No shrinking or normalized capture occurs. The `finally` block releases every controlled Promise; the owner cleanup collects failures without replacing the assertion failure. | +| ORC-011 | Not applicable. The public identity/count observations distinguish the reported fault directly. The same-object cell challenges the proposed dedupe mechanism rather than supplying a second semantic reference model. | +| ORC-012 | Pass for this bounded correction. This record names the reviewed semantic head, every applicable ORC-001–011 outcome, the killed wrong designs, and remaining coverage cells. | + +Verification at `8bd3fc35`: 532/532 Query DB package tests passed across 15 +files; Query DB source typecheck passed; changed-file ESLint had zero errors +and four warnings already present outside the edited lines; Prettier and Git +diff checks passed. The new persisted cases alone passed 2/2 on the PR branch. + +Production weight for this correction is 31 added and 17 removed lines, net ++14 relative to the prior PR head. The complete PR production diff is 46 +added and 24 removed lines, net +22 relative to `origin/main`. The added +state separates genuinely new terminal failures from observer re-notification +and fences an older asynchronous application; it does not add retry or +recovery machinery. The oracle adds 91 lines, counted separately. diff --git a/docs/contributing/oracle-reviews/pr-1997-sqlite-prefilter-review.md b/docs/contributing/oracle-reviews/pr-1997-sqlite-prefilter-review.md new file mode 100644 index 0000000000..7234151cac --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-1997-sqlite-prefilter-review.md @@ -0,0 +1,73 @@ +# PR #1997 SQLite prefilter oracle review + +Reviewed source head: `8dd372c2575cec594b7add5df2f61bede5165346`. +The published source before this repair was `5c45eaf1d74481aeb62a135236ffba3d363d5915`. +This record is added in a documentation-only follow-up to the reviewed source. + +## Contract and evidence + +At `SQLiteCorePersistenceAdapter.loadSubset` return, the SQL candidate set must +contain every row that the JavaScript predicate can return. The adapter may read +extra rows. Where an expression index is claimed, the receiving SQLite plan +must use that named index. The independent expected result comes from explicit +JavaScript comparisons and object/array fixtures, not the SQL compiler. + +The reported source failed three real-adapter witnesses at the SQL candidate +checkpoint: a large persisted Number compared with a nearby BigInt bound, +`{ part: { '0': 'match' } }` queried through `part.0`, and a Kelvin sign after +NUL in a lowercase equality. Each had one JavaScript match and zero SQL +candidates. The old nested boolean compiler read `args` 3,321 times at depth +80. Those are assertion failures at the intended checkpoints, not setup or +cleanup failures. + +The repaired Node receiving oracle checks candidate and public keys for four +inequalities, both operand orders, unary AND/OR, numeric object and array +carriers, coalesce, membership, prefix LIKE, lowercase equality, and NUL +placement. It checks named-index use for numeric paths through three digit +segments. A four-segment path checks the full-read fallback. The core oracle +checks that an 80-level nested AND reads arguments at most 800 times. On the +reviewed source, the Node expression-index file passed 116 tests and typecheck; +the full Node package passed 185 tests and typecheck. The full SQLite-core package passed 679 tests +with two TODOs and typecheck. The core package build, changed-file lint, and +format check passed. Coverage was disabled for local Vitest runs because the +worktree lacks its optional Istanbul package. + +The safe BigInt bound is kept selective. An unsafe positive BigInt bound adds +a candidate range over the indexed expression from `Number.MAX_SAFE_INTEGER` +upward; an unsafe negative bound adds the corresponding negative range. This admits Numbers +whose serialized decimal and binary value straddle the bound while JavaScript +still decides the public result. The real SQLite plan uses the named index for +the selective range orientation in either operand order. Its opposite +orientation can scan because the candidate union also admits text and rounded +Numbers. Numeric path compilation uses the same +array/object alternatives for index DDL and runtime predicates. More than +three numeric segments fall back to an unbounded candidate read. + +## ORC-012 requirement audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Applicable. The candidate-superset and indexed-plan laws above have their authority in the existing `loadSubset` evaluator and RFC #1659 invariant 8. Claims are bounded to the tested SQLite paths and values. | +| ORC-002 | Applicable. Fixed expected keys follow JavaScript comparison and independently constructed values; the direct SQL candidate keys are observed separately. The existing generated expression-index model uses its own path walker and scalar equality. | +| ORC-003 | Applicable. Both oracle file headers state the contract, fixture/model, grammar, real adapter driver, checkpoint, and comparison. | +| ORC-004 | Applicable to the existing generated expression-index campaign. Its file reconstructs the seven declared axes, checks ablations, bounds path/value domains, and rejects duplicate or missing axes. The new review witnesses are finite matrices, so no new random grammar is claimed. | +| ORC-005 | Applicable. The Node oracle uses the public core adapter with the real Better SQLite driver, captures the exact receiving SELECT, and compares raw candidates, public keys, and `EXPLAIN QUERY PLAN`. The BigInt range matrix checks named-index use on the selective orientation and records the broad orientation's scan limit. The core oracle records SQL and compiler argument reads at `loadSubset` return. | +| ORC-006 | Applicable. The prior code lost all three reported matches after SQL filtering; the old nested compiler exceeded the fixed work bound. The existing generated oracle also retains SQL and grammar mutants. | +| ORC-007 | Applicable to the existing generated property. Its fixed-seed and seedless campaigns run the same matrix with direct seed/path replay. The new finite matrices are executed by both ordinary and oracle package runs but do not claim random coverage. | +| ORC-008 | Inapplicable. Neither repair changes a stateful reference model. | +| ORC-009 | Applicable. “SQL candidate” means a row returned by the receiving SELECT before JavaScript filtering; “carrier” in the fixture means the object or array containing a digit path segment. Neither is a new production state. | +| ORC-010 | Applicable to the generated Node campaign. Its existing helper retains the primary semantic mismatch if SQLite cleanup fails, and replay preserves seed/path. The new fixed cases have no shrinking step. | +| ORC-011 | Inapplicable to this repair: no specific fault shared by the explicit JavaScript expectations and the SQL compiler was identified. The existing equality witness also compares a full adapter scan and a separate path walker. | +| ORC-013 | Applicable. The three-segment matrix reaches all eight array/object carrier combinations and checks indexed results; a four-segment object path distinguishes the bounded fallback. The large Number/BigInt and NUL cases distinguish the reported candidate boundary from the former narrow filter. | +| ORC-014 | Applicable to the core compiler-work observation only. Its `node:sqlite` driver reaches the real core adapter; the Node expression-index oracle separately receives the compiled predicates through the Better SQLite driver. No native-host claim is made. | + +## Scope left open + +These checks establish the reported regressions and nearby legal histories at +the named receiving paths. They do not prove every persisted scalar type, +arbitrary numeric-path depth, every LIKE pattern, native-host planning, +limit/offset after filtering, index use for broad unsafe BigInt range +candidates, or elapsed performance. The SQLite boolean and +Node expression-index entries in `docs/contributing/oracle-coverage.md` own +those remaining cells. A reachable in-scope counterexample would reopen the +candidate-superset claim. diff --git a/docs/contributing/oracle-reviews/pr-2043-retained-observer-snapshots.md b/docs/contributing/oracle-reviews/pr-2043-retained-observer-snapshots.md new file mode 100644 index 0000000000..80575becab --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-2043-retained-observer-snapshots.md @@ -0,0 +1,68 @@ +# PR #2043 retained observer snapshot oracle review + +Reviewed source head: the commit that adds this record on +`perf-lazy-observer-snapshot`. The production change is unchanged from +`069633263`. This record adds the fixed-seed campaign and this audit. + +## Contract and evidence + +`LiveQueryObserver.getSnapshot()` returns one object for each revision. A +consumer can keep that object and read `data` or `state` for the first time +after the Collection changes and after the observer builds newer snapshots. +The first read, in either order, must show the rows that were visible when the +observer built the snapshot. `data` must list the rows of `state.values()` in +the same order. A single-result query keeps only the first such row. + +The authority is the snapshot contract in +`packages/db/src/live-query-observer.ts`: "Each snapshot owns a captured view +of `state`/`data`, so reading an older snapshot cannot expose rows from a later +revision." #1542 broke this law with a getter that read the live Collection on +first access. The external review of #1542 reproduced that failure. + +The oracle is +`packages/db/tests/live-query-observer-snapshot-oracle.property.test.ts`. Its +model is a sorted map from key to version. The model records its rows at each +`capture` step, next to the unread production snapshot. At the end, the driver +reads each retained snapshot in a generated order and compares both properties +with the recorded rows. + +## Mutant results + +All mutant runs used the reviewed test file. The production source was +restored after each run. + +| Mutant | Outcome | +| --- | --- | +| The `state` getter reads `collection.entries()` on first access (the #1542 design) | Assertion failure in all four generated cases at fixed seed 2043, path `0:2:1:1:1`, and in the pinned history. The calibration test passed. | +| The `state` and `data` getters read the observer's latest entries | Assertion failure in 5 of 6 tests (recorded by the implementation run before this record). | + +Direct replay of the first mutant with `TANSTACK_DB_ORACLE_SEED=2043`, +`TANSTACK_DB_ORACLE_PATH=0:2:1:1:1` and +`TANSTACK_DB_ORACLE_PROPERTY=live-query-observer.retained-snapshot` reproduced +the same counterexample: snapshot 0 captured no rows, but its `state` showed +row `a`. + +## ORC-012 requirement audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Applicable. The law and its authority are above. The claim is limited to the shared observer over a local-only Collection behind an ordered live query. Hydration seeds, persisted status and framework wiring have other owners, which the oracle prose names. | +| ORC-002 | Applicable. The expected rows come from the model's sorted map. The model does not import the observer, its entry collection or its ordering. | +| ORC-003 | Applicable. The opening prose states the law, the model, the history grammar, the production path, the checkpoint and the limits. The ownership table maps each contract row to its observation. | +| ORC-004 | Applicable. The grammar has upsert, delete and capture over keys `a`..`d` and versions `0`..`9`, with a generated read order and a generated choice of `data` or `state` first. The pinned history reconstructs the case of a snapshot read after several newer snapshots. No ablation check is executable. An ablation of the read-order axis would remove the `state`-first case, which the live-read mutant needs. That gap is accepted for this bounded grammar. | +| ORC-005 | Applicable. The driver calls the production `getSnapshot()` with and without a subscriber. It reads the public `data` and `state` properties at the end of the history. | +| ORC-006 | Applicable. The calibration test gives the checker a snapshot whose properties read the live Collection, and the checker rejects it. The two production mutants above fail at the intended checkpoint. Each outcome is an assertion failure. | +| ORC-007 | Applicable. Each generated case runs the same property and budget twice: once with fixed seed 2043 and once with an unseeded or replay seed through `oraclePropertyOptions`. The replay interface accepts the seed, the path and the property name. The direct replay above reproduced the reported failure. The property does not use `fc.commands`. | +| ORC-008 | Applicable. The model keeps only the visible key-to-version map. The order of keys is derived from the map, so the model keeps no separate order state. | +| ORC-009 | Applicable. "Capture" is a model-only step. It means one call to `getSnapshot()` whose result is kept unread. "Recorded rows" are the model rows at that step. | +| ORC-010 | Applicable, with a gap. The harness cleans up the observer, the query and the source in a `finally` block after the comparison. If cleanup throws after an assertion failure, the cleanup error replaces the assertion error. The cleanup calls are local-only Collection cleanups, and no run showed a cleanup failure. Shrinking reduced the mutant counterexample to one upsert and one capture, and the replay reproduced it. | +| ORC-011 | Inapplicable. No reviewer named a fault that the model and production could share. | +| ORC-013 | Inapplicable. The oracle protects no threshold or range law. | +| ORC-014 | Inapplicable. No controlled provider supplies a premise. | + +## Unresolved + +- No per-adapter test proves that an adapter never reads `state` for a + `data`-only consumer. The allocation counts in the PR were taken with scratch + tests. A permanent check needs a test seam in production, which this PR does + not add. diff --git a/docs/contributing/oracle-reviews/pr-2062-external-review.md b/docs/contributing/oracle-reviews/pr-2062-external-review.md new file mode 100644 index 0000000000..33634dca04 --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-2062-external-review.md @@ -0,0 +1,101 @@ +# PR #2062 external review audit + +Reviewed product commit: `39721f20f4a3daa597a81c5172edf0109a4d83c1`. +Base: `9e8ed997885fb46ac98ec85906f4ca4f662e7cce`. +The source review supplied ten findings from code reading and no executed +reproduction. This record evaluates those claims against the reviewed commit +and the follow-up changes in this PR. Each source-order finding is recorded +below. + +## Finding dispositions + +| ID | Claim | Evidence and verdict | PR action and durable value | +| --- | --- | --- | --- | +| U1 | A synchronous scheduler callback throw leaves persistence stuck. | Confirmed. The direct serial-pacer witness failed with no second start (`[]` instead of `[2]`). A public manual-commit fault probe also left its next transaction pending on the reviewed commit. | **fixed-now**: reset scheduler activity, retain any reentrant pending callback, and rethrow the original error. Two direct throw histories now pass. | +| U2 | Manually committing a returned paced transaction makes its scheduled callback throw. | Confirmed conditional history: the public probe reached `completed` at the scheduled callback and observed the invariant error. The guide says the strategy controls write timing but does not explicitly define external `tx.commit()` on its returned transaction. | **design-decision**: decide whether strategy ownership forbids manual commit or manual commit must run early. The U1 repair prevents permanent scheduler poisoning; it does not define manual-commit semantics. | +| U3 | A throwing `onMutate` consumes debounce's leading edge forever. | Confirmed for a callback that throws before changing a row. The oracle expected immediate second start `[2]` and observed `[]` on the reviewed code. | **fixed-now**: restore leading admission when the failed callback installed no timer. Both trailing settings pass; the throwing callback's error identity remains observable. | +| U4 | A throwing `onMutate` consumes throttle's window and leaks partial optimistic intent into a later write. | Both effects reproduced. The window regression is new; the partial-intent leak also exists in the base transaction implementation. The original manual-transaction oracle failed for same-key update, distinct insert, and delete. | **fixed-now**: restore an unused leading window and restore the transaction's pre-callback mutation snapshot after a synchronous throw. The user chose to include the transaction contract change in this PR. Eight transaction histories and two paced receiving histories pass. | +| U5 | Repeated debounce calls can cancel an eligible leading write behind a held handler. | Confirmed. The original code kept only `[[1]]` where the oracle required `[[1],[2,3]]` when the held handler returned before the renewed quiet edge. | **fixed-now**: keep a pending leading callback; calls can join its transaction without canceling eligibility. Release before and after the quiet timer, plus a later no-duplicate cut, pass. | +| U6 | A throttle pending merge can coexist with a stale trailing timer that overwrites it. | Refuted for the stated path. The timer is cleared before it transfers its callback to serial pending state. The controlled held-write probe reaches the `serial.hasPending()` merge branch and persists `[2,3]` once, with the same transaction receipt. | **refuted**: retain the pending-merge oracle witness. A longer reentrant failure schedule remains outside this finite proof, so the invariant should be revisited if the timer handoff changes. | +| U7 | Two clocks cause an extra wait; use zero wait in the serial pacer. | Refuted as a fix and as the stated timing rule. Admission and actual backend starts are different boundaries. The zero-wait hostile mutant fails the existing actual-start spacing assertion: the third write starts one tick too early. | **refuted**: keep both clocks and the timing oracle. The coverage map still limits arbitrary schedules. | +| U8 | The commit-completion fallback is copied four times. | Confirmed by source inspection. A receipt can reject before its handler completes after manual rollback. | **fixed-now**: one `runWithCommitCompletion` helper is used by debounce, throttle, and queue. Existing manual-rollback and settlement histories pass. | +| U9 | Strategy type checks and positional callbacks deny every custom strategy dropped-call isolation. | The generic layer does branch on `_type`, but the claimed general bug conflates the built-in debounce/throttle admission convention with legacy custom queue and batch behavior. The oracle proves a false-returning custom batch retains its scheduled callback. | **refuted** as a correctness claim. Preserve the API-shape concern; a uniform admission result needs a separate compatibility design, not a change inside this repair. | +| U10 | The 48-line serial pacer violates the code-weight rule without an architectural reason. | The code weight is real. Installed Pacer Lite's debouncer has no asynchronous completion gate; its queuer drains callbacks synchronously without awaiting them. The 24 held-write histories and manual-rollback witnesses enforce the one-persisting law that a plain timer or LiteQueuer cannot express. | **refuted** as an AGENTS.md violation. The positive production diff is justified by the documented independent scheduler boundary. Keep reviewing production weight separately from oracle and documentation growth. | + +## Law coverage and calibration + +The paced owner is `packages/db/tests/paced-mutations-oracle.test.ts`. +Its public authority is the paced guide's one pending and one persisting +transaction rule, immediate optimistic admission, and debounce/throttle +edge behavior. The new synchronous-throw scheduler seam has a two-callback +model: the first error remains visible and the second eligible callback +starts. It failed at the second-start assertion before the fix. The reentrant +pending neighbor also passes. This seam does not decide U2's external +manual-commit policy. + +The leading-admission model treats a callback that throws before changing a +row as admitting no mutation. Its next same-tick call keeps the leading edge. +Both factories and both trailing settings reach that premise. The reviewed +code failed the start cut for debounce and throttle. A separate held-handler +history checks that a later debounce call cannot cancel an already eligible +leading transaction; the original implementation failed at the release cut. +The repaired driver observes rows at admission, ordered handler payloads at +start, transaction identity, receipt settlement, and absence of a later +duplicate. These finite histories do not cover arbitrary-length bursts. + +The optimistic transaction owner is +`packages/db/tests/optimistic-transaction-oracle.property.test.ts`, after +the stable `optimistic-history-oracle.ts` contract. The independent rule retains +only successful synchronous `mutate` callbacks. The grammar crosses a prior +successful callback or none with three failure operations, then commits a +later successful callback; a second witness touches two Collections. The +driver compares public rows and pending mutation payload immediately after +the throw, then the handler payload at commit. The original implementation +failed all three failure-operation kinds at that throw cut; the repair passes +all eight manual histories. A restoration-listener fault killed the first +repair at the original-error assertion. The final repair restores every +Collection and preserves the callback error as the aggregate cause. The paced +owner checks the same rule through +debounce and throttle. `query/scheduler.test.ts` checks that abandoned join +work cannot publish after the failed callback and explicit rollback. The +contract permits transient subscriber notifications while the synchronous +callback runs. Nested throwing callbacks and asynchronous work after that +scope are not covered here. + +The throttle pending-merge witness reaches the timer-to-serial handoff while +a first handler is held. It asserts one successor start with both admitted +rows, fulfilled receipts, and no later repeated start. The source transition +keeps `trailingTimeout` and serial pending mutually exclusive on that path. +The actual-start timing oracle rejects a zero-wait serial pacer with an +assertion failure one tick before the required spacing edge; the original +non-mutant code passes. These checks do not establish all possible timer +schedules or real-server timing. + +The committed coverage map names the bounded owners and remaining histories. +No new random campaign is claimed. Controlled Collections and fake timers +prove the DB source path, not framework scheduling or external persistence. + +## Oracle guide audit + +ORC-001 through ORC-003: guide and transaction documentation state the laws; +the paced and optimistic owners keep their model, legal grammar, driver, and +comparison cuts beside the code. ORC-004 and ORC-007 do not apply to the new +finite matrices. ORC-005 is met by real Collections, transactions, strategies, +and handler starts. ORC-006 is met by the original assertion failures and the +zero-wait hostile mutant; no timeout is counted as a kill. ORC-008 and ORC-009 +are met by the explicit pending-IDs and successful-callback abstractions. +ORC-010 uses cleanup helpers that preserve the primary failure. ORC-011 has +no named shared semantic fault requiring a second model. ORC-012 is this +revision-bound record and the coverage map. ORC-013 is met by failed and +successful neighboring admission cases and both sides of timing edges. +ORC-014 does not apply beyond the controlled DB boundary claimed here. + +## Reviewer assessment + +The review found several real asynchronous and exception-boundary failures +without test execution. Its strongest points were the scheduler stall, +leading-edge loss, and canceled leading write. Its weaker points inferred an +unreachable stale-timer combination and proposed zero wait despite the +actual-start spacing law. It also combined a new throttle regression with a +pre-existing transaction error contract. The actionable signal is high, but +the proposed fixes require executable calibration and contract separation. diff --git a/docs/contributing/oracle-reviews/pr-2062-followup-629c836.md b/docs/contributing/oracle-reviews/pr-2062-followup-629c836.md new file mode 100644 index 0000000000..98f26c3ad7 --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-2062-followup-629c836.md @@ -0,0 +1,111 @@ +# PR #2062 follow-up review at 629c836 + +Reviewed product commit: `629c83609d726a0a1e52c7838992594703394f05`. +The source was a nine-item medium-effort code-reading review with no executed +checks. This record keeps its source order and distinguishes the reviewed code, +the local follow-up repair, and questions that remain open. PR #2059's oracle +instrument guidance was read before changing the owning tests. Its distinction +between an observed bug and a chosen policy governs this audit. + +## Finding ledger + +| ID | Claim | Evidence and technical verdict | PR action and durable value | +| --- | --- | --- | --- | +| R1 | A canceled pending debounce leading transaction can strand a later call. | Confirmed. Direct rollback and same-key cascade both left the third transaction pending at the held-handler release cut. | **fixed-now locally**: track the pending leading transaction's identity. Both histories now start writes 1 and 3 and settle the third receipt. | +| R2 | A restoration subscriber writes into the failed transaction because its context remains ambient. | Confirmed. The original handler payload included subscriber row 3 after the failed callback's snapshot had been restored. | **fixed-now locally**: remove the failed callback's ambient frame before restoration publishes. Nested success and failure neighbors retain an enclosing callback's context. | +| R3 | A throwing outer `onMutate` can erase an admitted nested call while its receipt fulfills. | Confirmed. Debounce and throttle failed the nested receipt/backend-intent implication; the earlier-pending neighbor remained red after the first cleanup repair. | **fixed-now locally**: the user chose group rejection. Every receipt sharing the pending transaction rejects with the callback error, including an earlier call; a later independent call persists. Both factories pass fresh and earlier-pending histories. | +| R4 | Throwing `onMutate` retains a newly created empty pending transaction in its scope. | Confirmed by the scope-retention witness for admitted and dropped debounce calls, plus the adjacent queue path. All three were pending on the reviewed implementation. | **fixed-now locally**: roll back the newly created transaction and preserve the callback error. The three witnesses pass; this cleanup alone does not close R3. | +| R5 | Sharing one strategy instance across managers can lose another manager's reentrant write. | Confirmed. The two-manager probe and primary oracle both left the second receipt pending with no second backend start. The user chose to preserve one shared timing boundary and separate transaction receipts. | **fixed-now locally**: pending callbacks are keyed by transaction while the timer and serial pacer remain shared. Reentrant leading/trailing, ordinary trailing, renewed quiet, and held-handler rollback histories now settle both managers' work without overlap. | +| R6 | A manual early commit makes a later strategy timer throw globally. | Confirmed. The original manual commit started persistence, and its debounce timer later threw. A held-handler oracle failed the manual-guard cut for debounce, throttle, and queue. | **fixed-now locally**: the user chose strategy-owned commit. Manual `commit()` throws before state changes; the scheduled write still waits for its timer and prior handler. All three strategy witnesses pass. | +| R7 | Eager mutation-array snapshots add quadratic work over many calls. | Confirmed: five no-op calls with 16 prior mutations made 80 snapshot-entry visits on the reviewed code. `applyMutations` already traverses prior mutations for every real write, and catch-only copying cannot recover earlier merges. | **fixed-now locally for the added copy**: capture the prior array only before the callback's first `applyMutations`. The same counter now records zero visits for five no-op calls; the atomicity owner still passes. Real writes retain the pre-existing linear merge traversal, so long sequences remain quadratic overall. | +| R8 | The old issue-2058 review record incorrectly certifies current code. | The old record explicitly pins `632e46944` and limits its no-counterexample statement to a named grammar. R1 is outside that grammar, so the historical statement is not a current-head certification. | **stale as phrased**: preserve the old record and add this revision-bound audit. The coverage map now names the new histories and the reachable R3 gap. | +| R9 | A single-use throttle helper and debounce return branches should be shortened. | The helper is single-use, but an attempted inline check was narrowed to `undefined` across reentrant `onAdmit`; ESLint marked the branch unnecessary. A fresh helper read preserves the intended reentry observation. The ternary has no clarity gain. | **refuted as a useful cleanup**: retain the helper and explicit debounce branches. | + +## Law, reach, and calibration + +For R1, the guide's admitted-call persistence obligation and one-persisting +bound are judged by the paced owner. The legal history holds write 1, admits +write 2 into a pending leading slot, cancels it directly or through same-key +rollback, then admits write 3. The driver checks optimistic row 3 at admission, +only write 1 at the quiet edge, writes 1 and 3 after release, and all three +receipt outcomes. The reviewed implementation fails the backend-start assertion; +the ownership repair passes both histories. Longer arbitrary bursts and shared +manager state remain outside this finite calibration. + +For R2, the manual transaction contract says a failed synchronous callback +contributes no lasting intent. The optimistic transaction owner models the +subscriber's restoration-triggered write as a later authoring action. It +observes row 3 in the Collection but requires that the failed transaction's +payload contain no row 3 at the throw and commit cuts. The reviewed +implementation fails the payload assertion; the repair passes. Nested callback +success and failure witnesses check that removing one ambient frame does not +remove an enclosing frame. They do not prove arbitrary subscriber reentry or +cross-scope restoration. + +For R3, the user chose group rejection for paced reentry. Calls merged into +one pending transaction share its optimistic lifetime and settlement. The +paced owner checks both factories with a fresh transaction and with an earlier +pending call. At the throw cut, every group receipt rejects with the callback +error and no group row remains visible. At the scheduled edge, no failed group +member reaches the backend; a later independent call persists. The fresh pair +failed on the reviewed implementation, while the earlier-pending pair failed +on the partial repair. Both pairs pass after the group rollback. Ordinary +manual `Transaction.mutate` still retains successful earlier callbacks; only a +nested paced call that shares the failed group triggers this broader rollback. + +For R4, a callback that throws before returning a transaction cannot leave a +newly created empty pending transaction in its scope. The paced owner uses a +scope-retention assertion because the empty object has no Collection row to +observe publicly. The reviewed implementation fails at the post-throw state +assertion for admitted, dropped, and queue paths. The repair passes each path. +This is a resource lifecycle witness, not a claim about real-server timing. + +For R6, the user chose strategy-owned commit. The paced owner holds write 1, +admits write 2, and attempts manual commit before its scheduled start. Across +debounce, throttle, and queue, the reviewed path failed the synchronous guard +assertion. The repair leaves transaction 2 pending, starts no overlapping +handler, and later fulfills both receipts after write 1 releases and write 2 +becomes eligible. R5 uses the user's shared-timing contract. The paced owner +checks two managers on one strategy through reentrant leading/trailing and +ordinary trailing histories, plus a held first handler rolled back before the +second starts. A renewed-quiet history rejects a plausible partial repair that +leaves an eligible callback from another manager in the serial pacer. That +partial repair starts the callback at time 24 instead of the new time-32 edge. +A shared queue control retains two managers' independent +receipts in queue order. One pending callback slot cannot express this law: +the two transactions cannot merge, and replacing either callback loses its +receipt. The keyed serial-pacer queue is the needed architectural change. The +reviewed code fails the backend-start cut for both +factories; the repair passes. These finite histories do not cover arbitrary +numbers of managers or all queue capacity policies. R7 uses a deterministic +work count: the reviewed code visits 80 prior entries for five no-op callbacks +after 16 writes, while the repair visits none. The optimistic transaction +owner still checks restoration after failed writes, including nested +callbacks. The existing merge path remains linear per real write. R8 and R9 are +source and documentation judgments rather than product oracles. + +## Current local checks and limits + +The reviewed paced owner passed 134 tests before new histories were added. +R1, R2, R4, and fresh-transaction R3 failed the reviewed behavior at their +intended assertions. Earlier-pending R3 failed the partial repair; manual +commit failed the new guard cut on all three strategies. The local paced and +optimistic owners pass 298 tests. The final full DB package suite passes 8,848 +tests in 250 files, with no type errors. Focused ESLint, Prettier, and the DB +package build pass. Published PR checks remain. + +The nine source items are accounted for: seven locally repaired, one stale +historical claim, and one refuted cleanup suggestion. Published PR checks +remain before this branch can be called ready for review. + +## Reviewer assessment + +The review found four reproducible correctness or lifecycle gaps that the +existing tests missed, and it identified two real but policy-dependent paths. +Its strongest contribution is the cancel-then-readmit debounce history. The +performance observation is measurable, but its catch-only remedy would break +atomic restoration. The proposed documentation rewrite overlooks the old +record's revision limit, and the small cleanup suggestion would hide a +reentrant state read. Overall signal is high, with moderate fix quality; I +would accept this reviewer for correctness review while expecting executable +calibration and clearer separation of bugs from product choices. diff --git a/docs/contributing/oracle-reviews/pr-2065-74e399-review.md b/docs/contributing/oracle-reviews/pr-2065-74e399-review.md new file mode 100644 index 0000000000..d857dd639a --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-2065-74e399-review.md @@ -0,0 +1,98 @@ +# PR #2065 review at `74e399150` + +The ten-item medium-effort review of `packages/query-db-collection/src/query.ts` +was based on reading code and Query Core source; it ran no tests. This record +tracks the executable contract exposed by the review. + +## Laws and executable owner + +The Query Collection guide promises that an accepted direct write changes +Collection rows without automatically requesting a new server result. A fetch +that began before the write cannot undo its accepted keys. An explicit refetch +must establish post-write request authority. Cache edits must not claim fetch +success, freshness, or error recovery. A scoped cache may remain approximate, +but it cannot replay a stale row over the accepted write. A held +`deferDataRefresh` barrier prevents server result publication until release. +These are separate laws: no request on write, per-key order, explicit request +order, cache-state separation, and publication order. + +The primary owner is `ownership-lifecycle.oracle.test.ts`. Its reference is +the authored provider response, accepted direct-write order, and subset +ownership, not a copy of Query Core's fetch or cache-patch branches. The legal +histories added here include derived-select unload/reacquisition and eager +restart; ordinary unload of a still-fetching Query with an outside observer; +an explicit subset refetch; a derived result behind a held refresh barrier; +a provisional cached result waiting on a persisted baseline scan; two logical +observers sharing one physical Query; and a direct insert while the first eager +request is in flight. The driver uses the real QueryClient and Collection. +It checks request counts, public rows, Query cache rows and state at the +specific load, write, barrier, and refetch settlement cuts. Existing +`load-subset-lifecycle-oracle.test.ts` owns broader load settlement; +`cursor-pagination-oracle.integration.test.ts` checks that an idle, empty +eager cache entry can still be seeded by a direct write. + +The first five new regressions failed by assertion on the reviewed production +code before repair: derived subset reacquisition had no row; ordinary unload +revived a deleted row or reused the old request; explicit subset refetch reused +the old request; and a derived result published during the barrier. The +pending eager Query test also failed with data present in `status: pending`. +After repair, the pending-application test rejects a mutant that removes its +pending check at the cache-row checkpoint (`Older` versus `Manual`). The alias +test rejects a mutant that marks the physical Query visited before the +disjoint-scope skip (`Before` versus `After`). Separately deleting the +ordinary-unload fetch-start retention and the unobserved-fetch key claims +revived the row in the same controlled history; removing fetch-start retention +also left explicit refetch at one request instead of two. These are assertion +failures at the intended checkpoints, not setup failures or timeouts. + +The review's empty-cache deferred-refresh scenario cannot schedule a deferred +refresh: that path first requires a successful cached result. A cached-result +barrier-release control passes even when the extra cancellation call is +removed, because Query Core's normal refetch cancels a request when data +exists. The explicit subset-refetch subclaim is real and repaired. The review +also proposed replacing logical-hash fetch positions with a Query-keyed +WeakMap. That may simplify retirement, but query-function context provides a +key rather than the physical Query. A switch would add a Query lookup at fetch +entry and alter the ownership boundary. This review retains the current map +and checks both keep and retirement behavior instead. + +## Query Core compatibility + +The package had declared `@tanstack/query-core: ^5.0.0`. A standalone probe +against Query Core 5.0.0 held an empty-cache first fetch, cancelled it +synchronously, and called `observer.refetch()`. It made only one provider call; +the replacement refetch rejected with `CancelledError`. The current installed +5.90.20 source restarts when the retryer is already rejected, and the ownership +oracle passes the post-write refetch histories at that version. The peer +minimum is now `^5.90.20`, the version tested by this repository. A foreign +`QueryClient.fetchQuery` awaiting the cancelled request can still receive +`CancelledError` on that version. The existing overlapping-refetch oracle +records that separate Query Core boundary while requiring Collection refetch +callers to follow the successor. + +## Scope and remaining witnesses + +The finite Query Core histories prove the stated observation cuts for the +authored response shapes, observer order, and controlled persistence barrier. +They do not prove every custom hash collision, every Query cache removal +schedule, unbounded performance, native SQLite timing, or a full offline +reconnect replay. The Query DB ownership oracle owns further direct-write +cache histories; the cursor pagination owner covers windowed receiving, and +the offline transactions owner covers real `deferDataRefresh` replay. The +coverage map records these limits. No reachable in-scope counterexample was +left by this review's ten reported traces. + +## Compatibility follow-up at c64fd999 + +The prior `^5.90.20` peer floor was the installed test version, not the first +compatible release. Against the PR head before this manifest-only follow-up, +the post-write subset-refetch oracle timed out on Query Core 5.85.2 and passed +on 5.85.3. The upstream 5.85.3 change makes a fetch restart when its retryer +has already rejected. The full Query DB suite passed 668/668 on 5.90.15 and +5.90.19. On 5.90.14, the cursor fault-recovery property and the explicit +refetch-error remount test failed; upstream 5.90.15 changed error invalidation +and refetch-on-mount behavior. Query Core 5.0.0 cannot run the suite because +the package's pre-existing `partialMatchKey` import is not exported there. +The peer floor is therefore `^5.90.15`, the lowest full-suite version observed. +This finite version sample does not establish support for every newer 5.x +release. diff --git a/docs/contributing/oracle-reviews/pr-2069-explicit-storage-targets-6b0192c19.md b/docs/contributing/oracle-reviews/pr-2069-explicit-storage-targets-6b0192c19.md new file mode 100644 index 0000000000..415d6b64f2 --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-2069-explicit-storage-targets-6b0192c19.md @@ -0,0 +1,29 @@ +# Explicit SQLite storage targets: oracle review + +Reviewed code commit: 6b0192c19924e231049364cd5ef1fde7094245e0 +Scope: PR #2069 follow-up for the exported SQLite persistence adapter and its Browser and Electron receiving paths. + +The approved law is that an adapter data operation states whether it addresses an eager Collection or a claimed persisted cache generation. A managed target must carry its physical storage ID and claim ID on every read and write. Once that claim is collected, the operation rejects before it can recreate rows, metadata, or catalog state. An explicitly eager Collection may use the same ID bytes; no prefix is reserved. This is a new lower-level API contract, not proof that all third-party adapters honor claims. + +The contract-by-history-by-path-by-observation claim is bounded as follows: after a real SQLite claim, write, rotation, and collection, each of eleven core adapter data methods rejects the retired managed target, including through a reopened adapter. At each rejection checkpoint, the old row and tombstone tables and eight per-ID catalogs remain absent. The same test accepts explicit eager reuse of that ID while the managed target still rejects. A Browser coordinator history checks both leader and follower local mutations retain the claim through transaction construction; a real SQLite Browser receiver accepts a claimed mutation and rejects a late one without recreating old storage. An in-process Electron IPC receiver checks target forwarding under protocol v6. The native OPFS tests receive claim and hydration histories but do not enumerate all adapter operations. + +| Requirement | Outcome for this change | +| --- | --- | +| ORC-001 authority and limits | The design decision above is documented in the core README and the SQLite coverage-map owner. The oracle prose names eager reuse, managed rejection, and the native-host limits. | +| ORC-002 independent judgment | The expected outcome comes from the caller's declared target and claim lifecycle. The real-SQLite oracle compares physical tables and catalog rows; it does not call the production target resolver to derive expectations. Existing fixtures decode target IDs for older, unrelated row laws. | +| ORC-003 literate responsibilities | The SQLite resume-snapshot oracle explains the law, fixed history, real adapter path, physical observations, and settled checkpoints beside its test. The Browser coordinator oracle explains why local transaction construction must preserve the claim; the composed Browser receiver checks durable rows. | +| ORC-004 generated grammar | Not triggered by the new fixed target histories. Existing generated-history dimensions and models were migrated to the new adapter signature without changing their claims. | +| ORC-005 production path and observation | Real node:sqlite exercises all eleven core methods after collection and reopened reads and writes. Browser tests exercise local leader, follower RPC, and a real wa-sqlite durability path. Electron tests exercise the in-process main/renderer bridge. | +| ORC-006 calibration | A temporary bare-ID fallback mutant failed at the first post-collection read. A temporary eager reset-target mutant failed at the managed-reset target comparison. The follower claim-loss mutant failed at the follower success checkpoint; the original request returned success. These were assertion failures, not timeouts or setup failures; each mutant was restored. | +| ORC-007 campaigns and replay | Not triggered by the new fixed histories. The affected existing generated properties remained in their configured suites; no seed, path, or run budget changed here. | +| ORC-008 stateful-model minimality | Not triggered: this change adds a discriminated production API target but no reference-model state or transition. | +| ORC-009 vocabulary | The tests and documentation use Collection, persisted cache generation, persisted cache claim, physical storage ID, and rotation in the glossary's senses. The target kind is caller intent, not a new lifecycle state. | +| ORC-010 failure and cleanup | The SQLite history closes its database while preserving the primary failure. The real Browser receiving history uses failure-preserving cleanup for claims, coordinator, database, and temporary directory. | +| ORC-011 second formulation | A recording adapter alone could accept the wrong eager target. The real-SQLite Browser receiver independently checks durable row and catalog effects; Electron IPC is a separate transport cut. These do not prove arbitrary custom adapters. | +| ORC-012 review evidence | This record is tied to the exact code commit above; the later documentation commit does not change that reviewed code. | +| ORC-013 boundary distinction | Bare ordinary and cache-shaped IDs reject; explicit eager reuse of a collected physical ID succeeds; the old managed target still rejects. This distinguishes claim authority from a reserved-name rule. | +| ORC-014 controlled-premise handoff | The Browser recording witness reaches both routes, and its claim-preservation premise is received by real wa-sqlite for a leader mutation. Follower RPC over real OPFS and actual Electron process execution remain outside this cut. | + +Local verification on the reviewed code: SQLite core 963 passed and one todo; Browser 261 passed; Electron 105 passed; Browser persisted conformance 113 passed; Electric 680, Query 627, Node 178, React Native 200, Expo 62, Capacitor 92, and Tauri 64 passed. Native Chromium OPFS claim/hydration 27, Electric 16, and leader-close 6 passed. Six affected package typechecks, changed-file lint without errors, formatting, and diff checks passed. The Electron binary was unavailable for its real-process E2E suite. The documentation link checker reported 13 existing links outside its /docs boundary and no new link failure. A workspace package-manager attempt received a private-registry 403, so tests used installed binaries; the documented local checks are actual executions. + +Open boundaries: the API still permits a caller to choose an explicit eager target using a retired physical ID. That is the approved escape hatch for arbitrary eager Collection IDs, not an accidental managed fallback. A custom adapter can ignore the target contract, and a direct coordinator caller without a registered managed claim can request eager semantics. Native cross-tab scheduling and real Electron process behavior remain separate receiving cuts in the coverage map. This evidence closes the accidental bare-ID-to-eager adapter fallback for the exercised paths; it does not claim universal cache-generation lifecycle closure. diff --git a/docs/contributing/oracle-reviews/pr-2078-origin-attribution.md b/docs/contributing/oracle-reviews/pr-2078-origin-attribution.md new file mode 100644 index 0000000000..ec8b7332c5 --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-2078-origin-attribution.md @@ -0,0 +1,418 @@ +# PR #2078: origin attribution and refused insert review + +Review target: `a4e6acd33dbb41ca220541ef21414efb3fbbb5ce` against +`2c98b4992c412820f8476325ee6941db5ca9ac0f`. The eight external findings +were supplied in source order. The repair and executable oracle revision is +`43608b4f94a845de3e8d8be2373c42b0ab65efb7`. This record follows that +commit; it does not change its executable code. The private issue #2071 bridge +history remains unavailable and is not treated as refuted. + +## Finding ledger + +| ID | Reviewed claim | Evidence and technical verdict | Disposition and durable destination | +| --- | --- | --- | --- | +| F01 | `$origin` text omits pending transactions; a same-key source row can stay local after pending rollback, without truncate. | True. A manual pending transaction does not hold source commits. Four controlled cells cross optimistic visibility and fulfillment/rollback, and compare origin at source publication and settlement. | **fixed-now**: API comments, glossary, guide, both reference pages, and the primary optimistic-history model. | +| F02 | `pendingLocalChanges` says persisting, but includes pending. | True by the `overlayActiveTransactions()` state predicate and F01's public witness. | **fixed-now**: production comment names pending and persisting. | +| F03 | A truncate's retained origin leaks to a later same-key transaction in the same drain. | True product bug. The new reentrant history was RED on the reviewed production code: at failed-mutation settlement, the model expected `remote` and the public row was `local`. A one-transaction control passed. | **fixed-now**: scope retained snapshots to each sync transaction in `state.ts`; the same oracle is GREEN. | +| F04 | The source-batch model cannot express delete then reinsert within one transaction, and its opening omits that limit. | True on the reviewed head. The grammar now preserves mixed source operation order, and the opening states the default and ordered lanes. | **fixed-now**: primary model/driver and atomic versus later-transaction witnesses. | +| F05 | The real SQLite test claims an update but writes an insert. | True. The original continuation had insert, delete, insert. It now writes a real `update` and checks adapter forwarding, live/base/durable rows, and reopen. | **fixed-now**: real adapter receiving witness and accurate comment. | +| F06 | `cleanupRealWitness` duplicates `cleanupPersistedOracle`. | Substantially true shared loop, though the latter also enforces a 250 ms limit per action. | **fixed-now**: one `test-cleanup.ts` helper; the persisted owner keeps its timeout wrapper. | +| F07 | The first Collection is cleaned twice; a failure may remove the directory before resources settle. | The double call was true. The claimed open SQLite handle was not established: this witness uses a CLI driver with one process per operation. The old finalizer still attempted directory removal after cleanup failure. | **fixed-now**: track both Collections' successful cleanup and remove the directory only after both release; retain the primary failure and cleanup diagnostics. | +| F08 | The guide's timing rules leave overlapping same-key mutations unspecified while the oracle stays green. | The coverage gap is true. The guide already qualified its first-transaction rule to one mutation without truncate, so it did not promise every overlap. Sixteen bounded histories now cross two active same-key mutations, one/two queued source transactions, both settlement orders, and all success/failure pairs. | **fixed-now** for that bounded gap: guide, model, publication driver, and coverage map. Broader overlap schedules remain explicitly outside the closure claim. | + +The original review had eight items. All eight received an in-PR action. F07's +open-handle mechanism and F08's claim of an unqualified guide promise were +overstated; those qualifications do not erase their useful findings. + +## Laws and enforcement + +### L1. Active local keys and pending settlement (F01, F02) + +Authority: the key-and-timing `$origin` contract in `virtual-props.ts` and the +glossary. A pending manual local transaction is active before its mutation +function starts. A same-key source commit applies immediately, may receive +local attribution, and keeps that origin after the manual transaction rolls +back. The source identity is unknown. The old model had only `persisting`, +`completed`, and `failed`, so its grammar could not reach the reported cut. + +The model now distinguishes `pending` from `persisting`: the same source commit +applies immediately in the former state and queues in the latter. The driver +uses public `createTransaction({ autoCommit: false })`, `mutate`, sync writes, +`commit` or `rollback`, and Collection reads. Four cells cross optimistic +visibility and success/failure. After each step, the shared driver compares +public rows and origin, both subscriber replicas, downstream rows, request +outcomes, and complete publication cuts. A temporary production mutant that +required `hasPersistingTransaction()` before consulting `pendingLocalChanges` +reached the source and settlement cuts and failed all four cells. It was +restored before the executable commit. This establishes the bounded manual +one-key path, not every pending and persisting overlap. + +### L2. Truncate attribution ends with its own source transaction (F03) + +Authority: the existing per-atomic-transaction attribution contract and the +truncate comment in `state.ts`. A subscriber can submit a truncate and a later +same-key source transaction while core drains an earlier truncate. Those +accepted suffix transactions apply in source order during the next drain. +Attribution retained for the truncate must not color the later transaction. + +The old driver could only commit one source batch at a time, so a truncate +drained before it could queue a successor in the same drain. The new reentrant +step commits both from a real Collection subscriber. The independent model +queues the suffix and snapshots attribution for each batch. The one-batch +control expects local attribution; the two-batch history expects remote for +the later row. On the original production branch, the second history failed +at the public row observed when `isPersisted` rejected: expected `remote`, got +`local`. Moving the retained sets inside the transaction loop made both +histories pass. The driver also checks event replicas and downstream state. +Coverage is one active mutation, two truncates, and zero or one later same-key +transaction; arbitrary reentrant suffixes are not claimed. + +### L3. Ordered operations retain one atomic origin (F04) + +Authority: a sync transaction is the `begin()`/`commit()` atomic batch, and +the existing first-transaction origin law. The model's `row` action is a +grammar abstraction over source insert/update; the driver selects the source +write kind from its own key membership. The model does not import that driver +classifier. Ordered operations now represent delete then reinsert in one +batch. The model snapshots origin at batch start, consumes it for later +transactions, and derives expected rows independently of production caches. + +The fixed witness reaches the real sync path with delete then reinsert of an +existing key. A neighboring history adds a later same-key transaction. Public +origin at successful settlement is local in the atomic-only case and remote +after the later transaction. A temporary production mutant that omitted a +delete from the batch-local attribution set failed the atomic-only witness at +settlement, while the later-transaction control passed. The ordered grammar +rejects mixed `operations` with `rows`/`deletes` and more than one copy. Other +mixed operation sequences are representable but not exhaustively sampled. + +### L4. Successful overlapping mutations preserve one key grant (F08) + +Authority: queued source attribution is by key and timing, not by source +author or one grant per optimistic transaction. If two same-key mutations are +both persisting before a source transaction queues, a successful mutation +retains one attribution for the key even if its sibling fails. Two failures +retain none. The first same-key source transaction consumes that grant; a +second source transaction is remote without another local owner. + +The model already represented multiple persisting transactions but had no +distinguishing overlap campaign. Sixteen bounded histories now cross the two +outcomes, settlement order, and one/two source transactions. The core driver +compares public origin at each settlement, full event cuts, and downstream +rows. A temporary production mutant clearing `pendingLocalOrigins` whenever a +failed sibling remained failed four one-success/one-source cells at the +settlement read, while two-failure controls passed. The tested histories do +not include pending plus persisting overlap, multiple keys, truncate during +overlap, or all possible handler schedules. The coverage map retains those +limits; no universal overlap proof is claimed. + +### L5. Real SQLite receives source updates after refusal (F05, F07) + +Authority: accepted source transactions must reach the Collection and its +durable persisted replica; a rejected local insert contributes no durable row. +The real SQLite witness holds outbound rejection, accepts a same-key delete, +then follows one of two histories. The continuation now inserts, deletes, +reinserts, and updates through `SyncConfig.write`. It checks live and exposed +base rows, `applyCommittedTx`'s update mutation, durable `loadSubset`, and a +fresh Collection/adapter/driver reopened over the same file. A temporary +adapter mutant dropped only the final update before durable application. It +reached the new update cut and failed the durable-row comparison: SQLite kept +`later source row` instead of `updated source row`. The original broad mutant +also dropped an earlier source turn and failed too early; that result is not +counted as update calibration. The receiving witness uses a SQLite CLI driver +and a same-process reopen; other hosts and a process restart remain outside. + +## Oracle-guide check + +| Requirement | Result | +| --- | --- | +| ORC-001 authority and limits | L1–L5 name the contract and finite path limits. The private bridge remains unknown. | +| ORC-002 independent judgment | The model derives rows and origin from source actions and key ownership, without production caches or classifiers. The shared `sourceOperations` function only expands grammar order. | +| ORC-003 responsibilities | Opening prose states laws and limits; `HistoryModel`, `OptimisticStep`, the real Collection driver, and `check()` keep model, grammar, path, and refinement distinct. | +| ORC-004 grammar controls | Pending versus persisting, one versus two queued source transactions, batch versus transaction boundary, and both settlement orders have reconstructing witnesses. Bounded domains are above. The driver rejects an ordered batch mixed with legacy rows/deletes or multiple copies, and a reentrant trigger without truncate. Other legal schedules remain listed. | +| ORC-005 path and observation | Public Collection writes, sync callbacks, subscription replicas, downstream rows, receipts, and settlement-time reads reach the named cuts. The real adapter path checks durable and reopened rows. | +| ORC-006 calibration | The original production code and four targeted hostile edits fail assertions at the intended public cuts. The too-broad SQLite edit failed early and is excluded from the update claim. No timeout or setup error is counted as a kill. | +| ORC-007 fixed/random parity | Not triggered by the new bounded enumerations and fixed histories. Existing generated campaigns in the owner retain their prior configuration. | +| ORC-008 model minimality | Pending differs from persisting because sync applies now versus waits. Source operation order and a combined reentrant drain change legal next observations. | +| ORC-009 vocabulary | `pending`, `persisting`, sync transaction, publication, and settlement match the glossary. The model-only `row` action combines source insert/update as stated in the opening. | +| ORC-010 failure fidelity | The core owner keeps its primary failure through cleanup. The SQLite owners now share cleanup aggregation; the persisted owner still bounds each stage, and the real witness releases Collections before directory removal. | +| ORC-011 second formulation | Atomic delete/reinsert versus a later transaction challenges batch-boundary semantics. Causal source authorship cannot be independently decided without a source identity signal; no such guarantee is claimed. | +| ORC-012 review evidence | This record binds original head, executable revision, RED/GREEN, mutants, finite closure, and remaining cells. | +| ORC-013 distinguishing boundary | One versus two source transactions, one versus two reentrant suffix batches, pending versus persisting, and one/two overlapping outcomes distinguish plausible wrong timing rules at public cuts. | +| ORC-014 controlled handoff | The real SQLite receiver tests the controlled refused-insert and source-update premise. The core reentrant history is a core claim, not claimed as real-provider coverage. | + +## Verification and limits + +On the executable revision, 166 core optimistic history and state-retention +tests passed; the commit-hook rerun passed 61 core publication and outcome tests. +The relevant persisted and real SQLite run passed 18 tests, with unrelated +persisted cases filtered. Vitest's test type checks passed. Prettier and the +changed-file ESLint run had no errors; ESLint reported pre-existing warnings +in the large persisted oracle, and one new shadow warning was repaired before +commit. Git's commit hook ran ESLint fixes; the two focused suites were rerun +after the commit. The production diff in `state.ts` is seven added and six +removed lines, including the corrected comment. + +The issue #2071 bridge sequence is still unavailable. This review does not +claim to close its private path, every overlap schedule, every mixed source +batch, another SQLite host, or process restart. The optimistic-history owner +can represent mixed source order and bounded reentry; a bridge-specific +counterexample needs the callback/write sequence and a persisted-wrapper +receiving witness. The coverage map names those limits. No authorized in-scope +repair from these eight findings is left open. + +Final accounting: **8 raw items = 8 fixed-now**. F07 and F08 include the +technical qualifications recorded above; neither was dropped from the ledger. + +## Post-audit CI and review follow-ups + +The full `db-2` CI shard on `00fcf0f` passed 5,901 runtime tests but reported a +test type error at `optimistic-history-oracle.ts:987`: the new manual +`Transaction` was missing from the driver's transaction union. +Commit `9cb0e9998ba7fc4f8bb67f2ddc6c45e58717c24b` adds that type. The +package test TypeScript check and 42 origin-publication tests, including their +Vitest type check, pass locally. This change does not alter the model or +production behavior. + +CodeRabbit then reviewed `00fcf0f` and posted two actionable documentation +comments. Both were correct. The earlier issue #2071 review record used present +tense for the source-batch grammar's former delete/reinsert limit; its affected +statements now name the historical commit and point to the ordered-batch witness +added in this PR. The generated `VirtualRowProps` and `VirtualOrigin` reference +pages linked to old source line numbers; all seven declaration links now match +`virtual-props.ts`. These follow-ups do not change the eight-item accounting +above. All reported CI checks passed on `e659d413e0f52fa0f9d800fb1d5a41ca784199c3`. + +CodeRabbit's next review, [5459093045](https://github.com/TanStack/db/pull/2078#pullrequestreview-5459093045), +checked that head and found an oracle harness defect. Its three findings across +both reviews are accounted for separately from the user's eight findings: + +| ID | Claim | Verdict and evidence | PR action and durable value | +| --- | --- | --- | --- | +| CR1 | The earlier #2071 review record still claimed that delete then reinsert was outside the source-batch grammar. | Correct on `00fcf0f`; the ordered grammar and coverage-map witness existed by that commit. | **fixed-now** in `e659d413e`; historical wording now identifies the earlier limit and later witness. | +| CR2 | Six `VirtualRowProps` anchors and one `VirtualOrigin` anchor pointed into source comments. | Correct on `00fcf0f`; all seven current anchors match the declarations at lines 74, 85, 104, 133, 141, 149, and 36. | **fixed-now** in `e659d413e`; generated reference links were corrected. | +| CR3 | A rejected pending manual transaction never starts its handler, so its `isPersisted` promise and oracle cleanup can remain pending. Commit the transaction after rejecting its handler promise. | Correct on `e659d413e`. Two source-row histories and one later-edit history failed at the settlement row assertion after the test first made cleanup safe. The proposed single-line fix alone leaves a later-edit history pending because the shared `starting` variable points to the later request. | **fixed-now** in this follow-up: commit the rejected manual request, bind each manual handler to its own deferred result, and release any still-active transaction during cleanup. The primary oracle and coverage map retain the law and limits. | + +The reviewer's three findings were accurate and focused. The proposed fix for +CR3 identified the missing commit but did not account for a later edit replacing +the shared deferred promise. The reviews distinguished documentation defects +from a test-integrity defect and had little noise. Adjacent interleavings +still need checking before accepting the proposed patch verbatim. + +### CR3 law, reach, and calibration + +A pending manual optimistic transaction starts its mutation function only when +committed. Handler rejection must roll it back, reject `isPersisted` with the +same reason, and expose the applied source row after dropping any optimistic +overlay. This follows from the optimistic transaction and settlement entries in +the glossary and from `Transaction.commit()`/`rollback()`. The production path +is `createTransaction({ autoCommit: false })` → `mutate()` → `commit()` → the +controlled handler promise → `isPersisted`. The model already represented +pending, persisting, and failed transactions. Its grammar could select a +rejection but the pending fixed witnesses selected only success or direct +rollback. The driver did not commit on rejection, and cleanup tried rollback +only when the expected result was still pending. + +The expanded fixed matrix crosses optimistic visibility with success, direct +rollback, and handler rejection after a same-key source insert. A second pair +starts a later disjoint edit before the manual request succeeds or rejects. +After each step, the driver checks public rows, origin and pending-write +metadata, subscriber replicas, downstream rows, request outcome and reason, +and publication cuts. At settlement it also checks the rows captured when +`isPersisted` settles. The source-row and later-edit rejection cases failed at +that settlement assertion on the reviewed harness. They were assertion +failures, not Vitest timeouts; cleanup preserved the primary mismatch and +released the pending transaction. With the commit fix in place, temporarily +restoring the shared handler made both later-edit cases fail at the same +assertion. Binding the manual handler to its own deferred result makes all +eight targeted cases pass. This calibrates both the original omission and the +plausible one-line repair that misses an adjacent legal history. + +This establishes the bounded law for the named manual histories through the +core Collection path and public settlement, row, and publication checkpoints. +It does not prove longer manual interleavings, same-key cascades, or a persisted +adapter path. The coverage map keeps those limits under the optimistic history +owner. CR3 changes the oracle driver and its fixed histories, not the product +implementation or model state. + +| Guide requirement | CR3 evidence | +| --- | --- | +| ORC-001/003 | The oracle's opening states the manual settlement law and limits; the model, grammar, driver, and per-step comparison remain distinct. | +| ORC-002 | The reference transition remains independent of transaction implementation; no expected state was copied from the new driver. | +| ORC-004/007 | The new cases are bounded fixed histories; generated grammar and fixed/random campaigns did not change. | +| ORC-005/006 | Real core transactions reach `isPersisted` and public rows. Original and shared-handler variants fail at the intended settlement assertion; the repaired driver passes. | +| ORC-008/009 | Model state did not change. `pending`, `persisting`, `failed`, and settlement retain glossary meanings. | +| ORC-010 | Cleanup rolls back a still-active transaction regardless of expected outcome; the original assertion remains the aggregate cause, with secondary diagnostics retained. The deferred rejection has a handler before a manual commit starts. | +| ORC-011 | No plausible shared semantic fault between production and model was identified; this review concerned the production driver's ability to reach the modeled outcome. | +| ORC-012/013 | This record binds the reviewed head, RED/GREEN cuts, hostile variant, neighboring success/rollback cases, and remaining limits. | +| ORC-014 | The claim is limited to the core Collection path and makes no real-provider handoff claim. | + +Final CodeRabbit accounting: **3 raw findings = 3 fixed-now**. There are no +deferred or design-decision items. The original user's eight-item audit remains +**8 raw = 8 fixed-now**; the unknown private #2071 bridge sequence remains an +evidence limit, not a CodeRabbit item. + +## Subsequent nine-finding review of `e659d413e` + +The external medium-effort review examined `e659d413e0f52fa0f9d800fb1d5a41ca784199c3` +by reading code; it reported no executed checks. Evaluation began on +`ee21b1797eb7cfc9dfa18a05637477a10de67828`, which had already repaired +the manual-transaction defects in M04 and M05. The nine numbered findings are +all the review's claims; its closing remarks add context, not a tenth item. + +| ID | Review claim and proposed repair | Technical verdict and evidence | PR disposition and durable value | +| --- | --- | --- | --- | +| M01 | A truncate on key 1 clears active attribution for untouched key 2; a later key 2 source transaction in the same drain becomes remote. | **Correct, high severity.** A real subscriber committed a truncate suffix and later key 2 write while an edit on key 2 remained active. The public row was remote where the independent model required local. Both publication and settlement variants failed on the reviewed production logic. | **fixed-now.** Preserve active keys across truncate; the primary optimistic-history oracle owns the origin law. | +| M02 | Clearing `pendingLocalChanges` on truncate caused M01; keep it and remove the temporary origin snapshots. | **Correct.** The snapshot copies covered only the truncate transaction. Removing the clear and snapshots preserves untouched active keys while existing same-key controls still consume attribution. | **fixed-now.** The production change removes the per-transaction snapshots and clears only row origins, completed attribution, and the virtual-props cache at truncate. | +| M03 | The reentrant grammar never wrote a later different key with an active edit. | **Correct coverage gap.** The former fixed histories wrote key 1 only. The new key 2 history failed on the original production logic at the promised public cuts. | **fixed-now.** Two optimistic-visibility variants join the primary publication owner; the coverage map names the bounded shape. | +| M04 | A pending manual transaction's shared handler could take a later edit's deferred result. | **Correct at the reviewed head; already fixed** in `ee21b1797`. The prior review record documents a later-edit RED settlement assertion and a hostile shared-handler variant; the current driver binds its own deferred. | **already-fixed.** Preserve the later-edit witness and settlement law in the optimistic-history owner. | +| M05 | Rejection of a pending manual request never commits it, so the model fails it while production stays pending. | **Correct at the reviewed head; already fixed** in `ee21b1797`. The prior RED assertion and current guarded rejection, commit, and cleanup are recorded above. | **already-fixed.** Preserve success, direct rollback, and rejection cases; no second patch to the handler was needed. | +| M06 | The no-cascade model comment is false for a failed same-key edit and pending manual peer. | **Correct.** Explicit cascading rollback and handler rejection made production reject the peer while the old model kept its overlay. The mismatch reached settlement, not a timeout. | **fixed-now.** The model cancels pending same-key peers; six cases distinguish same/different keys, cascading/secondary rollback, and handler rejection. | +| M07 | The real SQLite test rejects an outbound promise that no handler observes if setup fails before insert. | **Correct.** A temporary pre-insert failure produced both the intended error and a Vitest unhandled rejection. The same injection after the guard preserved only the primary failure. | **fixed-now.** Reject outbound only once a transaction exists; this protects test failure fidelity. | +| M08 | The model keeps active keys through truncate while production clears them, without explaining the difference. | **Correct explanatory and implementation gap.** The model's active-key rule was the independent expected behavior that exposed M01; its comment did not distinguish active from completed attribution. | **fixed-now.** Opening and local model prose now explain why untouched active keys survive and completed one-use attribution ends. | +| M09 | The origin contract is repeated across source, glossary, guide, and generated reference pages and had drifted. | **Correct.** The same detailed paragraph appeared six times. The generated reference copy also stated the broken drain rule. | **fixed-now.** `VirtualOrigin` source prose is the detailed contract; the interface, glossary, and guide summarize and link to its generated reference entry. Five affected reference pages carry the regenerated text and corrected source anchors. | + +No original severity labels were supplied. M01 is the release-relevant product +regression; M03, M04, M05, M06, and M08 concern oracle reach or judgment; M07 +concerns test failure fidelity; M09 concerns contract drift. The reviewer was +technically accurate on all nine items at the reviewed commit and found the +interaction between the prior fix and active-key ownership. The proposed M02 +repair was both correct and smaller than the snapshot approach. The review +did not report execution, so its failure claims needed the probes above. + +### Laws, reachable histories, and enforcement + +**L6: Truncate preserves active attribution for untouched keys (M01–M03, +M08–M09).** The public `VirtualOrigin` contract and the existing key-and-timing +model require a source write on an active local mutation's key to be local +until a source operation consumes that key. A truncate ends completed one-use +attribution, but an active mutation on a key the truncate omits is still active. +The old production clear contradicted this law. The old oracle lacked the +different-key suffix, so it never distinguished the clear from the model. +The independent model retains active keys until a source operation touches +them and clears completed grants at truncate. The legal fixed history has one +active edit on key 2, a subscriber-triggering truncate, a second truncate +writing key 1, then a non-truncate key 2 source transaction in the same drain. +Both optimistic and nonoptimistic edit visibility are tested. The existing +later same-key suffix controls the opposite boundary: a key written by the +truncate loses attribution for a later transaction. The real Collection sync +driver compares public rows and origin, both subscriber replicas, downstream +rows, outcomes, and complete publication cuts after each step. On the old +implementation, the nonoptimistic case failed at source publication and the +optimistic case failed when the failed edit's `isPersisted` settled: actual +`remote`, expected `local`. Those are assertion failures at the intended +cuts. The repaired code passes both and the same-key controls. This closes the +two-key, one-active-edit, two-suffix-batch core boundary tested here; it does +not establish every longer reentrant drain or provider bridge sequence. + +**L7: A cascading failure cancels a pending same-key peer (M06).** Public +transaction settlement and the established same-key conflict rule require a +pending manual peer to reject when a failing edit cascades rollback to it. +A different-key peer, an already persisting peer, or a secondary rollback +does not acquire that outcome from this failure. The old model comment and +transition wrongly omitted the pending-peer cancellation, allowing false +failures against correct production behavior. The model now uses its own +one-Collection key equality and pending state to derive the canceled peers; +it does not import the production conflict helper. The six fixed histories +cross same/different keys with direct cascading rollback, secondary rollback, +and handler rejection. They run real `createTransaction({ autoCommit: false })`, +`mutate`, `commit` or `rollback`, and `isPersisted`. Each step compares +visible rows, origin, event replicas, downstream rows, request outcomes, and +publication cuts. The old no-cascade model failed at settlement in the +same-key cases; the corrected model passes all six, while the different-key +and secondary controls distinguish overbroad cancellation. This closes the +two-request pending-peer boundary tested here, not arbitrary conflict graphs. + +**L8: A pending manual request settles by its own handler (M04–M05).** The +contract, grammar, core path, settlement observations, original RED results, +shared-handler hostile control, and remaining longer-history limit are +documented under CR3 above. This review rechecked that the current driver +still uses that path and that its focused suite remains green. M04 and M05 +were true of `e659d413e` but required no new repair after `ee21b1797`. + +M07 needed a controlled failure injection, not a product model: the test +cleanup must not create an unhandled rejection that masks its primary setup +failure. M09 needed direct source comparison, TypeDoc output, and working +relative links, not a state oracle. The five generated reference pages contain +one detailed contract in `VirtualOrigin` and summaries elsewhere; their +source anchors match the current declarations. + +### Oracle-guide audit and verification + +| Requirement | Outcome for this review | +| --- | --- | +| ORC-001/003 | L6–L8 state authority, finite limits, model rules, grammar, production path, and comparison cuts beside the executable owner. The canonical origin prose and coverage map were updated with the executable law. | +| ORC-002/008/009 | The model retains active keys independently of production tracking and distinguishes pending from persisting. Cancellation uses model key/state, not the production helper. The model-only row action and public terms remain mapped in the opening. | +| ORC-004/013 | The new key 2 suffix reconstructs the reported trace; same-key later-write and both visibility modes distinguish wrong boundaries. The six peer histories ablate key equality and cascade mode. The existing grammar rejects a reentrant suffix without truncate. Domains and longer-history limits are explicit above and in the coverage map. | +| ORC-005/006 | Original truncate logic and old no-cascade model failed assertions at named public cuts. The earlier manual-handler omissions and shared-handler mutant failed at settlement. No timeout, setup error, or mere path reach is counted as a kill. | +| ORC-007 | The additions are bounded fixed histories, not a new important generated property. Existing generated campaigns were not changed. | +| ORC-010 | The real SQLite pre-insert injection separated its primary error from the unhandled rejection; the guard removed the latter. Existing cleanup aggregation still preserves primary history failures. | +| ORC-011 | No new shared semantic fault between production and the independent model was identified, so a second formulation is not triggered. Same-key and different-key controls test the boundary; no new real-provider equivalence is claimed. | +| ORC-012/014 | This record binds the reviewed head and RED/GREEN evidence. The core reentrant claim stays at the core Collection boundary; the private bridge still needs its callback/write sequence and a persisted receiver. | + +After the repair, 241 core tests passed across optimistic publication, +outcomes, state retention, and truncate readiness, with no test type errors. +The SQLite real-adapter and persisted oracle run passed 869 tests with two +existing todo tests and no test type errors. The doc generator emitted the five +relevant reference pages, but also produced unrelated changes because several +package dependencies were unavailable; those unrelated changes were not part +of this repair. The focused source and link checks found no stale repeated +origin paragraph or invalid `VirtualOrigin` relative link. + +Final loss audit for this review: **9 raw items = 7 fixed-now + 2 +already-fixed**. There are no refutations, accepted-design items, design +decisions, or agreed deferrals. The reviewed claims are accounted for and +the authorized bounded repairs are complete. No universal claim is made for +longer reentrant suffixes, larger rollback conflict graphs, other SQLite +hosts, or the unavailable private #2071 bridge sequence; their executable +owners and needed witnesses remain in the coverage map. + +### CodeRabbit review of `ee21b1797` + +[Review 5460095613](https://github.com/TanStack/db/pull/2078#pullrequestreview-5460095613) +contained one outside-diff behavior suggestion and one inline documentation +suggestion. Both are accounted for here; its suggested agent prompts and local +tool invocation are review metadata, not additional findings. + +| ID | Claim and proposed action | Evidence and technical verdict | Disposition and durable value | +| --- | --- | --- | --- | +| CR4 | The model keeps `activeKeys` after an empty truncate while production clears its active-key tracking; clear `activeKeys` after taking the batch snapshot. | Production did clear active tracking on the reviewed `ee21b1797`, so the observation was accurate there. Its proposed model change contradicts the `VirtualOrigin` active-key law and would make the model accept M01's product regression. After the production repair at `a109687c1`, that change was run as a temporary model mutant. Both new key 2 cases failed at their intended public cuts: the nonoptimistic case at complete source publication and the optimistic case at `isPersisted` settlement. The mutant was removed. | **stale** as a production/model mismatch after M01's repair; **refuted** as a model repair. Keep the empty-truncate/different-key challenge under the optimistic-history owner. | +| CR5 | Remove a hiring recommendation from the engineering review record. | Correct maintainability point: the durable record needs code and test evidence; the user-facing review can carry the requested reviewer assessment. | **fixed-now.** Removed the personnel recommendation from this record while retaining the technical quality assessment and all law evidence. | + +CodeRabbit accounting for this review: **2 raw items = 1 stale with a refuted +repair + 1 fixed-now**. CR4's historical observation and useful adversarial +history remain recorded; no product law or test coverage was weakened to +accept the proposed model change. + +### Merge-readiness self-review after CI passed on `7dcebc75f` + +The public origin prose said that a later same-key transaction after a +truncate is remote without specifying the drain boundary. The existing +reentrant witness has a trigger and suffix truncate in separate drains. The +still-active mutation can attribute the suffix truncate again. A later +same-key transaction within the suffix drain is remote. The source comment, +generated reference, oracle opening, publication comment, and coverage map +now state that narrower rule. This is a contract wording correction, not a +production change. + +Two temporary legal-history probes also crossed a pending and persisting +same-key pair with a reentrant truncate, and two active keys with that drain. +Both passed the core public-history comparison and test type check. The probes +were removed. They support those two sampled schedules but do not close the +coverage map's longer overlap or provider-path limits. + +### CodeRabbit review of `c7d99a43f` + +[Review 5460769310](https://github.com/TanStack/db/pull/2078#pullrequestreview-5460769310) +contained one inline documentation finding. The review body also suggested +running the local CodeRabbit CLI after a fix. Both items are accounted for +below. + +| ID | Claim and proposed action | Evidence and technical verdict | Disposition and durable value | +| --- | --- | --- | --- | +| CR6 | The “after green CI” heading implied the current head had passed; change it to say CI was pending. | The preceding head `7dcebc75f` had passed its 26 checks, but checks for reviewed head `c7d99a43f` were still pending at review time. The unqualified heading was ambiguous. | **fixed-now.** The heading now names the head whose CI had passed, preserving the historical sequence without implying current-head success. | +| CR7 | Consider running `coderabbit review --agent` after the fix. | This was an optional workflow prompt, not a code or contract finding. The hosted review already examined `c7d99a43f`; the local CLI would add no required evidence for this wording change. | **refuted** as an additional PR requirement. Keep the hosted review and CI status as the review evidence. | + +CodeRabbit accounting for this review: **2 raw items = 1 fixed-now + 1 +refuted**. This documentation finding affects no product law or oracle path; +its evidence is the recorded commit order and CI status at review time. diff --git a/docs/contributing/oracle-reviews/pr-2103-separate-wasm.md b/docs/contributing/oracle-reviews/pr-2103-separate-wasm.md new file mode 100644 index 0000000000..9d215ca4ea --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-2103-separate-wasm.md @@ -0,0 +1,128 @@ +# PR #2103 separate WASM asset review + +- Base: `origin/main` at the PR's merge base. +- Production reviewed at `3a7d88102`. The commit that adds this record changes + only oracles, tests, and documentation, so production is the same. +- Oracles: + - The OPFS open outcome oracle, + `packages/browser-db-sqlite-persistence/tests/opfs-open-outcome-oracle.ts`. + Its jsdom driver is `tests/opfs-open-outcome-oracle.test.ts` and its + Chromium driver is `e2e/opfs-open-outcome-oracle.e2e.test.ts`. + - The install-layout oracle, + `packages/browser-db-sqlite-persistence/e2e/install-layout-oracle.e2e.test.ts`. +- Supporting checks: `e2e/wasm-package-consumer.e2e.test.ts` (build output, + export map, classic worker, `about:srcdoc`, cross-origin page), + `e2e/bundler-consumers.e2e.test.ts` (webpack 5 and Next.js consumers), and + `tests/opfs-database.test.ts`. + +## Contract and evidence + +The README promises how an open settles. A capability failure in the asset +lookup, worker start, or WASM load rejects with `PersistenceUnavailableError`. +The open deadline rejects a pending open with `TimeoutError`, and an abort +rejects it with the abort reason; both cover every step. A failed or +interrupted open terminates the worker it started, and a step that completes +after the open settled starts no worker. + +The README also states which installs open in Vite development. A pre-bundled +copy asks the dev server for its assets by package name from the app root. It +opens when the root resolves that name, loads the root's copy when another copy +is installed, and otherwise rejects with `PersistenceUnavailableError`. +Excluding the specifier that the importer uses serves the copy in place. + +Earlier rounds of this PR found these defects against that contract, each with +a failing witness before its repair: the deadline and abort did not cover the +lookup; a failed lookup, an `about:srcdoc` frame, and a refused Worker +constructor rejected with raw errors; a missing WASM file was reported as an +internal error; a cache directory named `dist` skipped the needed lookup; and +an excluded transitive install still ran the lookup. The two oracles in this +record replace the fixed witnesses that stated their own expected results. + +The install-layout model disagreed with the README in two places. Two +installed copies open with the root copy's assets instead of rejecting, and an +npm alias must be excluded by its alias name. The README was corrected; the +model was not changed to match production. + +## Calibration + +Each control was a temporary source mutation, rebuilt and reverted after the +run. "Assertion failure" means the refinement check failed with an observation +diff, not a timeout. + +| Control | Owner and driver | Classification | +| --- | --- | --- | +| Asset lookup not raced against the deadline and abort | Open outcome, Chromium | Assertion failure in both interrupted histories: settlement `pending` one task after the interruption (about 120 ms) | +| Same | Open outcome, jsdom | Unreached path: this driver has no lookup | +| Interrupted open starts a worker when the lookup completes late | Open outcome, Chromium | Assertion failure in both interrupted histories; the failing field was not inspected | +| Worker reports a WASM load failure as an internal error | Open outcome, Chromium | Assertion failure in the WASM-failure history | +| Same | Open outcome, jsdom | Unreached path: the driver supplies the worker's response | +| Refused Worker constructor's error escapes unclassified | Open outcome, Chromium and jsdom | Assertion failure in the refused-worker history, in both drivers | +| Rejects before making the asset lookup | Open outcome, Chromium | Assertion failure on `reached.lookup` only (inspected); settlement and worker counts matched. The first attempt was a setup failure: an unconditional `throw` did not compile, so the old build ran | +| Lookup runs even when the copy is served in place | Install layout | Assertion failure in exactly the excluded transitive, alias, and two-copy cases | +| Built package starts a module worker | Bundler consumers | Assertion failure in all six webpack and Next.js cases (`workerTypes`) | +| Lookup runs without a Vite client on the page | Bundler consumers | Assertion failure in the four Next.js cases; equivalent within the two webpack cases, because webpack emits the worker and WASM into one directory and the moved-code check skips the lookup | +| Production builds keep the dev-only lookup (the code before the production gate) | Bundler consumers, Vite consumer | Assertion failure in the webpack production and both Next.js production cases and in the Vite consumer's production build (`shipsViteLookup`) | +| Worker accepts an opener from another version (the code before the version check) | Install layout | Assertion failure in the three pre-bundled two-version cases: the open succeeded with the other copy's worker | +| Worker reports a refused OPFS storage root as internal (the code before the storage check) | Worker diagnostics | Assertion failure in the storage-root examples and both campaigns: `INTERNAL` with the bare browser message | +| Worker sends a `PersistenceUnavailableError` message with its prefix (the code before the fix) | Worker diagnostics | Assertion failure on the response text; the opener showed the prefix twice | + +The reject-before-lookup control shows why the observation records reach. +Without `reached`, that history would have passed. + +No control was run for the worker-script failure history added in this +round. Production passed it unchanged, and a control that ignores the Worker +`error` event would leave the open pending and fail by timeout. + +## Requirements + +| Requirement | Outcome | +| --- | --- | +| ORC-001 Contract authority and limits | Each oracle opens with its law, the README as authority, and its limits. | +| ORC-002 Independent judgment | The open model is a decision rule over the history. The layout model reads two facts from the installed tree, the importer's specifier and the Node resolution of the package name from the app root, and imports no production code. | +| ORC-003 Distinguishable responsibilities | The open oracle keeps law, grammar, model, and comparison in one module and each driver in its own file. The layout oracle keeps model, cases, installer, and driver in separate sections. | +| ORC-004 Grammar controls | Both grammars are bounded exhaustive products. Open histories: a held lookup or silent initialization must be interrupted, a history that fails early fixes later steps, and an interruption in the same turn as a failing step is excluded because no law orders the two. Layout cases: every layout with three cache directories while pre-bundled, every layout excluded by import name, an alias excluded by package name, and every layout in production. | +| ORC-005 Production path and observation | Chromium drives the public opener from a built package in a consumer app; jsdom drives it from source. `reached` is the positive execution witness for the lookup, worker start, and initialization. | +| ORC-006 Calibration | See the table above. | +| ORC-007 Random campaigns | Not applicable: no history is randomly generated. | +| ORC-008 Model minimality | Not applicable: both models are stateless functions of one history or case. | +| ORC-009 Vocabulary | The open model's "worker start" combines Worker construction and script load, and "deciding step" is a model term; the module prose declares both. | +| ORC-010 Failure fidelity and cleanup | No shrinking. Drivers release held lookups and close their browser context or page in `finally`, after the comparison input is recorded. | +| ORC-011 Second formulation | Not applicable: no reviewer named a fault that the model and production could share. | +| ORC-013 Reusable boundary law | Not applicable: neither oracle is cited as protecting a law outside this package. | +| ORC-014 Controlled premises | The jsdom WASM failure response is a controlled premise; the Chromium driver's real WASM 404 is its receiving witness. The Chromium refused-worker init script is controlled; the consumer check's cross-origin page supplies a real refusal. | +| ORC-015 Declared reach | Each driver checks that its product covers the outcomes it declares, and each history's observation compares production reach with the model. | +| ORC-016 Law-only oracle files | The oracle files hold no unit tests; fixed checks stay in the consumer test file. | + +## Limits + +A manual probe ran the packaged consumer under Vite development and preview in +Playwright's Firefox 150, which opened and queried SQLite, and WebKit 26.4, +which refuses the OPFS storage root even on a blank page. With the storage +check, WebKit's open rejects with `PersistenceUnavailableError`; before it, +the open reported an internal error. Real Safari 27.0 on macOS was checked by +hand against `9d0ee7234`: under Vite development, through the asset lookup, and +under a Vite production preview, the page opened SQLite, wrote and read a row, +and started one classic worker. + +Both oracles cover Vite only, in Chromium. The bundler consumer checks add +webpack 5 builds and Next.js with Turbopack and webpack, in development and +production, as fixed receiving witnesses. Firefox and real Safari were checked by hand, +not by a repository test. Nothing covers webpack-dev-server, Rspack, Parcel, +esbuild, an open from inside a worker, or a Vite client injected without a +script `src`. The README states the last two as +cases that need the exclusion. + +The Vite production builds in these tests run with `NODE_ENV=production`. +Vitest sets `NODE_ENV=test`, and a Vite build under it keeps development code, +so the Vite consumer check first reported a lookup that a real `vite build` +drops. + +While the bundler checks were written, webpack and Turbopack first resolved +the workspace packages to their sources through each package's tsconfig +paths. Published packages ship no tsconfig, so the checks install the built +workspace packages as published packages instead. + +Open questions that need a product decision: no law orders an interruption +against a step that fails in the same turn, and two installed copies of +different versions can pair one copy's opener with the other's worker without +a version check. diff --git a/docs/contributing/oracle-reviews/pr-2116-confirmed-fixes.md b/docs/contributing/oracle-reviews/pr-2116-confirmed-fixes.md new file mode 100644 index 0000000000..676213d656 --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-2116-confirmed-fixes.md @@ -0,0 +1,186 @@ +# PR #2116: confirmed fixes found during unit-test retirement + +The seven index, five non-finite comparison, two ordinary Temporal comparison, +and six Date/RegExp assertions retired by this PR have their own loss ledgers in +the [coverage map](../oracle-coverage.md#unit-test-retirement-transfers-in-pr-2116). +Those transfers did not authorize new product guarantees. Each additional fix +below has an accepted law, a failing observation before repair, an independent +receiving witness, and a bounded hostile challenge. `origin/main` was +`bbbf017ef5b3884515295a4225faacdfd814499e` for the baseline checks. + +| Law and authority | Baseline failure | Receiving observation and hostile result | Limit | +| --- | --- | --- | --- | +| A full BasicIndex build keeps `undefined` at the configured nulls-first position. The default comparator and surviving ordered-index tests supply the rule. | Native `Array.sort` moved a raw `undefined` value last without calling the comparator. | The source-Map direct-index owner compares complete ordered reads after full build. The [build review](basic-index-nullish-build.md) records RED/GREEN and hostile evidence. | Default nullish order and bounded build histories. | +| Duration identity preserves all ten public fields. Changelog #1519 explicitly keeps equivalent displayed forms unequal for query equality; the Collection subscriber-mirror contract requires a changed row to reach its subscriber. | One second and 1000 milliseconds display alike. The former `deepEquals` and normalized index key treated them as equal, suppressing a Collection change message or returning a wrong exact match. The original implementation failed direct BasicIndex/BTreeIndex lookup, public scan and indexed `eq`, and constant and row-supplied `in` probes. | The deep-equality and change-event oracles compare a direct result, stored row, callback batch, and subscriber mirror. The Duration query owner checks ten fields through BasicIndex/BTreeIndex exact lookup and public `eq`, while the `in` evaluator owner uses constant and row-supplied lists. A seconds-only production mutant fails the adjacent-unit deep-equality property. Executed wrong-key controls reject display-string, sign-loss, and first-nonzero-field query keys; they were not production mutants. | Fixed replacement and single-row query histories; no arbitrary multirow update or live-query publication claim. | +| An indexed ZonedDateTime `orderBy` follows instant order, as promised by changelog #1519. A time-only Duration uses its elapsed-time order while keeping exact field identity. | Indexed first-row reads placed a later ZonedDateTime first when display text opposed epoch order, including a 9ns/10ns digit-width pair. PT120M also preceded PT1H under string-key order. | The Temporal index-order oracle compares exact direct BasicIndex/BTreeIndex reads and bounded public first rows, including an on-demand Collection. Adjacent nanoseconds at a large epoch reject `Number` precision loss. PT1H/PT60M exclusive cursors reject an exact-suffix tie-break; negative durations reject absolute-value order. The hostile reviewer found no sign mismatch in 2,000 seeded valid day/time pairs. | ZonedDateTime instant and time-only Duration order in the named fixed histories. Calendar units needing `relativeTo`, mixed Temporal kinds, custom index callbacks, and arbitrary update histories remain open. | +| A manual index cannot change the declared ascending `orderBy(rank).limit(1)` answer. Query order is public; index selection is physical planning. | A reverse manual BTree index made `currentStateAsChanges` return rank 30 instead of rank 10. | The custom-index selection oracle independently sorts two distinct numeric ranks and checks the direct public snapshot and an eager live-query result. The hostile reviewer found the direct path that the first witness missed; both paths now pass. An added compiler guard had no demonstrated effect on this witness and was removed. | Eager initial reads with distinct ranks; no callback-tie, on-demand, later-update, or work-bound claim. | +| Index updates conserve each source key even when number and string values occupy different comparison domains. | The old comparator could make `2` tie `"2"` while putting them on opposite sides of `"10"`; a direct deletion then duplicated or lost a key. | A source Map predicts full direct traversals after bounded delete/update/re-add actions, and a bounded indexed Collection read checks the public key set. The hostile reviewer confirmed the original comparator fails the source-Map comparison. | Key conservation, not a mixed-kind public order policy or all Collection histories. | +| An ordinary string beginning `\0tanstack-db:` obeys its declared string collation in indexed and scan reads. Internal Temporal order keys must not cause a tree to lose a neighboring ordinary string. | On `origin/main`, an en-US locale scan selected `\0tanstack-db:a` first while BasicIndex/BTreeIndex selected `tanstack-db:r`. Before the final kind classifier, deleting a neighboring ZonedDateTime or Duration also made BTreeIndex omit the surviving string. | The direct-index owner compares first public rows through scan and indexed paths under locale and custom string collations, with row IDs independent of label order. Two three-key BTree histories compare all surviving keys. The hostile reviewer ran 24 focused tests, 108 add/delete permutations, and a 27-value comparator transitivity sweep across five option variants. After the final classifier simplification, it also ran 3,456 four-key BasicIndex/BTreeIndex insertion/deletion histories (27,648 checkpoints) and a 16-value comparator sweep; no in-domain counterexample remained. | Named scalar, binary, Temporal, and string-kind histories. No arbitrary nested values, mixed-kind relative order, or arbitrary custom index `compareFn` guarantee. A separate two-value direct callback lane checks legacy scalar input kinds for immutable Date, Temporal.Instant, and Uint8Array values. | + +## Design decisions and open work + +The removed `subset-dedupe.test.ts` case that overrode `Date.getTime` and +`Uint8Array.slice` was omitted from the earlier retirement ledger. Its frozen +assertion checked that request transport kept the same `where` object and that +the evaluator produced the same answers before and after transport. A probe on +this PR still passes that case. The maintainer excluded guarantees after callers +modify standard data objects, so the overridden-method case has no receiving +oracle law. Immutable Date, byte-array, and Buffer request identity and matching +remain checked in `subset-dedupe.test.ts`; `immutable-demand-boundary.test.ts` +also checks the direct and deferred request handoff. This records the excluded +assertion explicitly rather than treating its deletion as a transfer. +The rewritten `binary-equality-work.test.ts` also stopped editing a compiled +byte literal in place. The evaluator still passes that old trace on this PR, +but in-place edits to standard values are outside the accepted contract. Its +fresh-value checks remain the supported equality and work witnesses. + +The captured-`inArray` list rebuild and per-row custom index callback ordering +were proposed extensions, then withdrawn by the maintainer. This PR does not +promise behavior after callers modify standard data objects in place, unsigned +byte ordering, or special handling for malformed Temporal lookalikes. The +[earlier-findings record](pr-2116-deferred-findings.md) keeps Temporal admission +observations, mixed-kind query order, the confirmed pre-existing indexed +Temporal exception gap, and earlier-branch observations separate from the +confirmed fixes above. + +The repeatable workflow is: freeze source assertions in a loss ledger; name +the receiving law and its limits; obtain a baseline failure before production +changes; extend an independent model at the public checkpoint; ask a fresh +hostile reviewer to attack authority, reach, and plausible mutants; then +recheck the exact committed head. A production-code budget is evaluated +separately from oracle and documentation growth. A green generated campaign +supports only its legal histories, paths, and observations. + +## Production-code weight + +Against the recorded `origin/main` baseline, the source diff under +`packages/db/src` adds 356 lines and removes 535, for **179 fewer production +lines**. The built DB ESM entry bundled with esbuild (`bundle`, `minify`, +`format: esm`, `platform: neutral`, `target: es2020`, +`NODE_ENV=production`; DB IVM, pacer-lite, and standard-schema external) is +335,613 bytes / 100,492 gzip at baseline and 336,504 bytes / 100,889 gzip at +executable head `905b93323`. That is **891 more minified bytes / 397 more gzip +bytes**. Both outputs use Node's `zlib.gzipSync`. The source-line budget is +negative; the consumer-bundle budget remains positive. + +The later build-cache commit `ad6f22105` removes the obsolete `compareStored` +minifier entry and registers 23 private members that the cache check recognizes +as safe. Production TypeScript is unchanged from the reviewed executable head. +After a fresh build, the same consumer-bundle measurement is 335,182 bytes / +100,737 gzip: **431 fewer minified bytes / 245 more gzip bytes** than baseline. +The local cache check and minified public API check pass. This narrows, but does +not close, the consumer-bundle gap. The repository's separate compressed-size +check reports **395 fewer bytes** across its published ESM package files at +`ad6f22105`; that unbundled package measure is net negative. + +## Exact-head hostile review of the index rewrite + +The executable revision is `905b93323` (following `2f852dcd1`); the baseline +is `origin/main` `bbbf017ef5b3884515295a4225faacdfd814499e`. The rewrite +replaces BTree's per-exact ordered entries with comparator groups while keeping +exact membership separate. Its binary ownership history compares a source Map +with complete forward and reverse index reads. An intermediate PR rewrite lost +a surviving byte owner after a fresh equal-content removal. The published +parent `78bd4946b` passes the six final binary cases, so that particular RED +is **not** evidence of a pre-existing defect. + +The hostile reviewer ran two wrong-output controls against the rewritten +BTree. One suppressed a surviving short-byte key after equal-content removal; +the oracle failed at the ordered-read length comparison. A second suppressed +one key after inserting two 8193-byte values that differ only in their last +byte. That mutant survived the first version of the binary lane because the +wide-byte case checked exact lookup only. The final oracle adds full forward +and reverse key conservation there; the same mutant then failed at that +comparison. The unmodified six cases passed. These controls are output +mutants, not claims that `origin/main` failed. + +The final BasicIndex retains the original stable byte value in its sorted +array and reuses the normalization cache populated at admission. A direct +offset-view and Buffer probe preserved exact membership and ordered keys after +fresh equal-content removal. Callers who edit the retained byte array in +place have no promise. This removes a redundant byte copy without changing +the accepted immutable-value law. Duplicate-heavy insert and removal timings +were near parity in the bounded A/B probe. An attempted merge of Array and +ordinary-object index kinds was discarded: it produced a strict three-value +comparator cycle, so the separate kind remains. + +The fresh review also reconstructed a public scan/index mismatch for mixed +`PlainDate`/`PlainDateTime` and for two `PlainMonthDay` values. The scan throws +as changelog #1519 requires, while either index returns rows. It reproduces +on clean `origin/main` and this head. The [deferred record](pr-2116-deferred-findings.md) +assigns the exception witness and ordered-query repair to the Temporal owner; +the bounded ZonedDateTime and time-only Duration claims above do not include +this case. + +| Guide requirement | Exact-head result | +| --- | --- | +| ORC-001 contract and limits | Transfer ledgers trace the retired assertions; changelog #1519 and the surviving index interface give the separate Temporal and key-conservation laws. The coverage map names excluded mixed-kind order, mutation of standard values, and wider histories. | +| ORC-002 independent judgment | Source Maps, supplied Duration fields, and supplied Temporal instants predict results without importing index grouping, normalization, or comparator classifications. | +| ORC-003 literate responsibilities | The owners state each law before mechanics and explain the model, legal actions, direct or public production path, observation, and checkpoint beside the code. | +| ORC-004 generated grammar | The transfer ledgers account for reconstruction, ablation, bounded ranges, and excluded histories of the existing generated index lanes. The new binary lane is a fixed history, so the generated-grammar trigger does not apply to that lane. | +| ORC-005 path and observation | Complete returned arrays expose missing or duplicate keys in both directions; exact lookups expose byte-content errors. Collection reads and index spies establish the named public routes. No earlier-publication information claim is made. | +| ORC-006 checker calibration | Short-byte and 8193-byte wrong-output mutants failed at ordered-read assertions; the wide mutant first survived, motivating the final assertion. Existing transfer ledgers retain the other hostile controls. | +| ORC-007 fixed and random replay | The important generated index properties retain their documented fixed and seedless campaigns and direct seed/path replay. The fixed binary examples do not trigger this requirement. | +| ORC-008 model state | The source Map retains key ownership because a later delete or replacement can distinguish live rows; the binary fixed history adds no production-shaped index state. | +| ORC-009 vocabulary | Model puts, deletes, source keys, and ordered reads map to direct index actions and public observations. Comparator groups remain production storage, not reference-model state. | +| ORC-010 failure and cleanup | Existing generated drivers retain their replay context and failure cause; public Collection witnesses clean up in `finally`. The fixed direct binary cases acquire no external resource. | +| ORC-011 second formulation | No plausible fault shared by the source Map and index ownership model was identified inside the bounded transfer. The separately confirmed mixed-Temporal public error is recorded as an unresolved neighboring law, rather than inferred from direct index equality. | +| ORC-012 review record | This versioned record identifies the executable head, baseline, mutant outcomes, tests, size, and unresolved public path. | +| ORC-013 boundary witness | Nullish boundary controls, opposing 9ns/10ns display and instant order, adjacent large-epoch nanoseconds, and Duration display ties reject nearby wrong rules at their named checkpoints. | +| ORC-014 controlled premise | The controlled Collection fixtures support only their named Collection observations; no real-provider behavior is claimed, so the cross-provider handoff trigger does not apply. | + +The DB source suite passed 13,192 tests in 282 files before the no-copy +follow-up. After that follow-up, the affected index and comparison files passed +623 tests, the built-package lane passed 291 tests, the minified DB check +passed, and the DB source TypeScript and affected-file lint checks passed. +Those runs cover the changed immutable-value paths; they do not establish +arbitrary later Collection histories or the deferred mixed-Temporal error. + +## 2026-10-13 high-effort review of PR head `16b219224` + +This addendum supersedes the earlier manual-index row's two-rank limit. The +earlier observations remain historical evidence. The follow-up tree has a +three-rank/two-row owner and one ordered-index eligibility check shared with +the mixed-kind repair. The reviewer withdrew the claimed wrong live-query +result after finding that its scratch test resolved `@tanstack/db-ivm` from a +different checkout. With the correct package, both the frozen PR head and +`origin/main` published the expected prefix before and after the reported +writes. + +| Review IDs | Law, experiment, and action | Remaining limit | +| --- | --- | --- | +| R01, R10 | Index choice cannot change an ordered public prefix. The new scan-versus-index owner rejected the frozen type-name order in eight initial ascending cases. Its repaired 20 ascending and descending BasicIndex/BTreeIndex cases include paired source updates and a ReverseIndex route. Forcing the index-eligibility getter true rejects all ten original ascending histories. A retained on-demand BTree history starts with numbers, inserts and moves a bigint, deletes a prefix row, then requests a later window. It asserts each settled public prefix against independently sorted provider rows. The finite request supplies a missing row; the later request uses a cursor after the retained index has become ineligible for new ordered plans. Withholding either requested row fails the corresponding public assertion. A single physical and query comparator is unsafe here: the query comparator does not form a transitive total order for all mixed JavaScript values. | The eager histories and this retained on-demand paging history pass. The withheld-row controls challenge the provider result, not production code. Other provider paging sequences and repeated domain changes remain untested; the retained loader still traverses the index after eligibility changes. | +| R03 | The reviewer withdrew the `[20,30]` public-result claim after fixing the scratch test's cross-checkout `@tanstack/db-ivm` resolution. The corrected three-row eager fixture returns `[10,20]` on both the frozen PR and `origin/main`; the reviewer also checked the insert and delete checkpoints. Our widened owner independently sorts three ranks and compares eager and prepopulated on-demand public rows after preload. The compiler's ordered-index eligibility check stays because mixed-kind finding R01 needs it; the R03-only planner assertion was removed. | No confirmed live-query correctness defect remains for this fixture. Incomplete-provider paging and later updates are outside this owner. | +| R04 | Direct index callbacks retain the legacy scalar input kinds for immutable Date, Temporal.Instant, and Uint8Array values. Five of six new callback cells failed on frozen head; callback-only normalization makes all six pass, with raw-object and collapsed-position hostile controls rejected. | Two-value direct-add histories only; arbitrary callbacks and later updates are not claimed. | +| R06, R07 | BasicIndex now keeps admitted exact keys beside ordered representatives; a 64-row Temporal range and ordered read made zero `toString()` calls, down from 128. BTreeIndex keeps a comparator-tie bucket map after one exact bucket leaves; a return to the tie no longer copies every peer. | BasicIndex may retain one former representative per live exact group. No general heap limit is claimed beyond existing retained-index state. | +| R08, R09 | Object comparison does allocate temporary reference-identity tuples, but a bounded timing probe found no object throughput regression; no extra hot-path code was added without a demonstrated cost. Auto-index now shares `indexableOperations`, and `DurationFields` is defined once. | An allocation profile under a representative workload would be needed to establish a material R08 performance defect. | +| R02, R05, R11 | In-place byte edits can stale the normalized-key cache, and the overridden-method request test was omitted from the earlier retirement record. Both concern caller modification of standard data objects, which the maintainer excluded; fresh immutable matching and request identity remain covered. The reviewer's random index run supplied no seed, grammar, or count. | The cache holds one key per live byte array. The random run is stress evidence, not a witness for mixed public order or callback inputs. | + +An adjacent previously recorded defect also changed under the mixed-domain +guard. On `origin/main`, ready BasicIndex and BTreeIndex direct ordered reads +returned a row for PlainDate beside PlainDateTime, or for two PlainMonthDays, +while the scan threw the `TypeError` promised by changelog #1519. The expanded +Temporal index-order owner now checks six scan/index cases at direct-read and +eager live-query checkpoints. All 68 tests in that file pass on this worktree. +The eager live-query paths already threw on `origin/main`; the repair evidence +is the indexed direct-read mismatch. On-demand and later-update exception +histories are not claimed. + +The same rewrite repairs a separate PlainDate boundary under lexical string +collation. The supplied year fields require `-100000-01-01` before +`+010000-01-01`, while their display strings have the reverse lexical order. +The expanded Temporal owner rejects the two `origin/main` indexed direct-read +results, which selected the positive year; scan and eager live-query preload +already selected the negative year. Current BasicIndex and BTreeIndex direct +reads pass. This establishes the fixed ready-source history, not every +extended-year pair or on-demand publication. + +The follow-up tree adds 406 and removes 541 production source lines against +`origin/main`, for **135 fewer lines**. A paired local minified build of the 105 +DB JavaScript package files totals 205,973 gzip bytes, versus 206,122 for the +same files on `origin/main`: **149 fewer gzip bytes**. Bundling each built DB +ESM entry into one consumer file with esbuild instead yields 100,746 versus +100,544 gzip bytes: **202 more gzip bytes**. Separate-file and whole-bundle +compression have different dictionaries and tree-shaking boundaries. React DB +has no source change in this follow-up; the CI size action has not yet run on +the final pushed commit. diff --git a/docs/contributing/oracle-reviews/pr-2116-deferred-findings.md b/docs/contributing/oracle-reviews/pr-2116-deferred-findings.md new file mode 100644 index 0000000000..ebb483c161 --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-2116-deferred-findings.md @@ -0,0 +1,46 @@ +# PR #2116: earlier findings and guarantee decisions + +This record separates findings discovered during the unit-test retirement +workflow from the bounded fixes retained in PR #2116. The transfer ledgers +remain the authority for retired assertions. The +[confirmed-fixes review](pr-2116-confirmed-fixes.md) records the baseline and +hostile evidence for fixes that this PR now keeps. A finding below is not +closed by passing a neighboring transfer oracle. + +| Finding | Authority and evidence | Next owner and witness | +| --- | --- | --- | +| R29: two valid `Temporal.Duration` values can have the same display text but different fields. A changed field can be suppressed as an equal Collection value. | Closed in this PR for the tested direct and Collection paths. The Duration field model checks all ten fields; a same-display replacement and adjacent-unit histories make the former `deepEquals` and change-event behavior fail before the repair. The query equality owner and `in` evaluator owner separately check Duration field identity. | General multirow and live-query publication histories remain outside these owners. Add them to the relevant owner if a new failure reaches those paths. | +| R16: an indexed Collection can admit a valid Temporal row whose index comparator has no public order, then throw after partial state changes. | Earlier PR revisions recorded RED admission and publication traces for Duration, PlainMonthDay, and mixed-kind rows. A paired probe on `origin/main` `bbbf017ef5` and PR head `0c10ae8e8` found no failure: 190 valid Temporal pairs per index type and revision passed direct insertion; 24 two-row sync and optimistic Collection histories retained both public rows, exact memberships, ordered storage, ready status, and both insert publications at the second write return. The earlier RED traces establish an intermediate-branch failure, not a current-base or current-head defect. Public query comparison errors remain separate from internal index storage order. | No admission repair follows from this evidence. If a valid current-head admission history fails, extend the direct-index and Collection publication owners with exact membership, ordered storage, stored rows, and subscriber observations before changing production. The probe does not cover all valid Temporal values, native-host implementations, updates, builds, or longer histories. | +| Mixed-kind indexed order and custom-index query selection may disagree with public query results. | The source-Map owner closes the mixed number/string direct-index key loss. The new scan-versus-index owner rejected the frozen PR's wrong mixed-kind public prefixes, and the current guard makes the tested eager paths use the query's order. A retained on-demand BTree history checks visible prefixes after a number/bigint transition, a missing-row finite request, and a later cursor request that still traverses the index; all settled public windows match independently sorted provider rows. A reverse manual index produced a wrong direct prefix on an earlier branch revision; the current three-rank owner checks direct, eager, and prepopulated on-demand public results. The reviewer withdrew its claimed live-query failure after fixing a cross-checkout `@tanstack/db-ivm` resolution error. The maintainer withdrew a per-row custom callback position guarantee for comparator-equal exact values. | Other retained on-demand paging sequences, different incomplete-source patterns, and later live updates need public-result witnesses. The owners do not define a universal precedence among unlike kinds. | +| Indexed ordering can suppress a required Temporal comparison error. | Changelog #1519 requires `TypeError` for mixed or unordered Temporal values. On clean `origin/main`, indexed direct reads returned rows for `PlainDate` beside `PlainDateTime` and for two distinct `PlainMonthDay` values while the scan threw. The new Temporal exception lane fails on those four baseline indexed reads and passes on this branch; eager live queries already threw on main. | The bounded direct-read repair is complete for these pairs. On-demand acquisition, later updates, and other mixed Temporal pairs need separate witnesses before claiming the whole exception class. | +| R17: an equal-time Date removal left an ordered ghost under added index machinery. | The recorded RED trace depended on code introduced during this PR. It does not establish an `origin/main` defect. The stronger rule for a substituted old row with a different callback position was withdrawn. | If ordinary direct remove/update still has a current-base counterexample, add a source-Map history to the direct-index owner before changing production. | +| PlainDate extended-year order under lexical string collation can diverge between scans and indexes. | On `origin/main`, a ready scan returns `-100000-01-01` first while BasicIndex and BTreeIndex direct public reads return `+010000-01-01`. The new Temporal owner checks calendar order from the supplied years and fails on those two baseline direct reads. Both direct indexed reads pass on this branch. Baseline eager live queries already returned the right first row. | The fixed ready-source direct-read history is closed. Other extended-year pairs, later updates, and on-demand publication remain outside the current witness. | +| Cross-realm native equality was asserted by a surviving request-transport unit test. | PR #1797 captured the then-current `instanceof` result. No public realm-distinctness promise was found. The maintainer questioned that distinction. This PR removes that incidental test expectation and makes no cross-realm equality claim. | A future public contract decision needs a host-boundary witness before either equality policy becomes an oracle law. | + +The 2026-10-13 review expanded the custom-index owner to three ranks and a +two-row window. The reported `[20,30]` public result came from a scratch test +that resolved `@tanstack/db-ivm` from another checkout; with the correct +package, both frozen PR head `16b219224` and `origin/main` returned `[10,20]` +at the reviewer's read checkpoints. The reviewer withdrew that finding. A +separate paged-provider witness now reaches a retained +on-demand mixed-kind index after its domain changes. It checks public rows +after a finite request fills a missing prefix row and after a later cursor +request traverses the retained index. Wrong-provider controls that withhold +either requested row fail the public comparison. Other provider histories +remain untested; this witness does not establish general safe traversal of an +ineligible retained index. + +Captured `inArray` list-entry refresh and whole-live-query reconstruction were +explored as new features. The pre-PR evaluator and live graph did not already +deliver the whole-result behavior. The maintainer withdrew that expansion for +this PR because of its production-code and query-planning cost. A separate +proposal must define its dependency and invalidation boundary before adding +an oracle or production graph machinery. In-place edits to a Date or byte +item, unsigned default byte ordering, and guarantees after callers alter +standard objects are also outside the accepted contract. + +Earlier experiments and RED/GREEN records remain in published commits on +`codex/oracle-test-retirement-2-blocks`. This PR restores the confirmed, +bounded fixes described above. The R16 and R17 rows still distinguish +earlier-branch observations from a current-base failure; neither justifies +new production machinery without a baseline witness under the accepted law. diff --git a/docs/contributing/oracle-reviews/pr-2170-react-paced-render-cuts.md b/docs/contributing/oracle-reviews/pr-2170-react-paced-render-cuts.md new file mode 100644 index 0000000000..62a1e0169c --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-2170-react-paced-render-cuts.md @@ -0,0 +1,99 @@ +# PR #2170 React paced-mutation render-cut review + +Reviewed product commit: `6d176225cdcac2547ec04c7df86c0a5b8bc0c88f`. +Oracle repair commit: `0e3214d12a47cd14cfc06046709847765452076e`. +Base: `ce0b4a98061e335f7aeb144283c7679d6fdc6529`. +The production repair already existed at the reviewed commit. This follow-up +replaces its example-only tests with an independent React receiving oracle and +updates the coverage map. + +## Law and result + +`usePacedMutations` exposes callbacks from the latest committed render. A +render that suspends cannot replace the callbacks used by the visible +component. A render that commits publishes its callbacks before descendant +layout effects. The returned mutate function keeps its identity across a +callback-only commit. + +The public hook contract supplies immediate optimistic mutation and delayed +persistence. React supplies the render/commit distinction and insertion effects +before descendant layout effects. The model retains only the document owned by +the latest commit. It predicts callback ownership without reading production +refs or effect choice. + +The bounded grammar crosses ordinary and StrictMode schedules with five +histories: mutation before a render that stays suspended, mutation after that +suspended render, mutation after the candidate commits, and delayed or +immediate mutation from a descendant layout effect. A committed history first +suspends B and then releases it. A descendant layout call without a commit is +excluded because that descendant does not mount. + +The driver renders the public hook, updates a real Collection, and controls the +debounce edge. It compares the ordered optimistic/persistence callback trace +and both public rows at the mutation cut, the trace after persistence, and +mutate identity at the commit cut. No history commits another render between +transaction admission and persistence. Transaction-admission callback affinity +remains a separate contract question. + +## Calibration and GREEN + +The original render-time ref update failed four of ten histories at the intended +comparisons. Before-render/suspended histories persisted through B's +uncommitted callback. After-render/suspended histories invoked B's optimistic +callback from A's visible button. Each failed in ordinary and StrictMode runs. + +A layout-effect-only mutant passed suspended-render isolation but failed both +delayed descendant-layout histories: B's layout call used A's optimistic +callback. A split mutant that published `onMutate` in insertion effect and +`mutationFn` in layout effect passed the delayed histories but failed both +immediate descendant-layout histories at the persistence trace. These mutants +failed by assertion after reaching the named public cut; none failed during +setup or by timeout. The insertion-effect implementation passes all ten +histories. The unchanged core paced owner passes 154 tests while these React +mutants are possible, confirming that it owns a narrower boundary. + +## External-review ledger + +| ID | Claim | Verdict and action | +| --- | --- | --- | +| ER-001 | An uncommitted render can replace the optimistic callback used by the visible component. | Confirmed by the after-render/suspended histories; fixed by the reviewed product and covered by the React oracle. | +| ER-002 | An uncommitted render can replace the persistence callback for an admitted mutation. | Confirmed by the before-render/suspended histories; fixed and covered. The oracle does not select callback ownership when another render commits between admission and persistence. | +| ER-003 | A parent layout effect publishes too late for a descendant layout mutation. | Confirmed by the layout-effect mutant; insertion-effect production and descendant histories cover it. | +| ER-004 | Callback refresh must preserve mutate identity. | Already preserved; retained as a neighboring assertion on every committed history. | +| ER-005 | Eight submitted examples close the reported class. | Refuted as test-integrity evidence. They reproduce faults but have no independent model or hostile timing challenges. Replaced with the bounded oracle. | +| ER-006 | Core paced tests do not exercise React's render/commit boundary. | Confirmed. The core owner remains unchanged; the coverage map now names the React receiving owner. | +| ER-007 | No actionable issue and minimal merge risk. | Refuted as a readiness conclusion because it missed the oracle and coverage gaps. Its implementation summary was accurate. | +| ER-008 | Add docstrings to satisfy the review bot's touched-function ratio. | Refuted. The exported hook is documented; local fixture helpers need literate oracle prose, not individual API docstrings. | + +## Oracle guide audit + +| Requirement | Result | +| --- | --- | +| ORC-001 | The opening prose states the committed-callback law, authority, and limits. Transaction-admission affinity stays open. | +| ORC-002 | The model tracks only the document committed at transaction admission and never reads production refs or effect choice. | +| ORC-003 | Contract, model, bounded grammar, public driver, observations, and checkpoints are distinguishable in the executable file. | +| ORC-004 | No generated property is claimed. The finite grammar reconstructs both reported faults and the layout neighbor; StrictMode supplies the alternate React schedule. | +| ORC-005 | The public hook and real Collection run. Ordered traces retain wrong callback ownership, and assertions run before and after persistence. | +| ORC-006 | The render-time, layout-effect, and split callback-publication mutants fail at their intended comparisons. | +| ORC-007 | Not applicable: this is bounded enumeration, not an important generated property. | +| ORC-008 | The model retains one committed owner because the next callback distinguishes A from B. No other model state is introduced. | +| ORC-009 | `owner` is the document whose committed callback runs. It is not Collection ownership or transaction ownership. | +| ORC-010 | Testing Library cleanup and Collection cleanup run after each history; calibration failures remained the primary assertions. | +| ORC-011 | No shared semantic classifier exists between the one-field model and the React ref implementation. The layout-effect mutant is a second timing challenge. | +| ORC-012 | This revision-bound record captures law, grammar, driver, cuts, mutant outcomes, limits, and the item ledger. | +| ORC-013 | Suspended and committed neighbors distinguish render from commit; immediate descendant persistence distinguishes insertion from layout publication for both callbacks. | +| ORC-014 | Not applicable: the claim stops at the controlled React host and does not assert server or other-framework behavior. | + +## Limits and readiness + +The evidence closes callback ownership for the ten named React histories. It +does not cover arbitrary transition interleavings, errors, unmount during a +pending write, multiple independently changing callback contexts, server +delivery, or other frameworks. Callback affinity to transaction admission is +an unresolved design question. These histories do not commit another render +between admission and persistence. + +All eight raw review items are accounted for: three fixed-now, one already +fixed and retained, one repaired test-integrity gap, one repaired coverage gap, +and two refuted review conclusions. The oracle repair is complete for the named +boundary. Whole-PR readiness still depends on final branch review and CI. diff --git a/docs/contributing/oracle-reviews/pr-2171-explicit-query-refresh.md b/docs/contributing/oracle-reviews/pr-2171-explicit-query-refresh.md new file mode 100644 index 0000000000..1dfb68d4b8 --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-2171-explicit-query-refresh.md @@ -0,0 +1,65 @@ +# Query Collection explicit mutation refresh + +This record applies to the change that contains it, after merge commit +`621833269`. The first-RC API decision removes automatic refetch after mutation +handlers. Handlers return `Promise` and synchronize explicitly. + +## Distinguishing observations + +The ownership oracle adds a bounded table over insert, update, and delete, +with no confirmation, an explicit refetch, or a direct write. The backend starts +empty for insert and with one row for update and delete. A gate keeps the +optimistic transaction persisting until its optimistic public rows are checked. +The backend then accepts the edit. At transaction settlement: + +| Handler confirmation | Query calls, including initial load | Public rows | +| --- | --- | --- | +| None | 1 | Initial synced rows | +| Explicit refetch | 2 | Confirmed server rows | +| Direct write | 1 | Confirmed server rows | + +The old automatic-refetch implementation fails all nine cases at the exact +fetch-count comparison. In particular, an explicit refetch produces three calls. +After removing it, all nine cases pass. Expectations come from the initial and +confirmed row arrays and the declared confirmation action, without production +ownership or fetch classifiers. + +Existing ownership and ordered-refetch assertions remain intact. Their handlers +now refetch explicitly where the old wrapper supplied the request. Obsolete +opt-outs disappear from handlers that already refetch or write confirmed data. + +## Evidence and rubric scope + +- ORC-001, ORC-002, ORC-003: the migration decision supplies authority. The + table's prose explains its independent reference, finite histories, real + Collection and QueryClient driver, and public observations. +- ORC-004, ORC-007: no generated grammar or campaign changes. The new table + exhausts its nine declared histories. Existing ordered-prefix fixed and + random campaigns run in the package suite. +- ORC-005, ORC-006: the real transaction reaches both the persisting and + settled checkpoints. The old implementation fails all nine cases by + assertion, rather than timeout or setup failure. +- ORC-008, ORC-009: no stateful model or vocabulary changes. Initial and + confirmed rows denote public data, not production bookkeeping. +- ORC-010: cleanup releases the gate and disposes the Collection and QueryClient. + The table has no shrinker or failure capture that can replace its assertion. +- ORC-011: no shared-classifier fault hypothesis requires a second model. +- ORC-012: this record bounds the added evidence. It does not claim universal + closure of ownership or scheduling defects. +- ORC-013: no confirmation and explicit confirmation distinguish the boundary + at settlement for each mutation. The explicit-refetch case also rejects + duplicate fetching. Claims cover these finite cases only. +- ORC-014: a controlled backend supplies complete arrays. The test proves the + Collection and QueryClient contract, not HTTP transport or backend ordering. + +Verification: Query Collection's complete suite passes with no type errors +(42 files, 1466 assertions/tests reported by Vitest). The first-RC release +fixtures cover a stable minor bump and reject a stable major bump. The merged +LocalStorage value-fidelity oracle passes 234 cases after migrating its wait to +`when('settled')`. The Node Browser reload driver passes all 29 fixed histories +after the same API migration; native Chromium and Electron reload drivers were +not rerun for this mechanical change. + +Excluded: independent Query polling, focus, reconnect, and external invalidation +triggers. The coverage map names the existing ownership and ordered-refetch +owners for their broader lifecycle laws. diff --git a/docs/contributing/oracle-reviews/pr-2213-opfs-close.md b/docs/contributing/oracle-reviews/pr-2213-opfs-close.md new file mode 100644 index 0000000000..63bde9a7bb --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-2213-opfs-close.md @@ -0,0 +1,95 @@ +# PR #2213 OPFS close review + +- Base: `origin/main` at the PR's merge base. Original PR commit: `832dff8a4`. +- Repairs: `b836bfc95` (connection close), `0cd9128c8` (worker request + order), `d56a7ee98` (closed-connection error), and the change that gives + the request-order oracle its Node and Chromium drivers. +- Oracles: + - The connection close lane of + `packages/browser-db-sqlite-persistence/tests/opfs-page-lifecycle-oracle.test.ts`, + which uses a controlled worker. + - The request-order oracle, + `packages/browser-db-sqlite-persistence/tests/opfs-request-order-oracle.ts`. + Its Node driver is `tests/opfs-request-order-oracle.test.ts` and its + Chromium driver is `e2e/opfs-request-order-oracle.opfs.spec.ts`. + +## Contract and evidence + +Every request an OPFS connection accepts settles exactly once. Close ends +admission when it starts, and concurrent close calls share one outcome. The +worker answers requests in arrival order, so close waits for earlier requests +and those requests keep their results. Pagehide or a Worker terminal event +during close wins: close and pending work reject with that event's failure. +The README states this contract. + +The original PR rejected pending work when close settled. An external review +found that concurrent close rejected the second caller, that work admitted +during close reached a worker that had cleared its state, and that the pinned +close table could not distinguish rejecting at close start from rejecting at +settlement. A real-worker probe then showed that the worker handled messages +concurrently: close ran while an execute waited for the OPFS access handle, +SQLite refused to close the unfinalized statement, and close was answered +first. + +## Calibration + +Each control below ran against the full lifecycle or request-order oracle. +"Killed" means the named lane failed an assertion rather than timing out. +Where a row names the failing observation, it was inspected; the other rows +record only which lane failed. + +| Control | Owner | Result | +| --- | --- | --- | +| Pre-PR connection (pending work never settles) | Close lane | Killed at close settlement | +| Original PR connection (second close posts its own request) | Close lane | Killed | +| Reject all pending work when close starts | Close lane | Killed | +| Concurrent close not memoized | Close lane | Killed | +| No admission check while closing | Close lane | Killed | +| Defer rejection past close settlement | Close lane | Killed at close settlement | +| Ignore pagehide while closing | Close lane, pagehide lane | Killed | +| Ignore Worker `error` while closing | Close lane, terminal lane | Killed | +| Concurrent worker (pre-repair) | Request-order, Node driver | Killed on an insert that waits for the handle while close reaches the worker | +| Worker queues executes but not close | Request-order, Node driver | Killed on the same history | +| Concurrent worker (pre-repair) | Request-order, Chromium driver | Killed in 20 of 32 examples: settlements differ from the model | +| Worker queues executes but not close | Request-order, Chromium driver | Killed in 20 of 32 examples | + +The pre-PR and original-PR connection controls are the exact earlier +production files. The other connection and worker controls were temporary +source mutations, reverted after each run. Before the close lane replaced it, +the original pinned close table let the "reject at close start" and "dispose +before reject" mutants pass. + +With the repairs, the package's unit and type suite passed: 20 files and 467 +runtime tests. All configured Chromium OPFS specs passed. + +A first version of the shared driver returned the handle in the same turn +that it submitted close. Close then never reached the worker during the wait, +and both worker mutants passed both drivers. The driver now waits for close to +reach the worker before it returns the handle. Both mutants then failed. + +## Limits + +The close lane uses a controlled worker that can answer in any order, so it +also covers a close response that overtakes pending work. The request-order +oracle runs the real worker module and wa-sqlite WASM in Node over an +in-memory VFS with OPFSCoopSyncVFS's lock protocol, and its Chromium driver +runs the same fixed examples against real OPFS in local Chromium. Together +they do not establish +other browsers, frozen tabs, a crash between a write and close, or multi-tab +contention beyond one held handle. + +`BrowserWASQLiteDriver.close()` does not wait for queued driver work, the same +as the Node and OP-SQLite drivers. A probe showed that a transaction and +queued work interrupted by close reject with the closed-connection error, and +close succeeds; SQLite rolls back the open transaction. This is the accepted +driver contract: draining would make close wait for a transaction callback's +own application code, which may itself wait for close. + +A closed connection used to report `InvalidPersistedCollectionConfigError`. +It now reports `SQLiteConnectionClosedError`, exported from the core and +browser packages. Work accepted before close and then cancelled still rejects +with `AbortError`, the same as pagehide. + +While writing the Chromium driver, a 63-character database name failed to +open with a bare `sqlite3_open_v2` error, while a 56-character name opened. +The limit and its error message are untested and unrecorded elsewhere. diff --git a/docs/contributing/oracle-reviews/pr-2258-gc-retention-round2.md b/docs/contributing/oracle-reviews/pr-2258-gc-retention-round2.md new file mode 100644 index 0000000000..1687c93566 --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-2258-gc-retention-round2.md @@ -0,0 +1,273 @@ +# PR 2258: second oracle review, decisions and enforcement + +Evaluation baseline: `f1a39978e2822a96e5add42136a88674c7bc5d17`, 2026-10-26. +This record supplements the [initial review](pr-2258-gc-retention.md); it does +not rewrite that review's historical results. The appended round-2 entry in +[the fingerprint manifest](pr-2258-gc-retention.sources.json) binds the final +sources. The repair revision is the commit that first adds this record. + +## Contract decisions and the three revisions + +The maintainer made two explicit decisions in this chat after examining rival +laws and concrete histories. First-usable-idle retirement stays a fixture +scheduling assumption. Public retention deadlines remain strict, without a new +public first-callback liveness guarantee. Pending manual transactions do not +retain Collections: callers keep a subscription while they need draft +visibility. Restart after GC does not guarantee restoration of the prior draft +projection. The separate earlier decision still applies: rolling back a +transaction that never left `pending` preserves its existing GC deadline. + +Here, main is `5649a07d468117891193c2e4cde9c7706308b1cd`, the submitted PR is +`f19f4908a3c0b8a91a696a2a77076e91230e4205`, and the evaluation baseline is +`f1a39978`. Historical source inspection is distinguished from executed evidence. + +| Rule | Main / submitted PR / evaluation baseline | Authority and implication | +| --- | --- | --- | +| Persisting transactions retain every touched Collection | Main lacks the hold; submitted PR adds it; baseline implements it | New explicit policy in this PR, motivated by issue 2256. Main's GC can discard ongoing work. | +| Full fresh gcTime after the final persisting hold ends | Main has no transaction renewal; submitted PR adds it; baseline enforces it | Stronger than merely waiting for the handler. It is an intentional PR retention promise. | +| Obsolete idle work respects a renewed deadline | Submitted PR can retire at 121ms despite renewed eligibility at 221ms; baseline cancels obsolete work | Consequence of the new full-interval promise. Historical controlled/native RED and repair are in the initial record. | +| Fast settlement preserves residual startup grace | Main has a startup floor; submitted PR can overwrite it with gcTime 1; baseline preserves the absolute horizon | Existing 50ms startup promise composed with the new holder. It does not grant a fresh 50ms after every settlement. | +| A real last unsubscribe ends startup handoff grace | All three use gcTime after the last subscriber | Prior pooled-GC oracle prose independently states the handoff rule. Public wording is now clearer. | +| Pending rollback preserves its existing deadline | Main preserves; submitted PR refreshes; baseline preserves | Explicit earlier maintainer decision, rather than an unambiguous pre-PR prohibition. | +| Pending drafts do not retain; caller owns visibility | All three omit pending drafts from GC holds | Historical behavior alone was insufficient authority. The maintainer explicitly chose this policy in round 2. | +| First usable idle must immediately retire | All three schedulers do so in the synchronous fixture; the model selects that cut | Fixture expectation only. A safe scheduler that needs another turn does not thereby violate the public contract. | +| Cleanup stays empty when old transactions settle | Baseline resurrects a surviving sibling during or after cleanup; this repair suppresses that overlay | Existing cleanup/discard promise, not a new law. Confirmed with the expanded oracle before production edits. | + +A diagnostic pending-draft history exposed two legal source forms. After GC, +readiness-only restart leaves the draft absent; an empty source commit rebuilds +it. Isolated main has the same immediate observations. This is not a PR +regression or a required restoration repair under the chosen lifetime policy. +The generated subscribed-draft lane instead enforces the supported workflow: +retain the row across the unheld deadline, unsubscribe, then observe normal GC. + +## Laws, experiments and repairs + +The review was considered as a whole before edits. The plan combined exact row +observations with warm/cold preload, sibling order and asynchronous cleanup +dimensions. Production remained unchanged until the expanded oracle and the +real-provider receiving path ran. This exposed a previously missed production +interaction, H03. + +| Law / gap | Decisive experiment | Repair and inference | +| --- | --- | --- | +| E01: cleanup clears public rows | Three hostile observations agree on status/phases but retain key 1 after automatic or explicit cleanup. Old checker accepts all three: calibration RED 3 | Shared checker requires empty keys at cleanup start and after retirement. Same comparison runs in generated, native and provider drivers. | +| E03: settled optimistic rows drop without source support | Old nonretired checker accepts stale completed and failed rows: calibration RED 2 | Exact key membership equals independently supplied source keys plus active optimistic inserts. Missing, duplicate, extra and stale keys are rejected; independently known source rows remain valid. Original two-Collection comparison is also exact. | +| H02: interacting owners and release causes | Expanded grammar crosses repeated warm preload with queued idle/persisting work; failed cold preload with both sibling orders; cleanup with settlement | First complete campaigns reveal H03. Final campaigns reach all 25 states; the six raw preload/cleanup scopes remain accounted for below. | +| H03: cleanup must not republish a discarded projection | Two persisting inserts, held adapter cleanup, then one handler completes: sibling key 1 reappears. Cleanup fully settles before a handler completes with a pending sibling: key 2 reappears | Lifecycle query identifies cleanup through final retirement. Active overlay skips that discarded run while settled transaction pruning still runs. Both cuts are GREEN after repair. | +| R01 / original R09: real Electric confirmation survives the unheld GC deadline | Actual assigned PostgreSQL txid remains uncommitted across the deadline. Handler stays persisting, caller pending, optimistic row visible. COMMIT supplies genuine Electric confirmation | New registered provider driver shares the model/checker. Four histories pass before and after H03 repair. No-hold runtime mutant fails all four at the intended shared comparison. | + +H03's first attempted refactor deleted settled entries while iterating SortedMap. +Existing sibling histories immediately rejected the resulting skipped overlay. +The repair keeps the existing deferred pruning pass. Clearing tracked +transactions wholesale would also lose unsettled ownership and affect later +restart, so it was not used. No new lifecycle state, queue or recovery machine +was added. + +The remaining holds, last release and handoff horizon determine the independent +deadline. `cleaningUp` distinguishes synchronous row discard from asynchronous +adapter settlement; the public status retains its prior value during that +interval. A legal cleanup-settle action distinguishes these states. The driver +checks public rows synchronously at cleanup start, then rows, phases, exact +tracked transaction identities, preload outcomes/errors, cleanup promise and +public status at each relevant cut. It permits only old transaction settlement, +rollback, elapsed time and idle after retirement; new admission/restart remains +outside that lane. + +## Reach, calibration and receiving paths + +The final fixed seed is 20261026. Both fixed and random boundary campaigns run +250 histories using the same grammar/driver/checker. Declared minimum is one +history per state; removing a dimension cannot silently turn the test green. + +New dimensions: gcTime 1/10/49/50/51/100ms, ages 0–80ms, release gaps 0–60ms, +preload count 1–3, independent sibling outcomes/order, failed preload as first +or last release, held versus settled adapter cleanup, pending/persisting +survivors, and subscribed pending drafts. Disabled values remain +0/-1/Infinity/-Infinity/NaN. Deadline-minus-one and deadline observations come +from the model. Concurrent cold calls share their public promise; exact source +error identity is observed. No expected deadline comes from production flags. + +| Declared state | Fixed | Random | +| --- | ---: | ---: | +| queued-idle-renewal | 15 | 20 | +| startup-release-before-floor | 48 | 48 | +| last-unsubscribe-short | 9 | 17 | +| pending-rollback | 37 | 32 | +| pending-does-not-hold | 30 | 42 | +| sibling-persisting | 90 | 89 | +| preload-last-release | 21 | 14 | +| disabled-gc | 19 | 19 | +| delayed-commit | 14 | 19 | +| write-started-sync | 4 | 9 | +| late-release-short | 62 | 44 | +| warm-preload-persisting | 20 | 12 | +| warm-preload-queued-idle | 20 | 12 | +| warm-preload-repeated | 20 | 12 | +| preload-failure-held | 23 | 15 | +| preload-failure-last | 21 | 15 | +| sibling-oldest-first | 42 | 42 | +| sibling-newest-first | 48 | 47 | +| async-cleanup-settlement | 39 | 37 | +| async-cleanup-pending-idle | 39 | 37 | +| retired-settlement | 10 | 18 | +| retired-sibling-held | 10 | 18 | +| pending-subscription-held | 12 | 22 | +| pending-last-unsubscribe | 12 | 22 | +| concurrent-preload | 46 | 28 | + +Reconstruction preserves the initial three reports, repeated warm overlap, +failed-final-preload, and both cleanup settlement timings. Ablation of each new +dimension loses the corresponding named reach/checkpoint. Single-transaction +cleanup remains a neighboring lane; sibling lanes construct two transactions +to retain meaningful reach. Settling an unstarted pending handler and +unsubscribing without an owner remain illegal. Restart after discarded pending +projection is outside the claimed row lifetime, by the maintainer's decision. + +All five new source mutants fail by assertion at the intended checkpoint: + +| Wrong design | Distinguishing rejection | +| --- | --- | +| Remove cleanup overlay guard | Generated and both reconstructed cleanup histories reject resurrected keys | +| Suppress overlay only while adapter cleanup is pending | Held-cleanup witness passes; post-cleanup witness rejects key 2 | +| Return early from transaction notification during cleanup | Rows stay empty, but exact tracked identities reject unpruned settled transactions | +| Omit warm-preload refresh | Repeated warm preload retires early at 144ms | +| Omit failed-preload hold release | Failed-final-preload misses retirement at 225ms in the controlled fixture | + +The final frozen-grammar guard-removal replay is seed 20261026, path +`1:0:0:0:0:0:0:0:0:0`: exactly one selected history fails on the mutant, then +passes after byte-for-byte source restoration. Earlier shrink paths are bound +to their earlier grammar revisions and are not represented as final replays. +Wrong-result calibration is distinct from source mutants. Original unchanged +production RED, attempted-repair RED, mutant RED and repaired GREEN were retained +separately; no setup failure or timeout is counted as an assertion kill. + +Electric receiving histories use gcTime 10/100ms × no subscriber/last unsubscribe. +Actual PostgreSQL 16 and Electric 1.8.1 supply the provider boundary, with the +installed SDK 1.5.15. A second SQL connection proves the uncommitted row is not +visible. Genuine `awaitTxId` acknowledges after COMMIT; caller settlement then +exposes the source fields, `$synced` and `$hasPendingWrites`. The driver supplies +expected source keys from its own SQL action history, never observed rows. +Idle delivery is controlled; elapsed timers and provider I/O are real. Node +environment avoids the existing jsdom/native-fetch AbortSignal mismatch. The +existing E2E glob registers the new file. DB was rebuilt from the worktree for +both the unchanged-production and repaired-provider runs. + +The provider no-hold runtime fixture produces four intended retirement +comparison failures. Its temporary copy was removed without production edits. +Native Chromium separately passes the existing three histories with actual idle +provenance/budget and cancellation receipts. Startup's receiver observes safety +at 10ms and does not establish eventual cleanup. Native and provider premises +are each witnessed; their combined browser/provider execution is not claimed. + +## Loss audit and reviewer assessment + +The append-only task ledger retains the four raw reports, their original +claims, source locations, technical verdicts, actions and durable value. The +final audit returned to all four raw reports and found E03 had been omitted +from the first headline summary; it was added and repaired. H03 is a separately +recorded sibling discovered by the planned oracle extension. + +| ID | Claim / disposition | Evidence or durable destination | +| --- | --- | --- | +| E01 | Retired-row comparison omission: fixed-now | Exact cleanup keys plus three RED/GREEN calibrations; original cleanup path also observes rows | +| E02 | Throwing settlement callback skips renewal: refuted for the reported path | Callback unsubscribes and throws exact error at 90ms; unsubscribe renews gcTime. No claim about every possible callback | +| E03 | Live checker accepts stale settled rows: fixed-now | Complete/fail calibration RED 2, exact membership GREEN; missing/extra/duplicate controls | +| A01 | First-idle law authority: accepted-design | Explicit maintainer choice, fixture-specific scheduling prose | +| A02 | Startup handoff wording ambiguity: fixed-now | Prior pooled contract authority; public gcTime wording clarified | +| A03 | Pending nonholding lacked separate authority: accepted-design | Explicit maintainer decision, public config/manual draft guide, subscribed-draft generator | +| A04 | Cleanup start differs from settled status: accepted-design | Public cleanup/glossary; held async adapter observations, without imposing synchronous final status | +| R01 | Duplicate of original R09 receiving gap: duplicate, canonical work fixed-now | Genuine-provider driver, baseline/final GREEN 4 and runtime-mutant RED 4 | +| H01 | Pending restart differs by source form: accepted-design | Both source forms remain legal; no restoration promise after discarded projection | +| H02 | Missing generated dimensions: fixed-now | All six raw scopes: queued warm/reuse; failed siblings both orders; cold-success boundary matrix; cold-failure boundary matrix; warm-persisting matrix; held cleanup/settlement | +| H03 | Cleanup resurrects unsettled sibling projection: fixed-now | Both original RED cuts, repaired GREEN and original/narrow/pruning mutants | + +Round-2 accounting: 11 items = 5 fixed-now + 4 accepted-design + 1 refuted + +1 duplicate. The duplicate's canonical original R09 is now repaired; it does +not erase the obligation. No deferred or confirmed-open round-2 item remains +within the declared boundaries. The original 12-item review's R09 evidence gap +is superseded by the executed provider witness here, preserving its historical +open verdict in the initial record. + +CodeRabbit's two reviews, 5656398687 at 425192752 and 5656525995 at f1a39978, +had 18 positive file notes and two successive wording suggestions. The first +wording was already fixed; the second is fixed here. The mutable summary's +docstring metric is not the repository's proportional literate-oracle policy; +relevant prose is present. All startGCTimer callers take no argument. Its +suspected leftover latestWindowRequest mapping corresponds to a real private +member and passes the generated cache check. Earlier fingerprint baseline is +intentional; this evaluation appends its own binding. The final PR description +must describe the actual oracle/native/provider scope. These seven distinct +summary/review items remain in the local ledger, including useful nonblocking +suggestions; no bot feedback is silently dropped. + +Hire the enforcement reviewer for precise checker calibration and qualified +callback refutation. Hire the histories reviewer for distinguishing legal +source forms and testing main before claiming regression. Hire the receiving +reviewer for precise native/provider evidence; its original blanket approval of +all model policy was too broad and is superseded by the explicit decisions. +Hire the authority reviewer for separating prior promises, PR additions and +undecided laws. The independent final challenger found no additional supported +defect and ran 67 tests across five files with stable source hashes. CodeRabbit +provided useful wording feedback but missed the substantive checker/cleanup +gaps; this sample does not support a substantive hire recommendation for it. + +## Validation and limits + +Production changes +are confined to the cleanup query and overlay fence; tests/documentation grow +separately. The five source mutants and real-provider runtime mutant were +restored. No install override or lockfile workaround was needed. + +The original rubric audit remains applicable, with these updates: ORC-001/009 +now cite the explicit timing/draft decisions and cleanup split; ORC-002 keeps +source-key premises independent; ORC-003/004 expose the expanded grammar and +its ablation/exclusions; ORC-005 checks exact keys and intermediate cleanup +cuts; ORC-006 adds checker/source/provider calibrations; ORC-007 preserves +equal-budget campaigns and final direct replay; ORC-008 justifies cleaningUp +through a distinguishing cleanup-settle continuation; ORC-010 retains cleanup +failures separately; ORC-012 is this source-bound record; ORC-013 includes the +new deadline-adjacent preload cuts; ORC-014 now has the genuine-provider +receiver; ORC-015 enforces all 25 states. ORC-011 has no remaining named shared +semantic-fault hypothesis needing a second formulation: the authority issues +were explicitly decided, and the exact-row reference is independent. + +Cleanup rejection, source recovery after preload failure, cleanup interrupting +a pending preload, paced/offline restoration, multiple provider transactions, +persistence-wrapper/provider combinations, and arbitrary legal permutations +are not established by these bounded campaigns. A proposed deferred-held-row +counterexample was checked against the real persisted wrapper: it clears its +deferred-publication list synchronously before cleanup awaits, while core clears +queued/held rows. Keeping that predicate true artificially would contradict the +current collaborator lifetime. No extra defensive machinery was added for it. +The existing cleanup-restart and optimistic ownership oracles retain those +neighboring contracts; passing these samples is not a universal proof. + + +Final local verification: + +- Full DB run: 287 files and 13,808 tests passed; one guarded index-update replay + child timed out under concurrent validation. No type errors. Its isolated + rerun passed all 38 guarded replay tests with no type errors. The original + timeout remains visible; the combined full run is not mislabeled GREEN. +- Independent final challenge: five files, 67 tests passed, including the two + GC properties, checker calibration, cleanup-restart and transaction ownership. +- Native Chromium: all three histories passed on scratch port 42879. The + configured port was occupied. Initial scratch import-path failure was setup, + not product RED; the corrected config preserved the driver/model/checker. +- Actual Electric/Postgres: four histories passed before and after repair; all + four no-hold runtime mutants failed the intended comparison. +- DB main/test TypeScript projects, Electric TypeScript, focused ESLint, + changed-file oracle lint, mangle-cache check and whitespace check passed. +- `pnpm size:compare --packages db --files`: whole PR versus merge base + 427a7c31e, DB +85B gzip/+114B brotli; typical app +84B gzip/+60B brotli. + The added cleanup fence pays for distinguishable discarded-projection + ownership while preserving existing transaction pruning. The attempted + deletion refactor failed SortedMap iteration and was rejected. Clearing + unsettled tracking or returning before pruning would break neighboring laws. + Existing shared-clock consolidation remains; no extra recovery state was added. + +The final pushed head and its CI are tracked separately; this local record +claims bounded tested behavior, not that green local checks imply merge readiness. + +Test source growth versus the evaluation baseline: 28,143 uncompressed bytes. +Documentation/changeset/manifest source growth: 25868 uncompressed bytes. diff --git a/docs/contributing/oracle-reviews/pr-2258-gc-retention-round3.md b/docs/contributing/oracle-reviews/pr-2258-gc-retention-round3.md new file mode 100644 index 0000000000..7eeb54185a --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-2258-gc-retention-round3.md @@ -0,0 +1,253 @@ +# Collection GC: interaction and receiving-path evidence + +This is an append-only third evaluation of PR #2258. The baseline is +`d0618586aa7d033b70de53f6e20598bb39b71147`; the evaluated test sources are bound by +`round3.sha256` in [the source record](pr-2258-gc-retention.sources.json). +The commit that first adds this report is the evaluated revision. Historical +reports retain their own claims and limits. + +## Law, authority and experiment + +`BaseCollectionConfig.gcTime` retains each Collection while any touching +transaction persists. The transaction-ownership oracle additionally requires +settlement notification to every Collection that admitted a transaction, even +when its mutations later merge away. Cleanup starts by discarding public rows; +its promise and final status wait for adapter settlement, including rejection +with `SyncCleanupError` whose cause is the adapter failure. The glossary, +`Collection.cleanup()` and the cleanup-restart oracle own this split. + +The maintainer's earlier decisions remain in force: pending manual rollback +preserves its deadline, pending drafts require a caller subscription for +visibility, restart after GC owes no discarded-draft restoration, and exact +first-usable-idle retirement remains a controlled fixture assumption. Explicit +cleanup overrides retention. Automatic cleanup waits for eligibility first. +This round introduces no new product promise and changes no production source. + +All four requested improvements use the existing independent logical-hold +reference. Expected deadlines use logical releases and the absolute startup +handoff deadline, never production timers or classifiers. The mutation driver +adds an independent projection of known source rows and declared update/delete +intent. Actual mutations prove path reach only; they do not compute expectations. + +1. **Interaction grammar:** preserves all 15 focused lanes and adds constructive + 6–14-action suffixes, up to seven transactions and three subscribers. A prefix + removes one of two subscribers before crossing the unheld deadline, then + combines delayed commit, three persisting siblings, non-FIFO settlement and + preload between releases. All emitted actions are checked for legality. +2. **Automatic cleanup:** generates both held and rejecting adapter cleanup after + independent deadline-minus-one/deadline cuts. Zero-budget idle cannot start + cleanup. Usable delivery must invoke the adapter before the driver joins the + public cleanup promise. Pending-sibling rollback checks row discard while + cleanup waits and after it settles. The first draft expected a raw adapter + error; that expectation contradicted the established cleanup wrapper. The + corrected oracle checks `SyncCleanupError` and exact cause. This was an oracle + authoring error, not a production RED. +3. **Mutation admission:** direct update/delete handlers, shared update/delete, + and a shared insert/delete pair that merges away on one Collection while a + peer update keeps a genuine persisting transaction. Both orientations, + completion/failure/rollback, absent/final-unsubscribed owners, gcTime + 1/10/49/50/51/100ms, ages 0–80ms and values −3…3 are generated. A fully empty + transaction settles synchronously and cannot stand in for persistence. +4. **Real provider siblings:** four original one-write paths plus eight paths + crossing gcTime 10/100ms, absent/final-unsubscribed owners and both local + settlement orders. Separate PostgreSQL connections keep assigned transactions + genuinely open. After one COMMIT and genuine Electric acknowledgement, the + sibling stays SQL-open, unacknowledged, persisting and caller-pending beyond + the first-release deadline. PostgreSQL txid allocation order is independent + of the recorded local optimistic order. + +Removing subscriber multiplicity permits premature collection with a surviving +subscriber. Removing cancellation permits ownership to depend on surviving +mutations. Removing a failure outcome permits success-only renewal. Removing +held cleanup loses the intermediate discard/status cut. Removing the second +provider writer admits a first-release implementation. The grammar rejects +settlement of an absent or already-settled slot, unsubscribe without a subscriber, +and commit of a non-pending transaction. Source projection cannot waive cleanup's +empty-row obligation. Fixed checker controls reject missing, extra, stale and +duplicate membership. + +## Executed evidence + +The six-file focused integration command ran the boundary and mutation +properties, the original two-Collection property, checker calibration, +cleanup-restart and transaction ownership: + +```sh +pnpm --dir packages/db exec vitest run tests/collection-gc-retention-oracle.property.test.ts tests/collection-gc-mutation-oracle.property.test.ts tests/collection-gc-transaction-oracle.property.test.ts tests/collection-gc-retention-oracle.test.ts tests/collection-cleanup-restart-oracle.test.ts tests/transaction-ownership-oracle.property.test.ts --coverage.enabled=false +``` + +GREEN: six files, **70 tests passed**, no type errors. The boundary property runs +250 fixed-seed and 250 random histories with the same generator and checker. +Mutation admission runs 150 fixed-seed and 150 random histories. Both fixed +campaigns use seed `20261026`. The boundary mixture weights are 5:1:1 for focused +lanes, constructive interactions and automatic held/rejected cleanup. All +required states below reached their normal-budget minimum of one. + +### Boundary reach + +| Declared state | Fixed | Random | +| --- | ---: | ---: | +| `queued-idle-renewal` | 17 | 18 | +| `startup-release-before-floor` | 70 | 45 | +| `last-unsubscribe-short` | 43 | 36 | +| `pending-rollback` | 83 | 82 | +| `pending-does-not-hold` | 57 | 62 | +| `sibling-persisting` | 108 | 87 | +| `preload-last-release` | 14 | 13 | +| `disabled-gc` | 11 | 13 | +| `delayed-commit` | 54 | 42 | +| `write-started-sync` | 5 | 5 | +| `late-release-short` | 64 | 61 | +| `warm-preload-persisting` | 59 | 34 | +| `warm-preload-queued-idle` | 14 | 9 | +| `warm-preload-repeated` | 46 | 25 | +| `preload-failure-held` | 6 | 9 | +| `preload-failure-last` | 15 | 10 | +| `sibling-oldest-first` | 45 | 38 | +| `sibling-newest-first` | 78 | 57 | +| `async-cleanup-settlement` | 19 | 34 | +| `async-cleanup-pending-idle` | 51 | 71 | +| `retired-settlement` | 11 | 7 | +| `retired-sibling-held` | 11 | 7 | +| `pending-subscription-held` | 14 | 12 | +| `pending-last-unsubscribe` | 31 | 33 | +| `concurrent-preload` | 22 | 20 | +| `interaction-subscriber-multiplicity` | 47 | 25 | +| `interaction-delayed-commit` | 47 | 25 | +| `interaction-three-persisting` | 47 | 25 | +| `interaction-preload-between-settlements` | 47 | 25 | +| `interaction-nonfifo-settlement` | 47 | 25 | +| `automatic-cleanup-held` | 32 | 37 | +| `automatic-cleanup-pending-rollback` | 32 | 37 | +| `automatic-cleanup-rejected` | 13 | 21 | + +### Mutation reach + +| Declared state | Fixed | Random | +| --- | ---: | ---: | +| `direct-update` | 34 | 27 | +| `direct-delete` | 49 | 49 | +| `shared-update-delete` | 37 | 41 | +| `shared-cancel` | 30 | 33 | +| `cancelled-a-retained` | 14 | 22 | +| `cancelled-b-retained` | 16 | 11 | +| `no-subscriber` | 75 | 75 | +| `last-unsubscribe` | 75 | 75 | +| `retired-after-complete` | 59 | 54 | +| `retired-after-fail` | 46 | 49 | +| `retired-after-rollback` | 45 | 47 | + +The original two-Collection property's seven required states also passed. +The independent read-only challenger reran the changed properties: 13 tests, +500 boundary histories and 300 mutation histories, all 33 and 11 states reached. +It inspected authority, model independence, legal histories, expected-key +projection, cleanup promise joining and provider premises. It found no supported +additional defect. Its fresh-key cross-product limit is retained below. + +### RED calibration + +No new production failure was found on the unchanged baseline. These deliberate +wrong implementations all failed intended assertions, rather than setup or +unrelated runtime errors: + +| Mutant | Intended distinguishing failure | +| --- | --- | +| Retain only Collections with surviving mutations | Cancelled Collection retires while its shared transaction persists. | +| Retain only non-delete mutations | Direct delete loses its persisting hold. | +| Notify only surviving mutation Collections | Cancelled Collection retains a settled tracking entry. | +| Remove cleanup's optimistic-overlay fence | Pending-sibling rollback resurrects discarded rows during automatic cleanup. | +| Collapse subscriber count to one | Interaction prefix retires while a second subscriber remains. | +| Publish cleaned-up at automatic cleanup start | Held adapter cleanup exposes final status before settlement. | +| Real-provider retention only while more than one transaction persists | All eight real sibling paths retire after the first acknowledgement despite a genuine open survivor. | + +Source-mutant commands ran the appropriate boundary or mutation test file with +`--coverage.enabled=false --typecheck.enabled=false`. Production edits were +serial and restored byte-for-byte in `finally` before GREEN verification. +The provider runtime mutant was a scratch driver, removed before final GREEN. +The first six are additional source calibrations; earlier reports retain their +11 source-mutant results. The provider partial-hold experiment is additional to +the earlier no-hold receiving mutant. + +Guarded replay reproduced the automatic-status failure exactly, then passed the +same history after restoring production: + +```sh +TANSTACK_DB_ORACLE_SEED=20261026 TANSTACK_DB_ORACLE_PATH=10:0:0:0:0:0 TANSTACK_DB_ORACLE_PROPERTY=collection-lifecycle.gc-retention-boundaries pnpm --dir packages/db exec node --import tsx tests/oracle-replay.ts tests/collection-gc-retention-oracle.property.test.ts --coverage.enabled=false --typecheck.enabled=false --maxWorkers=1 +``` + +Both runs execute one history through the guarded named property. The mutation +property also passed a guarded one-history replay. An initial invocation through +the `tsx` CLI failed on a sandbox IPC socket; that setup failure supplied no RED +evidence. The successful command above uses Node's `--import tsx` entry point. +The complete replay-guard suite passed **38 tests**, no type errors. + +### Receiving paths and verification + +Native Chromium: **3/3 histories passed** with the unchanged native receiver and +updated shared model/checker. Genuine Electric/Postgres: **12/12 paths passed**, +each reaching provider-confirmations-held, all-provider-writes-confirmed, +renewed-zero-budget and renewed-usable-idle once. Each of the eight sibling paths +also reached its settlement order and surviving-provider-hold-crosses-deadline +once. Each local settlement order occurred four times. + +```sh +pnpm --dir packages/db test:collection-gc:browser +NO_PROXY=127.0.0.1,localhost ELECTRIC_URL=http://127.0.0.1:3000 pnpm --dir packages/electric-db-collection exec vitest run --config vitest.e2e.config.ts e2e/collection-gc.e2e.test.ts --reporter=verbose --maxWorkers=1 +``` + +DB main/test and Electric TypeScript, changed-file oracle lint, focused ESLint, +Prettier and whitespace checks pass. The initial DB test-project command used +`tsconfig.tests.json`, which does not exist; the corrected `tsconfig.test.json` +passed. No install override or lockfile workaround was needed. + +Production shipped bytes do not change in this round. The whole-PR size result +versus the merge-base on origin/main remains DB +85B gzip/+114B brotli and typical +app +84B gzip/+60B brotli. Test source and evidence growth are recorded separately +in the closing weight note. Local logs remain under `/private/tmp/pr-2258-round3` +and the provider/mutation round3 evidence paths; the source-bound observations +and commands above are the durable evidence. + +## Rubric audit and limits + +| Requirement | Outcome for this expansion | +| --- | --- | +| ORC-001 | Existing public retention, ownership and cleanup laws, plus explicit maintainer decisions; no new product rule. | +| ORC-002 | Shared logical-hold reference and independent source/intent row projection; actual rows never predict expected keys. | +| ORC-003 | Opening contract, model mapping, legal histories, driver and checkpoint prose remain beside executable code. | +| ORC-004 | Domains, reconstruction, ablation and invalid continuations are described above; original focused lanes remain. | +| ORC-005 | Direct handlers, manual shared transactions and genuine provider commits reach exact rows/status/phases/receipts at admission, held deadlines, settlement and renewed deadlines. | +| ORC-006 | Six source mutants and eight rejecting provider sibling cases at intended comparisons; projection checker controls preserve exact emptiness. | +| ORC-007 | Equal fixed/random budgets per property, guarded mutation replay and exact boundary mutant RED/restored GREEN replay. | +| ORC-008 | Existing cleaningUp state distinguishes a next cleanup-settle action; mutate aliases admission without another lifetime state. | +| ORC-009 | Model prose maps mutate/insert and idle cleanupPending to production admission and split cleanup boundaries. | +| ORC-010 | Primary comparison and secondary teardown failures remain separate; source/SQL fixtures are restored. | +| ORC-011 | No remaining concrete shared semantic-fault hypothesis requires another formulation; source intent independently projects mutation rows. | +| ORC-012 | This report binds the reviewed sources and reports finite claims, not universal closure. | +| ORC-013 | gcTime 1/10/49/50/51/100, deadline-minus-one/deadline, zero/usable idle, one/two subscriber and first/final release distinguish boundary mistakes. | +| ORC-014 | Shared native receiver and genuine provider sibling driver establish their premises separately. Combined native/provider scheduling remains unproved. | +| ORC-015 | All 33 boundary and 11 mutation states reach the required normal-budget minimum; provider reach is per path. | + +All numbered requirements apply to some part of this expansion. ORC-011's +conditional second-formulation obligation has no remaining named hypothesis, +so no second lifetime model was added. The independent challenger is a strong +hire on this sample: it verified actual comparisons and provider premises, +reran normal budgets, and distinguished a missing cross-product from a defect. +There were no new review findings to discard or defer. + +The bounded claim is retention and cleanup refinement across these generated +histories, core admission paths, native idle paths and real provider +acknowledgements. It does not establish every possible future behavior. Fresh +optimistic keys still exclude same-key rollback cascades and confirming-sync +queue interactions with GC. The Collection lifecycle GC owner in the coverage +map needs a combined generated witness before extending its claim there; +neighboring optimistic-history and transaction-ownership owners protect their +semantic parts independently. Other limits are three-or-more real provider +writers, provider failures/recovery, combined native browser/provider schedules, +persistence-wrapper combinations, paced/offline restoration, cleanup during +pending preload and arbitrary legal permutations. No concrete reachable +counterexample under the declared contract remains known from this round. + +Round3 uncompressed source growth versus the baseline: tests/receiving drivers +31,235 bytes; documentation and registration 20,164 bytes +(before this closing note). Production growth: zero bytes. These source counts +are separate from the whole-PR shipped bundle measurement. diff --git a/docs/contributing/oracle-reviews/pr-2258-gc-retention.md b/docs/contributing/oracle-reviews/pr-2258-gc-retention.md new file mode 100644 index 0000000000..f27ece4368 --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-2258-gc-retention.md @@ -0,0 +1,160 @@ +# PR 2258: GC retention review and repair evidence + +Reviewed commit: `f19f4908a3c0b8a91a696a2a77076e91230e4205`, PR [2258](https://github.com/TanStack/db/pull/2258). +Evaluation date: 2026-10-26. HEAD stayed at that commit during the experiments. The repair revision is the commit that first adds this record; its source fingerprints bind the evaluated tree without a self-referential commit hash. Publication was authorized after the review closeout. The [source fingerprints](pr-2258-gc-retention.sources.json) bind this evidence to the evaluated repair files. + +## Laws, experiments, and inference + +Automatic GC must respect the last release of a subscriber, pending preload, or persisting transaction. Eligibility begins after `gcTime`; host idle delivery determines when eligible cleanup runs. Startup without subscribers and preload completion establish a 50ms absolute handoff deadline. A real last unsubscribe removes that handoff constraint. Pending manual transactions do not retain a Collection. The maintainer explicitly decided during this evaluation that rollback without entering `persisting` preserves the existing deadline. Explicit cleanup remains the caller's authority. + +Authority is `BaseCollectionConfig.gcTime`, the existing startup floor and preload contracts, and that explicit pending-rollback decision. The review correctly observed pending rollback refreshing retention, but its original claim of an already unambiguous public prohibition was too strong. The maintainer's decision resolves that policy question. + +| Items and law | Existing oracle's actual reach | RED on unchanged production | Repair and distinguishing neighboring history | +| --- | --- | --- | --- | +| R01: obsolete idle work cannot bypass a renewed deadline | `advance` drained both elapsed timers and the idle polyfill; no transaction could intervene | Queued idle work, insert, settle, deliver idle: retired at 121ms instead of remaining active until 221ms. Chromium also retired at native core idle at 101ms instead of waiting until 201ms | Renewal cancels old timer and idle work. Complete, reject, and explicit persisting rollback run; a surviving sibling still holds the Collection | +| R02: settlement preserves the residual startup horizon | `gcTime` was always 100ms, above the 50ms floor | `gcTime=1`, fast settlement, idle at 10ms: retired instead of active. Generated delayed commit/settlement shrank to seed `20261026`, path `3:0:0:0`, idle at 49ms. Chromium reproduced the 10ms violation | Store one absolute handoff deadline. Check immediately before and at independently calculated eligibility. After late settlement or a real unsubscribe, an always-50ms delay fails | +| R03: pending rollback preserves the old deadline | Every manual write was committed inside its insert action | Pending insert, rollback at 90ms, native/controlled usable idle at 110ms: still active instead of retired at the original 100ms deadline | Capture whether rollback left `persisting`; only actual hold release renews retention. Delayed commit, pending-alone expiry, failure and persisting rollback remain distinct | + +The oracle changes preceded production repairs. First the aggregate property and three reconstructions failed against unchanged production; the original two-Collection campaigns still passed. Then all three Chromium receivers failed at the intended public status comparisons. Proxy routing and sandbox port-binding failures were resolved before this gate; setup failures were not counted as RED. + +The primary GC owner now has a companion independent law/model/checker in `packages/db/tests/collection-gc-retention-oracle.ts`, a generated controlled driver in `collection-gc-retention-oracle.property.test.ts`, and a native driver in `packages/db/e2e/collection-gc/`. The original two-Collection property retains shared-transaction and restart coverage. The boundary model stores logical holds, last release, transaction phases, and an absolute handoff deadline. It imports no production scheduler, GC classifier, or transition helper. Production callbacks never compute expected deadlines. + +The controlled driver compares public `cleaned-up` status, active optimistic keys, and transaction phases after every legal action and microtask flush. It separates elapsed timer dispatch from idle delivery, including zero-budget and timed-out callbacks. Chromium saves native idle functions before Playwright installs elapsed clocks. It records the native budget core actually reads, checks after core callback delivery, and waits until outstanding native cleanup callbacks have delivered or been cancelled before a separate usable native observation. It shares the same expected-result computation. This establishes a native host boundary, not a real Electric provider boundary or a wall-clock latency guarantee. + +## Grammar and declared reach + +The boundary grammar varies release cause, `gcTime` in 1/10/49/50/51/100ms, age 0–80ms, complete/fail/rollback, a persisting sibling, zero to two no-budget idle periods, timeout delivery, eager versus write-started sync, pending rollback, delayed commit, and preload readiness. Disabled values include 0, -1, Infinity, -Infinity, and NaN. Original histories use two Collections, 1–14 commands, 20/60/120ms steps, and cleanup or success/failure drain. + +Reconstruction uses the same model, driver, and checker for all three reported traces. Generated lanes reach the same violations through the new idle/phase/startup dimensions. Ablation: removing delayed idle loses R01; removing low delays or handoff age loses R02; removing pending/commit/rollback loses R03; removing siblings loses non-last release; removing real unsubscribe loses the distinguishing no-floor regime; removing preload loses a separate handoff owner; removing deadline-adjacent cuts admits unnecessary delay. The direct-write axis distinguishes write-started sync from an already eager sync. Disabled and idle-budget axes preserve neighboring GC and host contracts. Removing shared Collection writes from the original property loses retention for another touched Collection. + +A nearby invalid history, settling a transaction before commit, is rejected by `gcActionLegal`; so are committing a settled transaction and unsubscribing a missing owner. Actions after retirement are excluded from the boundary lane; restart is exercised by the original property. This bounded grammar does not enumerate every legal permutation. + +Each campaign asserts minimum reach of one history per declared state. Final normal run counts, fixed/random: + +| Boundary state | Fixed 20261026 | Random | +| --- | ---: | ---: | +| queued-idle-renewal | 19 | 20 | +| startup-release-before-floor | 18 | 15 | +| last-unsubscribe-short | 9 | 7 | +| pending-rollback | 12 | 16 | +| pending-does-not-hold | 20 | 24 | +| sibling-persisting | 55 | 51 | +| preload-last-release | 22 | 15 | +| disabled-gc | 18 | 24 | +| delayed-commit | 25 | 19 | +| write-started-sync | 7 | 8 | +| late-release-short | 29 | 25 | + +| Original property state | Fixed 20261024 | Random | +| --- | ---: | ---: | +| gc-due-while-persisting | 16 | 21 | +| retired-after-completion | 16 | 23 | +| retired-after-failure | 26 | 22 | +| retired-after-shared-settlement | 20 | 25 | +| settled-while-subscribed | 10 | 8 | +| cleanup-while-persisting | 13 | 12 | +| restarted-after-retirement | 13 | 8 | + +Budgets are 150 histories per boundary campaign and 100 per original campaign. Random counts describe this execution, not a permanent distribution guarantee. Direct replay selects one campaign; both property IDs emitted exactly one `numRuns:1` witness for path `0`. The guarded runner also replayed `20261026 / 3:0:0:0` once: RED with the startup-floor mutant, GREEN after restoring the repair. Nonselected reconstructions and harness calibrations stand aside only during explicit replay. + +## Calibration and verification + +All six production mutants failed at intended public-status comparisons, not by timeout or setup failure. Production files were restored byte-for-byte after each: + +| Wrong implementation | Rejected observation | +| --- | --- | +| Renew timer without cancelling old idle work | Premature retirement at stale idle delivery | +| Always impose 50ms after settlement | Missing retirement at the independently calculated deadline | +| Drop the startup floor | Retirement before the absolute startup horizon | +| Refresh on every failed transaction, including pending rollback | Missing retirement at the original deadline | +| Renew only after success | Failure/rollback retirement fails the renewed deadline law | +| Remove the persisting hold and settlement renewal | Original property: retirement while persisting; fixed seed 20261024, path `2:1:0:3:1:4` | + +The cleanup calibration injects a primary checkpoint failure and a secondary adapter-cleanup failure. It proves every cleanup runs and that `AggregateError.cause` and `errors` preserve the primary and secondary diagnostics. This repairs a harness-integrity risk; no corresponding production defect was claimed. + +Executed GREEN checks: + +- Core suite: 287 files, 13,798 tests, no configured type errors. Final harness-only edits were followed by the two GC files: 8 tests, no type errors. +- Native Chromium: all three receiving histories pass, with request/delivery/cancellation premise checks. The existing browser E2E job now runs this receiver and uploads its evidence directory. +- Electric adapter: 252 runtime/type checks pass after building its generated declarations. Initial runtime assertions passed but declaration resolution failed; that initial run was not counted as GREEN. +- React conformance: 83 checks pass. Offline transactions: 20 files, 301 tests pass. +- Full DB `tsc --noEmit`, changed-file oracle lint, focused ESLint, private-member cache check, and diff whitespace check pass. + +`pnpm size:compare --packages db --files` against merge-base `427a7c31e` on `origin/main`: DB +54B gzip/+128B brotli; typical app +58B gzip/+140B brotli. These are the entire PR plus repair deltas. Test/docs growth is excluded from shipped bytes. The repair adds 31,591 uncompressed bytes of oracle/test/driver source and 23,459 uncompressed bytes of documentation and changeset source relative to the reviewed head. The repair consolidates scheduling through the existing lifecycle entry points and shares the queue's elapsed clock, removing its per-instance clock field (24 fewer minified module bytes). Remaining growth pays for a distinguishable absolute handoff deadline and phase-specific release notification. Dropping that state or replacing it with a fresh 50ms delay fails calibration; no additional lifecycle state machine was added. + +## Lossless review ledger + +The task-local append-only intake retained each claim, severity, original location, and proposed fix before evaluation edits. This table records every source-order item from the raw review, including prose outside the numbered findings. + +| ID | Original claim and proposed action | Verdict and disposition | Durable destination | +| --- | --- | --- | --- | +| R01 | P2 stale idle at lifecycle.ts:334–336; cancel/invalidate old work | Reproduced in controlled and native drivers; fixed-now | Lifecycle renewal and shared GC boundary owner | +| R02 | P2 startup floor at same lines; preserve grace without imposing it after unsubscribe | Reproduced; fixed-now with residual absolute grace | Shared model, low-delay/age grammar, deadline cuts | +| R03 | P3 pending rollback at transactions.ts:713–719; distinguish real hold release | Timing change reproduced; original authority qualified; maintainer chose preserve deadline; fixed-now | Public gcTime prose, transaction notification, pending/delayed-commit lanes | +| R04 | P3 direct replay executes twice; select one case | Original witness had duplicate executions; fixed-now, each target now emits one witness | Registration and replay manifest | +| R05 | Missing head-bound ORC-012 record; preserve requirement/calibration evidence | Confirmed; fixed-now in this tracked-path record and source fingerprints, pending commit with repair | This record | +| R06 | Missing idle/startup/pending reconstruction and incomplete grammar controls | Confirmed; fixed-now with dimensions and explicit reconstruction/ablation/range/exclusion analysis | Primary boundary property and grammar section above | +| R07 | Finally can hide mismatch or stop release; improve failure fidelity | Valid conditional harness risk, not a reproduced product bug; fixed-now and calibrated | Existing failure-preserving utility, controlled/native cleanup | +| R08 | No distinguishing startup threshold witness | Confirmed; fixed-now with low/equal/high delays and before/at deadline cuts | Boundary grammar and always-50/no-floor mutants | +| R09 | No native idle or real Electric awaitTxId/GC receiver | Native part fixed; real Electric receiver remains confirmed-open as an evidence gap, not a known production counterexample | Coverage-map Collection lifecycle row; Electric E2E registration | +| R10 | Exact retirement timing overclaims real-host promise | Confirmed wording issue; fixed-now, separate eligibility from idle opportunity | Public prose and both oracle contracts | +| R11 | Model `pending` means pending handlers rather than transaction phase | Valid ambiguity; fixed-now: original uses `pendingHandlers`, shared model uses actual phases | Both oracle models | +| R12 | Size author-reported and broad suites unrun | Accurate original limit; fixed-now for size/core/React/Electric/offline checks; other framework/provider schedules remain outside this evidence | Verification section and coverage map | + +Loss audit: 12 raw items = 11 fixed-now + 1 confirmed-open. No raw item was merged away, refuted, deferred, or assigned an invented owner. R09 preserves its partial native repair and its remaining provider evidence separately. This is not permission to drop a promised integration path. It prevents claiming that this core/native experiment proves that provider path. + +Independent challenge additionally caught an invented native idle-budget premise and missing late-release, delayed-commit, and disabled-value grammar distinctions during test authoring. Those were corrected; no extra production counterexample was found. A final fresh challenge independently ran 12 retention/queue tests and found no additional production bug. + +## Rubric outcomes and limits + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Laws and authority traced above; pending rollback authority is an explicit decision | +| ORC-002 | Shared reducer derives eligibility from logical holds and elapsed law, without production semantic imports | +| ORC-003 | Opening law, adjacent model/grammar/driver prose, and shared checker remain executable documentation | +| ORC-004 | Reconstruction, axis ablation, bounded ranges, and illegal neighbors recorded above | +| ORC-005 | Real public Collection paths; status, row visibility and phase checks after every action; native delivery checks. No missing core input hypothesis identified; no exhaustive information-sufficiency proof claimed | +| ORC-006 | Six wrong designs rejected at status comparisons; guarded mutant replay preserves the same checkpoint | +| ORC-007 | Equal-budget fixed/random campaigns plus exactly-once seed/path replay; no fc.commands replay path is applicable | +| ORC-008 | Holds, phases, sibling identity and handoff horizon each change a legal next action or public idle observation; retired state excludes unsupported boundary-lane restart | +| ORC-009 | Canonical Collection/transaction/cleanup vocabulary; model handoff and elapsed fields explicitly local | +| ORC-010 | Failure-preserving teardown plus injected primary/secondary failure calibration; native callback failures retained and page errors checked | +| ORC-011 | Not triggered: no concrete shared semantic fault requiring a different reference formulation was identified | +| ORC-012 | This reviewed-head record, source fingerprints, law/history/path/checkpoint limits and loss audit | +| ORC-013 | Before/at threshold cuts reject always-50 and no-floor rules, including true unsubscribe and late release | +| ORC-014 | Native Chromium supplies the controlled host's idle premise through the same model/checker. Actual Electric provider transfer remains open in R09 | +| ORC-015 | All seven original and eleven companion declared states reached in both normal campaigns; minimum enforced | + +The new grammar does not generate warm-preload overlap, preload failure, commit after a pending transaction's Collection retired, or restoration of offline/paced mutations. Existing broader suites passing does not fill those exact GC history cells. The GC boundary owner is the receiving owner for preload/retirement history extensions; the Electric E2E registration is the receiving owner for real provider confirmation. No known reachable in-scope production counterexample remains in the exercised core/native domain. Finite campaigns and mutants are not universal closure proofs. + +A real Electric/Postgres confirmation across GC eligibility remains unverified. At evaluation time the default Electric health endpoint on localhost:3000 and Postgres listener on port 54321 were unavailable. No provider setup failure was counted as evidence. A future witness must hold genuine confirmation past eligibility while the mutation persists, then observe successful caller settlement, Collection status, and renewed eligibility. An ordinary adapter awaitTxId test is not that witness. + +Reviewer assessment: hire for substantive debugging. The two P2 findings were precise and experimentally supported, with high signal and useful test-gap diagnosis. The P3 observation was correct but initially stated stronger authority than the public prose supplied. Proposed repair directions were sound but needed residual-grace, idle-budget, late-release, and receiving-path analysis. The principal review was produced in this task; fresh independent challenges support its technical conclusions but do not make this an independently scored hiring trial. + +At review closeout, before publication was authorized, published PR readiness was **not ready**. The published head still has the three confirmed defects, despite all 26 reported CI checks succeeding. Repaired working-tree readiness: **unproven**. Local repairs and bounded oracle coverage are complete for the exercised core/native paths, but had not yet been committed or pushed; final pushed-head CI and the named Electric integration witness have not been verified. Review accounting is complete; provider evidence remains open. + +## Reproduction commands + +```sh +pnpm --dir packages/db exec vitest run tests/collection-gc-transaction-oracle.property.test.ts tests/collection-gc-retention-oracle.property.test.ts --coverage.enabled=false +pnpm --dir packages/db test:collection-gc:browser +TANSTACK_DB_ORACLE_PROPERTY=collection-lifecycle.gc-retention-boundaries TANSTACK_DB_ORACLE_SEED=20261026 TANSTACK_DB_ORACLE_PATH=3:0:0:0 pnpm --dir packages/db exec node --import tsx tests/oracle-replay.ts tests/collection-gc-retention-oracle.property.test.ts --coverage.enabled=false --typecheck.enabled=false +pnpm exec tsc --noEmit --project packages/db/tsconfig.json +pnpm lint:changed +pnpm size:compare --packages db --files +``` + +In proxy-managed environments, native tests need localhost excluded from the proxy. Test logs and the original review were retained in task-local `/private/tmp/pr-2258-review/`; verdict-critical results are summarized here so the versioned evidence does not depend on those temporary files. + + +## Second review follow-up, 2026-10-26 + +The [second review and enforcement record](pr-2258-gc-retention-round2.md) +supersedes this evaluation's open provider cell and narrower grammar limits. +The maintainer chose fixture-specific first-idle timing and caller subscription +ownership of pending draft visibility. Expanded exact-row and cleanup histories +found and repaired sibling-row resurrection during and after explicit cleanup. +Real Electric/Postgres confirmation now has four executed receiving histories +and a no-hold runtime mutant rejected at the shared comparison. The original +results and open verdicts above remain a record of the earlier evaluation; +current sources are bound by the manifest's appended round-2 entry. diff --git a/docs/contributing/oracle-reviews/pr-2258-gc-retention.sources.json b/docs/contributing/oracle-reviews/pr-2258-gc-retention.sources.json new file mode 100644 index 0000000000..10e9b45b2a --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-2258-gc-retention.sources.json @@ -0,0 +1,222 @@ +{ + "reviewedHead": "f19f4908a3c0b8a91a696a2a77076e91230e4205", + "repairState": "evaluated on reviewedHead plus the source contents fingerprinted here; repair revision is the commit that first adds this record", + "sha256": { + "packages/db/src/collection/cleanup-queue.ts": "f1873937d0ae8e5f53a2557b607720636d17dec7eac34f5480a5480feb071005", + "packages/db/src/collection/lifecycle.ts": "0bf12a87ba70ce8cbf6f420d82fed5aaf62e00398514bb873660f123ee463517", + "packages/db/src/transactions.ts": "5254e65c5b92bce077d8d70660827cee3c6cad3f309cc32d81593d26f60f2c09", + "packages/db/src/types.ts": "604617928eaa154942b7c1c6db3582479a650a86d7f8b13a79894215dbfa55e3", + "packages/db/mangle-cache.json": "62f59eb00d91ea76da4d4028468d9b04242a57d684233c97ff676bf3c843f51e", + "packages/db/tests/collection-gc-transaction-oracle.property.test.ts": "f99df298d6d5ed44696da4da36b6ec44fcb57c8879096a2f76bc0404697fadb0", + "packages/db/tests/collection-gc-retention-oracle.property.test.ts": "c2f36965c1ab81f405f4f6b619010c47ccc9ec438a46c8926d890a570317004a", + "packages/db/tests/collection-gc-retention-oracle.ts": "34be905d72bbdb68d7f37c43ec802f616b13f65d2ec4f903c07bb115959b603d", + "packages/db/tests/oracle-config.ts": "61d3a191a34a7886c7e83fddbfa94a6597e12f8d52d3adef9dcf8b145acdd7ee", + "packages/db/tests/oracle-replay-manifest.ts": "492b9545f6102c4fc4f315b2bee841edf3957c29f91f3a75c38c1fc75cae2039", + "packages/db/tests/utils.ts": "13d5356bd2cb15850ba62967705d0dbb826d42427cf750340a516edd0f78fe3a", + "packages/db/e2e/collection-gc/browser.ts": "3162b0e273002ae99512007d6ee73781d9fe5b1d12fde78ccd05a2aaf8dd82d7", + "packages/db/e2e/collection-gc/browser.html": "4aa79460e5be04a6c3f46355ca0514c82305d6c4983152cce61fd127afc5cae1", + "packages/db/e2e/collection-gc/retention.spec.ts": "e7db26fc26c573b74836ecc762d79278d8727922bfb63885360fb7b6c261d718", + "packages/db/playwright.collection-gc.config.ts": "2c2ac0d6c14e17ff1f401e5b6b6ca2f7d43c1e205950ba6a8837f546e8c2bf37", + "packages/db/package.json": "69bc01bcb55f9f27618fd65873f61eb63212d7f2270f549336f86d99fe9fc38c", + "packages/db/tsconfig.json": "b759b1caa96020777b7833f756a9591c65939c467c4cb10b55f307ba325d7538", + ".github/workflows/e2e-tests.yml": "26550f9c26fe312a240413a9e76e0f1ac19b2e28dd17493e78a44134e268bc88" + }, + "round2": { + "baselineHead": "f1a39978e2822a96e5add42136a88674c7bc5d17", + "repairState": "sources evaluated for the second review; repair revision is the commit that first adds pr-2258-gc-retention-round2.md", + "sha256": { + "packages/db/src/collection/cleanup-queue.ts": "f1873937d0ae8e5f53a2557b607720636d17dec7eac34f5480a5480feb071005", + "packages/db/src/collection/lifecycle.ts": "99e63bc8e950b0e775a7b98e09168dc30434087b116e5eae421f866d757836f0", + "packages/db/src/transactions.ts": "5254e65c5b92bce077d8d70660827cee3c6cad3f309cc32d81593d26f60f2c09", + "packages/db/src/types.ts": "3cfeea62080865a55e4d65a1a2b4859a9be96d6ea87fd1200f18e21ff6ee1035", + "packages/db/mangle-cache.json": "62f59eb00d91ea76da4d4028468d9b04242a57d684233c97ff676bf3c843f51e", + "packages/db/tests/collection-gc-transaction-oracle.property.test.ts": "1cbe3ced6cbacd0e6eb72086a8d1968d2462bd8ee20083f9e1838fe3739fd49d", + "packages/db/tests/collection-gc-retention-oracle.property.test.ts": "4f1a79905e60d9ebab685d848d8ed2686666f69050048075cca510f4cbae57c7", + "packages/db/tests/collection-gc-retention-oracle.ts": "f15a756e72c3bddeaba9987132a95ce44bd20af30e92df71005902f3852b0789", + "packages/db/tests/oracle-config.ts": "61d3a191a34a7886c7e83fddbfa94a6597e12f8d52d3adef9dcf8b145acdd7ee", + "packages/db/tests/oracle-replay-manifest.ts": "492b9545f6102c4fc4f315b2bee841edf3957c29f91f3a75c38c1fc75cae2039", + "packages/db/tests/utils.ts": "13d5356bd2cb15850ba62967705d0dbb826d42427cf750340a516edd0f78fe3a", + "packages/db/e2e/collection-gc/browser.ts": "3162b0e273002ae99512007d6ee73781d9fe5b1d12fde78ccd05a2aaf8dd82d7", + "packages/db/e2e/collection-gc/browser.html": "4aa79460e5be04a6c3f46355ca0514c82305d6c4983152cce61fd127afc5cae1", + "packages/db/e2e/collection-gc/retention.spec.ts": "e7db26fc26c573b74836ecc762d79278d8727922bfb63885360fb7b6c261d718", + "packages/db/playwright.collection-gc.config.ts": "2c2ac0d6c14e17ff1f401e5b6b6ca2f7d43c1e205950ba6a8837f546e8c2bf37", + "packages/db/package.json": "69bc01bcb55f9f27618fd65873f61eb63212d7f2270f549336f86d99fe9fc38c", + "packages/db/tsconfig.json": "b759b1caa96020777b7833f756a9591c65939c467c4cb10b55f307ba325d7538", + ".github/workflows/e2e-tests.yml": "26550f9c26fe312a240413a9e76e0f1ac19b2e28dd17493e78a44134e268bc88", + "packages/db/src/collection/state.ts": "d01ed44ee09c5e9b9193b09e127a388f344f8a95985821e778e4ef55805bf3fa", + "packages/db/tests/collection-gc-retention-oracle.test.ts": "bfa15c78d55b79b902a7ceebf6a2d27e7b332669fcdcf61cf7ff595c696bfef6", + "packages/electric-db-collection/e2e/collection-gc.e2e.test.ts": "d0d7694199cdf3c88efa9cfe658435fc143acc0df4d28e78c09714d121aaea97", + "docs/guides/mutations.md": "1cd095f487793c8b61add9745cf6d5014d784d3a7a74d6550cf60b780f8e8796" + } + }, + "round3": { + "baselineHead": "d0618586aa7d033b70de53f6e20598bb39b71147", + "repairState": "unchanged production plus oracle expansion; evaluated revision is the commit that first adds pr-2258-gc-retention-round3.md", + "sha256": { + "packages/db/src/collection/cleanup-queue.ts": "f1873937d0ae8e5f53a2557b607720636d17dec7eac34f5480a5480feb071005", + "packages/db/src/collection/lifecycle.ts": "99e63bc8e950b0e775a7b98e09168dc30434087b116e5eae421f866d757836f0", + "packages/db/src/transactions.ts": "5254e65c5b92bce077d8d70660827cee3c6cad3f309cc32d81593d26f60f2c09", + "packages/db/src/types.ts": "3cfeea62080865a55e4d65a1a2b4859a9be96d6ea87fd1200f18e21ff6ee1035", + "packages/db/mangle-cache.json": "62f59eb00d91ea76da4d4028468d9b04242a57d684233c97ff676bf3c843f51e", + "packages/db/tests/collection-gc-transaction-oracle.property.test.ts": "1cbe3ced6cbacd0e6eb72086a8d1968d2462bd8ee20083f9e1838fe3739fd49d", + "packages/db/tests/collection-gc-retention-oracle.property.test.ts": "b09bed5dfd40137b308051a1819440cff365ac75cd34e39abd3e606c21b4d928", + "packages/db/tests/collection-gc-retention-oracle.ts": "a7f576158d016f696f7b583332caa0fa0ffb9115a5c7edf55f4b1d6c74fc13b2", + "packages/db/tests/oracle-config.ts": "8944392764cf400fa9e7871b737fdd5ea0a2c73ce5351f7a2c171a78c72d4d85", + "packages/db/tests/oracle-replay-manifest.ts": "7f717acc5e83360ba2d2fadcf0ce0971e6dab79db2b633be5750fb309215b3a8", + "packages/db/tests/utils.ts": "13d5356bd2cb15850ba62967705d0dbb826d42427cf750340a516edd0f78fe3a", + "packages/db/e2e/collection-gc/browser.ts": "3162b0e273002ae99512007d6ee73781d9fe5b1d12fde78ccd05a2aaf8dd82d7", + "packages/db/e2e/collection-gc/browser.html": "4aa79460e5be04a6c3f46355ca0514c82305d6c4983152cce61fd127afc5cae1", + "packages/db/e2e/collection-gc/retention.spec.ts": "e7db26fc26c573b74836ecc762d79278d8727922bfb63885360fb7b6c261d718", + "packages/db/playwright.collection-gc.config.ts": "2c2ac0d6c14e17ff1f401e5b6b6ca2f7d43c1e205950ba6a8837f546e8c2bf37", + "packages/db/package.json": "a56bb3b720fafb8bd1e4a5f7a46d2ffa7093cd580ac964c29dcf4da4497eeefe", + "packages/db/tsconfig.json": "b759b1caa96020777b7833f756a9591c65939c467c4cb10b55f307ba325d7538", + ".github/workflows/e2e-tests.yml": "26550f9c26fe312a240413a9e76e0f1ac19b2e28dd17493e78a44134e268bc88", + "packages/db/src/collection/state.ts": "d01ed44ee09c5e9b9193b09e127a388f344f8a95985821e778e4ef55805bf3fa", + "packages/db/tests/collection-gc-retention-oracle.test.ts": "bfa15c78d55b79b902a7ceebf6a2d27e7b332669fcdcf61cf7ff595c696bfef6", + "packages/electric-db-collection/e2e/collection-gc.e2e.test.ts": "70d4a778568601211db3b26871bfe60799955f728ae0eab8c40220e2e17dc574", + "docs/guides/mutations.md": "1cd095f487793c8b61add9745cf6d5014d784d3a7a74d6550cf60b780f8e8796", + "packages/db/tests/collection-gc-mutation-oracle.property.test.ts": "0aef8529af0280cdc32dca50dc6cac6753850468a6193abe1d365b838806909e", + "packages/db/tests/transaction-ownership-oracle.property.test.ts": "623134bd0fcc80d62c5b8ea24518c434f784a8352ab8eba0ad508505ee794c72", + "packages/db/src/collection/changes.ts": "c6deb4b04891feec0f2c4368ffa9ea652d12593f4e7ddf4edbe392a706f314df" + } + }, + "sizeRefactor": { + "baselineHead": "baa7d7d730329fdbf1fab30cc25a0932abc757da", + "integratedMain": "45c33292b2c4f09a63f856a829c1e15ca2b470aa", + "parentHead": "b44df2f66c2ef5e5886e85a4104519ca1f870f5f", + "repairState": "unchanged laws and oracles with smaller production control flow; evaluated revision is the commit that first adds pr-2258-gc-size-refactor.md", + "sha256": { + "packages/db/src/collection/cleanup-queue.ts": "869a5fdff4680354a9b3c68d3e5228ffe701507968dd1a5ae2123ed856fe517e", + "packages/db/src/collection/lifecycle.ts": "4327d9fbed4a8c095bc32351d66876b304162bdcfcbc4b2f968b84795d0b0b80", + "packages/db/src/transactions.ts": "902ced26704bc6c93f130d6ed89496a72a1a16095f949173086f6ed1cb83ab1e", + "packages/db/src/types.ts": "3cfeea62080865a55e4d65a1a2b4859a9be96d6ea87fd1200f18e21ff6ee1035", + "packages/db/mangle-cache.json": "ae56072590102d26461f5c19de64e9873597bdd3fff35a9ebe2118fce7c4f10d", + "packages/db/tests/collection-gc-transaction-oracle.property.test.ts": "1cbe3ced6cbacd0e6eb72086a8d1968d2462bd8ee20083f9e1838fe3739fd49d", + "packages/db/tests/collection-gc-retention-oracle.property.test.ts": "b09bed5dfd40137b308051a1819440cff365ac75cd34e39abd3e606c21b4d928", + "packages/db/tests/collection-gc-retention-oracle.ts": "a7f576158d016f696f7b583332caa0fa0ffb9115a5c7edf55f4b1d6c74fc13b2", + "packages/db/tests/oracle-config.ts": "410da5e586bdcd103906f70e4ab8114e970cf94ee83055487a9ea5e245772168", + "packages/db/tests/oracle-replay-manifest.ts": "7f717acc5e83360ba2d2fadcf0ce0971e6dab79db2b633be5750fb309215b3a8", + "packages/db/tests/utils.ts": "13d5356bd2cb15850ba62967705d0dbb826d42427cf750340a516edd0f78fe3a", + "packages/db/e2e/collection-gc/browser.ts": "3162b0e273002ae99512007d6ee73781d9fe5b1d12fde78ccd05a2aaf8dd82d7", + "packages/db/e2e/collection-gc/browser.html": "4aa79460e5be04a6c3f46355ca0514c82305d6c4983152cce61fd127afc5cae1", + "packages/db/e2e/collection-gc/retention.spec.ts": "e7db26fc26c573b74836ecc762d79278d8727922bfb63885360fb7b6c261d718", + "packages/db/playwright.collection-gc.config.ts": "2c2ac0d6c14e17ff1f401e5b6b6ca2f7d43c1e205950ba6a8837f546e8c2bf37", + "packages/db/package.json": "a56bb3b720fafb8bd1e4a5f7a46d2ffa7093cd580ac964c29dcf4da4497eeefe", + "packages/db/tsconfig.json": "b759b1caa96020777b7833f756a9591c65939c467c4cb10b55f307ba325d7538", + ".github/workflows/e2e-tests.yml": "26550f9c26fe312a240413a9e76e0f1ac19b2e28dd17493e78a44134e268bc88", + "packages/db/src/collection/state.ts": "01e98385294290ad6f3f228893f4ba823f932219b852632b1cecc5a660fbb7fd", + "packages/db/tests/collection-gc-retention-oracle.test.ts": "bfa15c78d55b79b902a7ceebf6a2d27e7b332669fcdcf61cf7ff595c696bfef6", + "packages/electric-db-collection/e2e/collection-gc.e2e.test.ts": "70d4a778568601211db3b26871bfe60799955f728ae0eab8c40220e2e17dc574", + "docs/guides/mutations.md": "1cd095f487793c8b61add9745cf6d5014d784d3a7a74d6550cf60b780f8e8796", + "packages/db/tests/collection-gc-mutation-oracle.property.test.ts": "0aef8529af0280cdc32dca50dc6cac6753850468a6193abe1d365b838806909e", + "packages/db/tests/transaction-ownership-oracle.property.test.ts": "623134bd0fcc80d62c5b8ea24518c434f784a8352ab8eba0ad508505ee794c72", + "packages/db/src/collection/changes.ts": "c6deb4b04891feec0f2c4368ffa9ea652d12593f4e7ddf4edbe392a706f314df", + "docs/contributing/oracle-reviews/pr-2258-gc-size-refactor.md": "e8f8c9fa953e85b1991434fc869e9e5327171a223aed478d0281054fb3a323ca" + } + }, + "sizeIntegration": { + "baselineHead": "ca97e53feec8000d78a70c997aea028312806596", + "integratedMain": "6c1ca8f8f9efe437d3cea61b5b9d0a086e2a5e34", + "parentHead": "80159c9ecc3f5fd310997710f8a87a47ae0d0a37", + "repairState": "unchanged laws and oracles after main integration with shared current/prior state reads; evaluated revision is the commit that first adds pr-2258-gc-size-integration.md", + "sha256": { + "packages/db/src/collection/cleanup-queue.ts": "869a5fdff4680354a9b3c68d3e5228ffe701507968dd1a5ae2123ed856fe517e", + "packages/db/src/collection/lifecycle.ts": "4327d9fbed4a8c095bc32351d66876b304162bdcfcbc4b2f968b84795d0b0b80", + "packages/db/src/transactions.ts": "32fc7e6ae58ecabc13c20f10f0cefa5e1b5ce887b3a5a3e94fd3b1dd9590c7f9", + "packages/db/src/types.ts": "3cfeea62080865a55e4d65a1a2b4859a9be96d6ea87fd1200f18e21ff6ee1035", + "packages/db/mangle-cache.json": "bbf8e5cf2a1c3f099a6dc9713603aa02c626904c6224eb346ffe0848fca0aaa9", + "packages/db/tests/collection-gc-transaction-oracle.property.test.ts": "1cbe3ced6cbacd0e6eb72086a8d1968d2462bd8ee20083f9e1838fe3739fd49d", + "packages/db/tests/collection-gc-retention-oracle.property.test.ts": "b09bed5dfd40137b308051a1819440cff365ac75cd34e39abd3e606c21b4d928", + "packages/db/tests/collection-gc-retention-oracle.ts": "a7f576158d016f696f7b583332caa0fa0ffb9115a5c7edf55f4b1d6c74fc13b2", + "packages/db/tests/oracle-config.ts": "410da5e586bdcd103906f70e4ab8114e970cf94ee83055487a9ea5e245772168", + "packages/db/tests/oracle-replay-manifest.ts": "7f717acc5e83360ba2d2fadcf0ce0971e6dab79db2b633be5750fb309215b3a8", + "packages/db/tests/utils.ts": "13d5356bd2cb15850ba62967705d0dbb826d42427cf750340a516edd0f78fe3a", + "packages/db/e2e/collection-gc/browser.ts": "3162b0e273002ae99512007d6ee73781d9fe5b1d12fde78ccd05a2aaf8dd82d7", + "packages/db/e2e/collection-gc/browser.html": "4aa79460e5be04a6c3f46355ca0514c82305d6c4983152cce61fd127afc5cae1", + "packages/db/e2e/collection-gc/retention.spec.ts": "e7db26fc26c573b74836ecc762d79278d8727922bfb63885360fb7b6c261d718", + "packages/db/playwright.collection-gc.config.ts": "2c2ac0d6c14e17ff1f401e5b6b6ca2f7d43c1e205950ba6a8837f546e8c2bf37", + "packages/db/package.json": "a56bb3b720fafb8bd1e4a5f7a46d2ffa7093cd580ac964c29dcf4da4497eeefe", + "packages/db/tsconfig.json": "b759b1caa96020777b7833f756a9591c65939c467c4cb10b55f307ba325d7538", + ".github/workflows/e2e-tests.yml": "26550f9c26fe312a240413a9e76e0f1ac19b2e28dd17493e78a44134e268bc88", + "packages/db/src/collection/state.ts": "6ee794b9026bc8805e5a516c004b97e6d7f170178809cbb89e8d42e567c29b3d", + "packages/db/tests/collection-gc-retention-oracle.test.ts": "bfa15c78d55b79b902a7ceebf6a2d27e7b332669fcdcf61cf7ff595c696bfef6", + "packages/electric-db-collection/e2e/collection-gc.e2e.test.ts": "70d4a778568601211db3b26871bfe60799955f728ae0eab8c40220e2e17dc574", + "docs/guides/mutations.md": "1cd095f487793c8b61add9745cf6d5014d784d3a7a74d6550cf60b780f8e8796", + "packages/db/tests/collection-gc-mutation-oracle.property.test.ts": "0aef8529af0280cdc32dca50dc6cac6753850468a6193abe1d365b838806909e", + "packages/db/tests/transaction-ownership-oracle.property.test.ts": "623134bd0fcc80d62c5b8ea24518c434f784a8352ab8eba0ad508505ee794c72", + "packages/db/src/collection/changes.ts": "c6deb4b04891feec0f2c4368ffa9ea652d12593f4e7ddf4edbe392a706f314df", + "docs/contributing/oracle-reviews/pr-2258-gc-size-refactor.md": "e8f8c9fa953e85b1991434fc869e9e5327171a223aed478d0281054fb3a323ca", + "docs/contributing/oracle-reviews/pr-2258-gc-size-integration.md": "1254f5770df25ab1e1c155755e8cfacc95454899b6b523437a81856fa7002a54" + } + }, + "sizeCiFollowup": { + "parentHead": "a8b047cb5d88ba43c850494a3a84d4996135885d", + "integratedMain": "6c1ca8f8f9efe437d3cea61b5b9d0a086e2a5e34", + "repairState": "preserve frozen development error template and delete an obsolete documentation suppression; runtime behavior and measured size unchanged; evaluated revision is the commit that first adds sizeCiFollowup", + "sha256": { + "packages/db/src/collection/cleanup-queue.ts": "869a5fdff4680354a9b3c68d3e5228ffe701507968dd1a5ae2123ed856fe517e", + "packages/db/src/collection/lifecycle.ts": "4327d9fbed4a8c095bc32351d66876b304162bdcfcbc4b2f968b84795d0b0b80", + "packages/db/src/transactions.ts": "aa41ad08a2df85e719e37396272b00c1b3a7d77236834f421e1883abc98530fb", + "packages/db/src/types.ts": "3cfeea62080865a55e4d65a1a2b4859a9be96d6ea87fd1200f18e21ff6ee1035", + "packages/db/mangle-cache.json": "bbf8e5cf2a1c3f099a6dc9713603aa02c626904c6224eb346ffe0848fca0aaa9", + "packages/db/tests/collection-gc-transaction-oracle.property.test.ts": "1cbe3ced6cbacd0e6eb72086a8d1968d2462bd8ee20083f9e1838fe3739fd49d", + "packages/db/tests/collection-gc-retention-oracle.property.test.ts": "b09bed5dfd40137b308051a1819440cff365ac75cd34e39abd3e606c21b4d928", + "packages/db/tests/collection-gc-retention-oracle.ts": "a7f576158d016f696f7b583332caa0fa0ffb9115a5c7edf55f4b1d6c74fc13b2", + "packages/db/tests/oracle-config.ts": "410da5e586bdcd103906f70e4ab8114e970cf94ee83055487a9ea5e245772168", + "packages/db/tests/oracle-replay-manifest.ts": "7f717acc5e83360ba2d2fadcf0ce0971e6dab79db2b633be5750fb309215b3a8", + "packages/db/tests/utils.ts": "13d5356bd2cb15850ba62967705d0dbb826d42427cf750340a516edd0f78fe3a", + "packages/db/e2e/collection-gc/browser.ts": "3162b0e273002ae99512007d6ee73781d9fe5b1d12fde78ccd05a2aaf8dd82d7", + "packages/db/e2e/collection-gc/browser.html": "4aa79460e5be04a6c3f46355ca0514c82305d6c4983152cce61fd127afc5cae1", + "packages/db/e2e/collection-gc/retention.spec.ts": "e7db26fc26c573b74836ecc762d79278d8727922bfb63885360fb7b6c261d718", + "packages/db/playwright.collection-gc.config.ts": "2c2ac0d6c14e17ff1f401e5b6b6ca2f7d43c1e205950ba6a8837f546e8c2bf37", + "packages/db/package.json": "a56bb3b720fafb8bd1e4a5f7a46d2ffa7093cd580ac964c29dcf4da4497eeefe", + "packages/db/tsconfig.json": "b759b1caa96020777b7833f756a9591c65939c467c4cb10b55f307ba325d7538", + ".github/workflows/e2e-tests.yml": "26550f9c26fe312a240413a9e76e0f1ac19b2e28dd17493e78a44134e268bc88", + "packages/db/src/collection/state.ts": "6ee794b9026bc8805e5a516c004b97e6d7f170178809cbb89e8d42e567c29b3d", + "packages/db/tests/collection-gc-retention-oracle.test.ts": "bfa15c78d55b79b902a7ceebf6a2d27e7b332669fcdcf61cf7ff595c696bfef6", + "packages/electric-db-collection/e2e/collection-gc.e2e.test.ts": "70d4a778568601211db3b26871bfe60799955f728ae0eab8c40220e2e17dc574", + "docs/guides/mutations.md": "1cd095f487793c8b61add9745cf6d5014d784d3a7a74d6550cf60b780f8e8796", + "packages/db/tests/collection-gc-mutation-oracle.property.test.ts": "0aef8529af0280cdc32dca50dc6cac6753850468a6193abe1d365b838806909e", + "packages/db/tests/transaction-ownership-oracle.property.test.ts": "623134bd0fcc80d62c5b8ea24518c434f784a8352ab8eba0ad508505ee794c72", + "packages/db/src/collection/changes.ts": "c6deb4b04891feec0f2c4368ffa9ea652d12593f4e7ddf4edbe392a706f314df", + "docs/contributing/oracle-reviews/pr-2258-gc-size-refactor.md": "e8f8c9fa953e85b1991434fc869e9e5327171a223aed478d0281054fb3a323ca", + "docs/contributing/oracle-reviews/pr-2258-gc-size-integration.md": "37102a2160045a39cce984a5a158f410fd207ae21a8e27cc3acb6d565bd85f6e", + "scripts/doc-snippets-baseline.json": "7a32c84483aff8d42d847f7273543016c898f719d92ab3c479fceb902ad9f105" + } + }, + "main164Integration": { + "parentHead": "bb188e6eff58d850091766e930aa2bb2e4978772", + "integratedMain": "1640de834bca0588f91432ea80a0f4569157e9dc", + "repairState": "normal merge; retain both replay registries and regenerate private-member cache; unchanged GC laws and comparison; evaluated revision is the merge commit that first adds main164Integration", + "sha256": { + "packages/db/src/collection/cleanup-queue.ts": "869a5fdff4680354a9b3c68d3e5228ffe701507968dd1a5ae2123ed856fe517e", + "packages/db/src/collection/lifecycle.ts": "dca3224bc3dca98dedca96be385826720a475ff781d30483a301d8cbd7438bfe", + "packages/db/src/transactions.ts": "aa41ad08a2df85e719e37396272b00c1b3a7d77236834f421e1883abc98530fb", + "packages/db/src/types.ts": "3cfeea62080865a55e4d65a1a2b4859a9be96d6ea87fd1200f18e21ff6ee1035", + "packages/db/mangle-cache.json": "eab558c7f590c3d935c3859ea0f13682835c5ffe119de9e7135fbb19c24290d2", + "packages/db/tests/collection-gc-transaction-oracle.property.test.ts": "1cbe3ced6cbacd0e6eb72086a8d1968d2462bd8ee20083f9e1838fe3739fd49d", + "packages/db/tests/collection-gc-retention-oracle.property.test.ts": "b09bed5dfd40137b308051a1819440cff365ac75cd34e39abd3e606c21b4d928", + "packages/db/tests/collection-gc-retention-oracle.ts": "a7f576158d016f696f7b583332caa0fa0ffb9115a5c7edf55f4b1d6c74fc13b2", + "packages/db/tests/oracle-config.ts": "93130fc51c31c07e445737da360c9b9bb9891a88b80b6dae234315527377ad60", + "packages/db/tests/oracle-replay-manifest.ts": "8255d8b8ea61ceabdf652528ebca8a6abbeccfe642edb4786f46b980bf3cda72", + "packages/db/tests/utils.ts": "13d5356bd2cb15850ba62967705d0dbb826d42427cf750340a516edd0f78fe3a", + "packages/db/e2e/collection-gc/browser.ts": "3162b0e273002ae99512007d6ee73781d9fe5b1d12fde78ccd05a2aaf8dd82d7", + "packages/db/e2e/collection-gc/browser.html": "4aa79460e5be04a6c3f46355ca0514c82305d6c4983152cce61fd127afc5cae1", + "packages/db/e2e/collection-gc/retention.spec.ts": "e7db26fc26c573b74836ecc762d79278d8727922bfb63885360fb7b6c261d718", + "packages/db/playwright.collection-gc.config.ts": "2c2ac0d6c14e17ff1f401e5b6b6ca2f7d43c1e205950ba6a8837f546e8c2bf37", + "packages/db/package.json": "a56bb3b720fafb8bd1e4a5f7a46d2ffa7093cd580ac964c29dcf4da4497eeefe", + "packages/db/tsconfig.json": "b759b1caa96020777b7833f756a9591c65939c467c4cb10b55f307ba325d7538", + ".github/workflows/e2e-tests.yml": "26550f9c26fe312a240413a9e76e0f1ac19b2e28dd17493e78a44134e268bc88", + "packages/db/src/collection/state.ts": "6ee794b9026bc8805e5a516c004b97e6d7f170178809cbb89e8d42e567c29b3d", + "packages/db/tests/collection-gc-retention-oracle.test.ts": "bfa15c78d55b79b902a7ceebf6a2d27e7b332669fcdcf61cf7ff595c696bfef6", + "packages/electric-db-collection/e2e/collection-gc.e2e.test.ts": "70d4a778568601211db3b26871bfe60799955f728ae0eab8c40220e2e17dc574", + "docs/guides/mutations.md": "1cd095f487793c8b61add9745cf6d5014d784d3a7a74d6550cf60b780f8e8796", + "packages/db/tests/collection-gc-mutation-oracle.property.test.ts": "0aef8529af0280cdc32dca50dc6cac6753850468a6193abe1d365b838806909e", + "packages/db/tests/transaction-ownership-oracle.property.test.ts": "623134bd0fcc80d62c5b8ea24518c434f784a8352ab8eba0ad508505ee794c72", + "packages/db/src/collection/changes.ts": "0c07c750b94a97773bd707975a7ec7a41959ca7271112c4c0efeb79763a52b70", + "docs/contributing/oracle-reviews/pr-2258-gc-size-refactor.md": "e8f8c9fa953e85b1991434fc869e9e5327171a223aed478d0281054fb3a323ca", + "docs/contributing/oracle-reviews/pr-2258-gc-size-integration.md": "12bf83c26df31f50c91a734317219b948bdda8ee5b82fc78c5aaa1d76b932800", + "scripts/doc-snippets-baseline.json": "7a32c84483aff8d42d847f7273543016c898f719d92ab3c479fceb902ad9f105" + } + } +} diff --git a/docs/contributing/oracle-reviews/pr-2258-gc-size-integration.md b/docs/contributing/oracle-reviews/pr-2258-gc-size-integration.md new file mode 100644 index 0000000000..23e13a33b7 --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-2258-gc-size-integration.md @@ -0,0 +1,155 @@ +# GC retention: size after main integration + +This record supplements [the earlier size refactor](pr-2258-gc-size-refactor.md). +Its measurements remain historical. This record evaluates the complete PR +against `origin/main` at merge-base +`6c1ca8f8f9efe437d3cea61b5b9d0a086e2a5e34`, after a normal merge at +`80159c9ec`. The evaluated revision is the commit that first adds this record. +`sizeIntegration.sha256` in [the source record](pr-2258-gc-retention.sources.json) +binds its production sources and unchanged oracle drivers. + +## Preserved behavior and simpler state reads + +The final integration adds no product law, oracle rule, history, assertion or +run budget. The original GC defect, authority, RED/GREEN evidence and limits +remain in the retention reviews. This is an internal refactor, so the initial +GREEN implementation is not evidence of another product defect. + +Current and prior optimistic layers now share one keyed-row lookup. Current +and prior virtual-property snapshots share one computation, replacing separate +sync-status and attribution helpers and an override wrapper. Every supplied +prior state contains its three required maps, while current snapshots default +to the state manager. Local-only rows remain local and synced. Existing virtual +fields on upstream rows still take precedence during enrichment. Prior rows +are enriched before current rows so the cache retains the published value. +Collection identity compares directly, and mutation application reads each +incoming global key once. Removed private members leave the mangle cache. + +The preceding refactor also shares transaction completion, mutation merging, +GC renewal, idle cleanup and queue dispatch, and prunes settled transactions +with deferred sorted-map deletion. These reductions pay for persisting holds +without changing cleanup error identity, settlement order, handoff deadlines, +last-unsubscribe delay or pending-rollback deadlines. + +## Executed evidence + +The final source passes **636 tests across 18 files**, with no type errors. +This retains the earlier 608-test selection and adds the row-metadata +composition, metadata publication and virtual-row-field owners. It covers +GC, mutation admission, ownership, queue behavior, cleanup/restart, optimistic +payloads, composition, outcomes, publication and settlement boundaries. +The fixed and random GC campaigns retain all declared reach requirements. + +A hostile mutant discards supplied prior maps by assigning `state = this` at +snapshot creation. The existing publication oracle rejects it at the raw +subscriber's prior-row semantics, and the metadata publication owner rejects +its callback event batches. The four-file run reports 41 failures and 35 +passes. Six of the failures are checker-calibration prerequisites and are not +independent mutant kills. The metadata property shrinks to seed `1805001`, +path `1:2:2:2:3:3`: two same-key committed edits with metadata set then delete. +These are assertion failures, not setup errors or timeouts. Row-metadata +composition and virtual-row-field owners pass under this mutant and receive +no kill credit. The exact source bytes are restored, then all **76 tests in +these four files pass**. The preceding ordinary-deletion mutant remains in +the earlier record. + +Native Chromium passes **3/3 histories**. Real Electric/Postgres passes +**12/12 paths** using the freshly built DB package, including both sibling +settlement orders and a SQL-open survivor beyond the first-release deadline. +These remain separate host and provider witnesses. They do not establish a +combined browser/provider schedule or expand the earlier bounded claims. +DB source/test TypeScript, focused ESLint, Prettier, private-member cache and +whitespace checks pass. Logs remain under `/private/tmp/pr-2258-size`, with +`final18-` prefixes, `candidate18-size.log`, `prior-state-mutant-red.log` and +`prior-state-restored-green.log`. + +## Whole-PR shipped bytes + +`pnpm size:compare --packages db --files` uses the normal production minifier, +app scenarios and compression settings against merge-base `6c1ca8f8f`. +Every measured output decreases in both formats: + +| Production output | gzip delta | Brotli delta | +| --- | ---: | ---: | +| DB bundle | -180 B | -165 B | +| Typical app | -173 B | -59 B | +| App with Effect | -156 B | -121 B | +| App with groupBy | -137 B | -246 B | +| App with includes | -172 B | -178 B | +| App with join | -171 B | -98 B | +| App with LocalStorage | -169 B | -186 B | +| App with optimistic action | -155 B | -64 B | +| App with paced mutations | -174 B | -164 B | + +No names, comments, formatting, compression settings, scenario definitions or +minifier options change to obtain this result. Tests and contract documentation +are separate from these shipped-byte measurements. This integration changes +no test expectations and adds only this evidence and source binding. + +The local-only install workaround remains uncommitted: +`baseline-browser-mapping@2.11.32` returns HTTP 403, `2.11.31` is unavailable, +and `2.11.30` installs. Both comparison trees use that override. Existing +overrides remain intact; neither workspace nor lockfile changes enter the PR. + +## CI follow-up + +CI on `a8b047cb5` passes every completed runtime shard except the frozen +error-site source-template check. That check records the interpolation +identifier as well as the message literals. Restore its original `_exhaustive` +identifier without changing the runtime message, error code or diagnostic +parameter key. All **19 production-error oracle tests pass** without changing +an expectation. A fresh size comparison retains every byte delta above. + +The documentation snippet check also identifies one obsolete suppression: +`packages/db/skills/db-core/persistence/SKILL.md#81435553635b5b53` now compiles. +Delete only that baseline entry. The complete check then retains the other +279 known failures across 757 examples, with no new failure or stale entry. +No documentation example or oracle expectation is weakened. The final source +binding is `sizeCiFollowup.sha256`; earlier bindings retain their historical +commit meanings. + + +## Main integration at 1640de834 + +A normal merge integrates main `1640de834bca0588f91432ea80a0f4569157e9dc` +with published head `bb188e6eff58d850091766e930aa2bb2e4978772`. +The only textual conflicts are the private-member cache and replay selectors. +The replay registry retains both branches' entries. The official mangle +writer starts from main's assignments, drops removed private helpers and +assigns `gcGraceUntil` the free name `aP`. Main's lifecycle change resets the +cleanup promise only when a replacement run actually enters `loading`. +The GC retention refactors remain intact. No model, history, comparison, +expected result or run budget changes in this resolution. + +The fresh production comparison against this main revision reports: + +| Production output | gzip delta | Brotli delta | +| --- | ---: | ---: | +| DB bundle | -170 B | -39 B | +| Typical app | -182 B | -72 B | +| App with Effect | -159 B | -200 B | +| App with groupBy | -129 B | -130 B | +| App with includes | -169 B | -297 B | +| App with join | -160 B | -124 B | +| App with LocalStorage | -182 B | -198 B | +| App with optimistic action | -169 B | -183 B | +| App with paced mutations | -172 B | -176 B | + +This measurement uses `pnpm size:compare --packages db --files --exact-base` +before the merge commit, so its baseline is the fetched main revision rather +than the older HEAD merge-base. The temporary baseline uses the same local +`baseline-browser-mapping@2.11.30` override as the candidate. The override and +lockfile remain uncommitted. + +Native Chromium passes 3/3 histories and Electric/Postgres passes 12/12 paths +on the merged production sources. DB source and test TypeScript, changed-file +lint, mangle validation, conflict-file formatting and whitespace checks pass. +The replay manifest's three registry checks pass. The first expanded test run +finishes its 21 core files, but its additional guarded replay subprocess +campaign stalls and is interrupted. That run is not credited as a clean GREEN. +A separate core run supplies the final suite result below. Logs use the +`main164-` prefix under `/private/tmp/pr-2258-size`. + +The separate core run passes **1,033 tests across 21 files**, with no type +errors. It includes the updated cleanup/restart and subscription-lifecycle +owners from main. The source binding is `main164Integration.sha256`. diff --git a/docs/contributing/oracle-reviews/pr-2258-gc-size-refactor.md b/docs/contributing/oracle-reviews/pr-2258-gc-size-refactor.md new file mode 100644 index 0000000000..94a353d219 --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-2258-gc-size-refactor.md @@ -0,0 +1,128 @@ +# GC retention: production size refactor + +This append-only record supplements the three GC retention reviews. It evaluates +an internal refactor of the published `baa7d7d730329fdbf1fab30cc25a0932abc757da` +implementation, after a normal merge of `origin/main` at +`45c33292b2c4f09a63f856a829c1e15ca2b470aa`. The evaluated sources are bound by +`sizeRefactor.sha256` in [the source record](pr-2258-gc-retention.sources.json). +The commit that first adds this record is the evaluated revision. Earlier source +bindings and experiments remain historical records. + +## Preserved laws and smaller machinery + +No oracle model, history grammar, expected result, run budget, or public contract +changes in this refactor. The original violated GC law and authority, original +production RED, and hostile controls remain in the earlier records. The size +request does not identify another product defect: the starting implementation +passes the focused behavioral checks but exceeds the requested shipped weight. + +The governing laws remain persisting-transaction retention, renewal after the +last hold, absolute startup/preload handoff, exact last-unsubscribe delay, +pending-rollback deadline preservation, explicit-cleanup precedence, and no +restoration of discarded optimistic rows. The optimistic transaction owners +also require the same net mutation payload, original snapshots, metadata, +settlement outcome and public rows. Cleanup retains its three observation cuts +and ordered error diagnostics, including an error whose value is undefined. + +The refactor removes duplicated control flow and temporary structures: + +- Empty and nonempty transaction commits share completion and publication. + Empty commits still complete synchronously without invoking the handler. +- Mutation merging dispatches on the incoming operation without allocating a + composite operation string on supported paths. Insert/update and update/update + share field and metadata merging while preserving their distinct envelopes. + Applying mutations shares one insert-or-replace path. +- The GC timer has one renewal entry point. Settlement preserves remaining + handoff time, while last unsubscribe clears it. Explicit cleanup reuses timer + cancellation. Automatic cleanup already consumes its queue entry. +- Idle delivery calls cleanup directly after the same eligibility and budget + checks. The cleanup promise owns reentrant callers, and sync cleanup selects + exactly one synchronous or asynchronous finish path, so no additional finished + flag is needed. +- The shared timeout executes due queue work directly, without a single-caller + dispatch method or a second empty-queue guard. +- Size calculation counts directly instead of allocating filtered arrays. + Collection iteration delegates to its existing entries iterator. Settled + transaction pruning uses the existing sorted map's deferred-delete operation + instead of a scratch list and a second pass. + +Deferred deletion is essential. Ordinary deletion splices the sorted key array +while it is being iterated and skips the following transaction. The overlay loop +has no callback or ordered transaction-map read between deferred deletions. +Restoration of the ordered view therefore occurs at the next ordered read, +after the overlay is complete. + +## Executed behavioral evidence + +The final focused run passes **608 tests in 15 files**, with no type errors. +It includes GC retention, GC mutation admission, the two-Collection GC property, +checker calibration, cleanup/restart, transaction ownership, queue properties, +Collection and transaction examples, optimistic payload/composition, outcomes, +publication, and settlement boundaries. The GC properties retain all declared +reach minima and both their fixed and random campaigns. + +Calibration deliberately replaces `transactions.delete(id, true)` with ordinary +`transactions.delete(id)`. The existing optimistic-outcome owner rejects it at +`rows visible when isPersisted settled`: a surviving peer disappears when its +preceding settled entry is removed. Eight finite histories and both generated +campaigns fail the intended comparison, for ten failures total. The fixed +campaign shrinks to seed `851203`, path `0:0:0:0:0:0`, with absent initial rows, +rollback, a non-optimistic first request and a surviving peer. This is an +assertion rejection, not a setup error or timeout. The ownership owner passes +its eight tests under that mutant and is not credited with this kill. The +source is restored byte-for-byte before the final 608-test GREEN run. + +Native Chromium passes **3/3 histories** on the final source. Real +Electric/Postgres passes **12/12 paths** on the final built package, including +both sibling settlement orders and a genuine SQL-open survivor beyond the +first-release deadline. Each path reaches its declared provider-confirmation, +renewal and usable-idle cuts. The previous cross-host limits remain: these are +separate native-host and real-provider witnesses, not one combined schedule. +DB source/test TypeScript, private-member cache, focused ESLint, Prettier and +whitespace checks pass. + +Commands and local logs are under `/private/tmp/pr-2258-size`. The full commands +for the unchanged receiving drivers remain in the round-three report. No new +oracle coverage or universal bug-class closure is claimed by this refactor. + +## Shipped bytes + +`pnpm size:compare --packages db --files` uses the normal minified production +build, compression settings and app scenarios. The whole PR compares against +merge-base `45c33292b` on `origin/main`, rather than against an older PR version. +Negative values mean fewer shipped bytes. + +| Production output | gzip delta | Brotli delta | +| --- | ---: | ---: | +| DB bundle | -6 B | -69 B | +| Typical app | -18 B | -52 B | +| App with Effect | -8 B | -57 B | +| App with groupBy | -19 B | -69 B | +| App with includes | -14 B | +56 B | +| App with join | -20 B | -113 B | +| App with LocalStorage | -20 B | -103 B | +| App with optimistic action | -4 B | +50 B | +| App with paced mutations | +1 B | -25 B | + +The DB bundle and typical app meet the net-negative target in both formats. +Compression is not monotonic: the small increases in three feature scenarios +are retained explicitly above. They prevent a claim that every application +bundle becomes smaller. No names, comments, formatting, compression settings, +scenario definitions or minifier options were changed to obtain these results. +An attempted removal of the cleanup outcome wrapper reduced raw source but +increased Brotli weight, so the existing wrapper and error handling remain. + +Production source growth is separate from test/documentation weight. This +refactor adds no tests and changes no test expectations. Its documentation +records the size result and binds the final production sources; test and +contract growth for the broader PR remains in the earlier reports. + +## Local install recovery + +After the main merge, the registry returns HTTP 403 for +`baseline-browser-mapping@2.11.32`. Version `2.11.31` is absent from the registry; +`2.11.30` is the nearest earlier installable release. A local-only workspace +override and resulting lockfile changes remain uncommitted. The temporary main +baseline uses the identical override. Existing overrides are preserved. This +dependency is outside the changed production modules, and the workaround is +not included in the pull request. diff --git a/docs/contributing/oracle-reviews/pr-2259-persisted-option-types.md b/docs/contributing/oracle-reviews/pr-2259-persisted-option-types.md new file mode 100644 index 0000000000..0f0107877b --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-2259-persisted-option-types.md @@ -0,0 +1,583 @@ +# Persisted option type preservation: PR #2259 + +Reviewed PR head: `e3e8a9b5bce0a99d889b73f6eb7578b6d7ef3769`. +Original defective parent: `d2935d0fcac164163adc231c6a796b8a4c234d9b`. +This self-audit covers the oracle migration built on that head. Production +`persisted.ts` is unchanged from the reviewed PR after all temporary mutants. +Optional raw logs remain in local scratch storage. This versioned record and +the calibration command below preserve the verdict-critical reproduction steps. + +The initial migration record below precedes the prep-pr follow-up at the end. +That follow-up supersedes its owner hash, construction counts, and scoped +readiness assessment while preserving the earlier experiments. + +The migrated executable owner has SHA-256 +`c164515bf9705864bd5f0b27dc6e7769d1f7214cfba8158130fb12b36c04c9a0`. +The replay registry has SHA-256 +`53837b427c149964cd6dda52c7f87d71de57626790c48012527d1d2028f90889`. +These identify the historical initial instrument. Later sections explicitly +supersede them. They are not hashes of the current owner. + +## Law, experiment, and inference + +The SQLite guide's **Add persistence to a synced Collection** example composes +adapter options with `persistedCollectionOptions` and `createCollection`. +The public `createCollection` contract assigns schema input to insert, schema +output to reads, and the configured key and user utilities to callers. +Wrapping must preserve those roles, including when the caller has no schema. +The existing public persistence option types also require a present `sync` +property to contain a `SyncConfig`. + +| Existing evidence | Decisive experiment | Inference and repair | +| --- | --- | --- | +| Eight fixed core configurations and two browser configurations checked broad string/number keys. | Change only the schema-free result key types to `TKey extends string ? string : number`. All eight original core tests and both original browser tests pass. | Their expected answers do not generalize key preservation. Generate key domains and compare exact public key types with the independent input model. | +| Browser tests restated their expected types. | Run browser source exports and real Query Collection options through the same generated law as core. The parent implementation fails with TS2769 at `createCollection`; the PR implementation passes. | A receiving driver can share the model and comparison. Separate browser expectations are unnecessary. | +| Schema-free results must retain their row/key/utility contract without damaging schema-bearing options. | Restore either original overload, erase utilities, or return `never` from both schema-bearing overload results. Each mutant fails the generated comparison. | The repair must preserve both persistence modes and the neighboring schema-input/output law. | + +The primary executable owner is now +[`persisted-options-type-oracle.ts`](../../../packages/db-sqlite-persistence-core/tests/persisted-options-type-oracle.ts). +Its generated TypeScript model declares input/output rows, keys, and utility +signatures without importing production inference helpers. Its comparison +checks exact insert input, exact `get` parameters, the optional non-null read +result, the complete documented published-row shape, absence +of `any` reads, and supplied utility signatures. Generated invalid row, key, +utility, and sync expressions must remain compiler errors. The real Query +Collection driver additionally checks exact utility namespace preservation before/after +wrapping and exercises its write/refetch signatures. + +Each generated shape crosses 24 related construction forms: two persistence +modes, schema-free/transforming schema, typed-spread/inline arguments, and +inferred/partial/full explicit arguments. Browser adds the actual Query +Collection path. Partial explicit arguments select TypeScript's utility +default; they do not promise inference for unspecified later type parameters. +The original string, numeric, and browser row shapes survive as replay data +through the same checker, without separate expected answers. + +## Evidence block + +- **Law:** The source-level composition law above, from the SQLite guide and + public `createCollection`/persistence option contracts. +- **Reach:** Each receiver runs 12 generated histories with seed 2259 and 12 + with a fresh seed. In both campaigns, each of `schema-free-local`, + `schema-free-synced`, `schema-input-output`, `typed-spread`, + `inferred-arguments`, `explicit-arguments`, and `row-key-arguments` reaches + 12 histories. Browser's `query-adapter` also reaches 12 in each campaign. + Counts derive from the construction paths actually compiled and compared. + The three fixed replay inputs do not contribute to generated reach. +- **RED:** With the exact parent `persisted.ts` temporarily restored, both + receivers fail their first generated history with TS2769 at the public + `createCollection(persistedCollectionOptions(...))` call. The fixed-campaign + command is `../../node_modules/.bin/vitest run --coverage.enabled=false + --typecheck.enabled=false tests/persisted-options.test.ts -t fixed`, run + from each receiving package. Source restoration uses `finally`. +- **GREEN:** With PR production restored, both fixed and fresh-seed campaigns + pass through core and browser. There are 48 generated histories and 1,176 + consumer constructions across these four campaigns, plus shared replays. + Core's nine surrounding `persisted.test-d.ts` tests also pass. The runner + and shared harness typecheck in both packages without source errors. +- **Mutants:** All outcomes below are compiler comparison failures, not + timeouts or setup failures. All eleven receiving-path checks were replayed + again against the final generator after restoring its original names. +- **Limits:** This is source execution with TypeScript 5.9.3, Vitest 3.2.4, + and fast-check 3.23.2. It establishes no runtime parsing, I/O, publication, + generated declaration, npm resolution, other host re-export, or compiler + version claim. The grammar has 1–4 non-key fields, scalar/nullable/array/ + nested field types, six key domains, field optionality, and scalar utility + signatures. Domain values are sampled; there is no claim that every key + domain or every input appears in each fresh campaign. There is no known + counterexample in the declared grammar. The coverage-map entry retains the + owner and these limits for future extension. + +| Hostile implementation | Receiving path | Rejection | +| --- | --- | --- | +| Exact defective parent, both schema-free results retain `TSchema` | Core and browser | TS2769; seed 2259, path `0:0:0:0:0:0:0:0:0` | +| Restore only the synced schema-free result | Core | TS2769 at the synced construction | +| Restore only the local schema-free result | Core | TS2769 at the local construction | +| Widen literal/branded keys to broad string/number | Core | TS2344 at exact key equality; seed 2259, path `3:1:1:1:1:1:1`; reduced key is `"alpha" \| "beta"` | +| Erase schema-free user utilities to `UtilsRecord` | Core | TS2344 at utility equality and unused negative utility directive | +| Apply `never` to schema-bearing result types too | Core | TS2344 and unused negative schema-input directives | +| Remove `undefined` from the public getter result and assert the implementation return non-null | Core | TS2344 at optional read-result equality | +| Add a required `leaked` field to the public getter result | Core | TS2344 at complete published-row type equality | +| Add an overload that admits a present `sync: undefined` | Core and browser | Unused negative sync directives (TS2578); persistence is supplied so the error cannot arise from a missing adapter | + +The original ten fixed tests pass the widened-key mutant. This is an executed +false-green witness, not an inference from the file's name. The other mutants +reject at the reduced path `0:0:0:0:0:0:0:0:0` as well. + +Direct replay was verified against the parent: exactly one history ran, with +the captured seed and complete shrink path, and failed without further +shrinking. The same replay passed after restoration of PR production. The +shared replay witness recorded `numRuns: 1`, `failed: true` for the parent and +`failed: false` for the repair. To reproduce the repaired replay: + +```sh +# From packages/db-sqlite-persistence-core +TANSTACK_DB_ORACLE_SEED=2259 \ +TANSTACK_DB_ORACLE_PATH=0:0:0:0:0:0:0:0:0 \ +TANSTACK_DB_ORACLE_PROPERTY=persisted-options.types.core \ +pnpm exec vitest run --typecheck.enabled=false --coverage.enabled=false tests/persisted-options.test.ts +``` + +Normal verification from the respective package directories: + +```sh +# Core: campaigns, harness typecheck, and surrounding persistence type contracts. +pnpm exec vitest run --coverage.enabled=false tests/persisted-options.test.ts tests/persisted.test-d.ts + +# Browser: campaigns; the next command separately checks the harness types. +pnpm exec vitest run --typecheck.enabled=false --coverage.enabled=false tests/persisted-options.test.ts +pnpm exec vitest run --typecheck.only --coverage.enabled=false tests/persisted-options.test.ts +``` + +Local commands invoked the same Vitest executable directly, with workspace +`node_modules/.bin` first on PATH. The configured registry returned 403 and +the public fallback returned 503 during installation; existing installed +external dependencies were reused without changing the lockfile. Explicit +compiler aliases target this checkout's production sources. Early failures +from jsdom import URLs, missing worker/schema declarations, and an accidentally +selected global TypeScript 7 compiler were setup failures and supply no RED +evidence. Final compiler runs retain all diagnostics rather than suppressing +source errors. ESLint, formatting, `pnpm lint:changed`, and `git diff --check` +also pass. + +## Requirement audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Authority, source-level observation, finite grammar, and receiving limits are stated above and beside the model. | +| ORC-002 | Expected rows/keys/user utilities are independently declared; the published virtual shape comes from the public WithVirtualProps/VirtualRowProps documentation. No production schema inference or virtual-row helper computes expectations. PublicShape flattens type intersections while retaining every key, readonly/optional modifier, and undefined. The Query utility relation trusts the unchanged adapter contract. | +| ORC-003 | Contract, grammar, independent row model, construction drivers, and compiler comparison have adjacent prose in the owner. | +| ORC-004 | Reconstruction: replay shapes preserve the original row/key/utility cases. Ablation: typed spreads and inferred arguments expose the parent; mode variants reject single-overload repairs; key domains reject widening; schema neighbors reject blanket-never; utility signatures reject erasure. Range: 1–4 non-key fields, six keys, optional/nullable/array/nested fields, scalar utility signatures. Exclusion: duplicate names cannot be generated; boolean/symbol keys are outside the legal domains. | +| ORC-005 | Real source entries feed the compiler. Diagnostics and exact type constraints observe the public call boundary. The original and mutant diagnostics positively identify those paths. There is no runtime information-delivery claim. | +| ORC-006 | Original implementation and eight additional wrong designs reject at the intended compiler comparison; the legacy-suite survivor demonstrates the missing distinction. | +| ORC-007 | Fixed/fresh campaigns share one grammar, comparison, and budget. Seed/path/property replay runs directly, and the shared reporter records one failed/passing replay. No command-model replay path applies. | +| ORC-008 | Not applicable: the reference computes static type shapes and introduces no stateful runtime abstraction. | +| ORC-009 | A history here is a model-only declaration/construction/compilation sequence, explicitly mapped to the source boundary. It is not a Collection sync run. | +| ORC-010 | Full compiler diagnostics retain the violated type constraint and source expression. Fast-check preserves the generated/shrunk shape and path. Generated consumers live in memory; temporary mutation experiments restore source in `finally`. | +| ORC-011 | No additional shared-semantic-fault hypothesis was identified for the independent shape model. The Query driver's before/after utility relation is supplemental; it does not independently specify Query Collection internals. | +| ORC-012 | This record identifies the reviewed head, scope, experiments, dispositions, and applicable/non-applicable requirements. No universal bug-class proof is claimed. | +| ORC-013 | Typed/untyped and schema-free/schema-bearing neighbors distinguish omission of the schema premise from blanket schema erasure. Exact broad/literal key cases reject widening. | +| ORC-014 | The browser public source export and actual Query adapter are receiving drivers of the shared law. No SQLite/provider/runtime-host premise is transferred by this source-only claim. | +| ORC-015 | Shared `createOracleReach` counters require every declared path to appear, count histories once, and report the measured counts above. Fixed replay data does not inflate them. | +| ORC-016 | No standalone unit/type examples remain in this owner. Fixed cases are model input data; both receiving runners register the same generated law. Negative type checks are emitted by the reusable comparison from each shape. | + +## Lossless finding ledger and reviewer assessment + +The raw sources are the preceding assistant review, the user's correction, +and CodeRabbit's PR comment. The GitHub review and inline-comment lists were +empty at capture. There were no additional capped findings or footnotes. + +| ID | Claim | Technical verdict and evidence | PR action | Durable value/destination | +| --- | --- | --- | --- | --- | +| R1 | The PR does not edit its oracle. | Refuted literally: the original diff edits the coverage-map owner. This does not answer R2. | `refuted`; no separate code change. | Preserve the distinction between editing an owner and enforcing a reusable law. | +| R2 | Fixed assertions do not provide a generated type-preservation oracle; ban unit tests in oracle files. | Confirmed by the widened-key mutant passing all original tests. | `fixed-now`: generated owner, shared replay inputs, reach/replay/calibration; AGENTS, authoring skill, and ORC-016 prohibit standalone unit tests in oracle files. | Primary owner and this evidence record. | +| R3 | Browser checks repeat separate fixed expectations. | Confirmed by source inspection and the same legacy-suite survivor. | `fixed-now`: browser/Query receiving paths use the identical model/checker. | Receiving runner and primary owner. | +| R4 | CodeRabbit reports no actionable issue and says the fix is ready for normal merge checks. | Refuted as an oracle-sufficiency assessment: it missed R2/R3 and the executed false green. This does not establish a new defect in the PR's two-line production fix. | `refuted`; repair the actual coverage gap. | Keep whole-PR readiness separate from a green fixture matrix. | +| R5 | CodeRabbit's 0% docstring warning requires missing docstrings. | Refuted as an actionable documentation defect: changed public overload return types retain the same API; the warning identifies no incorrect/missing public contract. Its metric itself was not reimplemented. | `refuted`; no metric-driven docstrings. | The executable owner now explains the law and limits; retain useful prose rather than chasing the percentage. | + +CodeRabbit's overload summary was technically accurate. Its oracle analysis +had low depth and prioritized a documentation metric over a real enforcement +gap. The preceding +assistant review located the right owner and distinguished compile-time from +runtime coverage, but was too generous in treating fixed expected shapes as +sufficient independent oracle judgment. The surviving mutant corrects that +assessment. This is a self-audit, not an independent external review. + +Loss audit: **5 raw items = 2 fixed-now + 3 refuted**. No confirmed-open, +deferred, duplicate, stale, or unresolved design-decision items remain in this +scoped migration. The unit-test prohibition is an explicit review requirement; +the existing syntactic lint rules cannot prove semantic model independence. + +Whole-PR readiness: **not assessed**. The focused instrument and type contract +checks pass; full runtime CI, published package resolution, and integration +outside the named source receiving paths were not rerun or claimed. + +## Prep-pr follow-up: contextual schema inference + +Reviewed production remains `e3e8a9b5bce0a99d889b73f6eb7578b6d7ef3769`. +The final executable owner has SHA-256 +`5fa47aeaec4790188ee46194e687ca269c507d47d97555d475edc219d45dabf4`. +The replay registry is unchanged from the initial migration above. + +The independent prep-pr reviewer found one P2 gap: all generated core `getKey` +callbacks annotated their row as `Output`. The deleted schema examples used +an inline unannotated callback. A spread of an annotated callback cannot test +whether the schema context supplies the output row. + +An in-memory compiler-host mutant changed only the two schema-bearing input +overloads to accept `getKey: (row: any) => TKey`. The baseline accepted both +construction paths. The mutant passed the annotated path and the entire +initial fixed core campaign, including its replays and all declared reaches. +The original unannotated path rejected it: an invalid numeric key became +accepted, producing TS2578 for the unused negative directive. This is an +executed false-green witness for the migration, not a production defect on +the repaired PR. + +The repair adds six generated contextual schema forms: local/synced crossed +with inferred/partial/full explicit arguments. They put the unannotated +callback directly inside the wrapper argument. The callback's public row +parameter must equal the independently declared schema output. All 24 +annotated forms remain. Core therefore compiles 30 forms per shape; browser +compiles the same 30 plus its actual Query Collection receiving path. +Fixed regressions remain inputs to that same model, driver, and comparison. + +- **Law:** The public schema-output contract also supplies the contextual + `getKey` row. It must preserve the inferred key. Authority is the schema + overload's documented output-row role and the original schema composition + examples, rather than the mutant's behavior. +- **Reach:** `schema-contextual-key` reaches 12 generated histories in each + fixed and fresh campaign on both receivers. Every prior declared state also + reaches 12. Across four campaigns, 48 generated histories compile 1,464 + constructions, plus shared regressions excluded from generated reach. +- **RED:** The initial fixed core campaign survives the contextual-row mutant. + The expanded comparison rejects it with TS2344 at `CallbackInput`, with + additional inferred-key/result mismatches. Direct replay uses seed 2259, + path `0:0:0:0:0:0:0:0:0`, and the appropriate receiver property. +- **GREEN:** The expanded core and browser campaigns and harness typechecks + pass. Core also passes all nine neighboring persistence type tests. The + commands retain the initial verification selectors and limit Vitest to two + CPUs. +- **Mutant:** The same compiler-host overlay supplies the wrong contextual + type to the unchanged expanded comparison. Both core and browser reject it + at the callback parameter equality. The nine earlier wrong designs were + also replayed against the expanded owner: all eleven receiving checks still + reject at their compiler comparisons. In total, ten wrong designs reject + across thirteen receiving checks. Production on disk remains the PR's + two-overload correction after temporary calibration experiments. +- **Limits:** The initial source-only and compiler-version limits still apply. + This adds no runtime, built-declaration, or package-resolution claim. + +ORC-004 reconstruction now includes the original contextual schema path. +Its ablation is decisive: removing the six contextual cells lets this mutant +pass. ORC-005 observes the callback parameter at compilation as well as the +resulting Collection. ORC-006 rejects the previously surviving wrong design. +ORC-015 declares and measures the added path. The other ORC outcomes above +retain their scope, with the expanded counts and owner hash stated here. + +Finding ledger: **one raw prep-pr finding = one repaired coverage regression**. +The simplifier made no changes and proposed no additional suggestions. +Technical reviewer assessment: the reviewer +identified a lost public path and proved its significance with a full-campaign +survivor and a distinguishing old-path failure. Root independently reran the +four-way probe and inspected the full-campaign overlay and diagnostics. +The reviewer then inspected the expanded owner and confirmed the repair, +retained paths, and declared reach, with no additional supported findings. + +The bundle-size comparison could not build the cached baseline because that +worktree lacks Vitest/TypeScript dependencies. A separate TypeScript 5.9.3 +transpile comparison emits identical JavaScript for the parent and repaired +`persisted.ts`. This is emit evidence, not a completed bundle measurement. + +## External review follow-up at 4ea6b7663 + +The supplied review read PR head +`4ea6b76634ef3b16ec8461fb29bb4aebb51819da` without running its tests. +This follow-up evaluates all ten items and repairs confirmed oracle gaps. +Production remains the original two schema-free result corrections; no new +production defect was demonstrated. This section supersedes earlier descriptions +of the current grammar, Query expectation, diagnostics, counts, and replay path. +The follow-up owner SHA-256 is `8f206d9195231239989e2a2c3370fd4abaf4fe4bab32717689c0b5e04834905c`. +The containing Git commit identifies the complete follow-up tree. Historical +hashes above describe earlier experiments, rather than a mutable current file. + +### Law, experiment, inference, and enforcement + +| Law and authority | Experiment and inference | Final enforcement and limits | +| --- | --- | --- | +| Public persistence utilities retain required/optional signatures, alongside caller utilities. Authority: PersistedCollectionUtils API and SQLite guide. | Removing the local result intersection passes the reviewed full campaign. The expanded comparison rejects TS2344 at LocalCapabilities. | Independent signatures observe acceptMutations, optional getLeadershipState and optional forceReloadSubset. PendingMutation/LoadSubsetOptions are trusted domain carriers, not expected-result helpers. | +| Documented persistence options and contextual callbacks remain usable across schema/sync configurations. Authority: SQLite guide and public SyncConfig/option contracts. | Old grammar omits those options and uses no-argument sync functions. Widening schemaVersion, initialRender strategy, syncMode or contextual sync input now rejects TS2344. | Twelve extra configurations per shape cross schema and sync modes with eager/on-demand/network-first. Network-first is eager. Generated schemaVersion is 1–100 and timeout is 0–10000. This checks types, not runtime option validation. | +| Query composition preserves its documented closed capabilities. Authority: QueryCollectionUtils public reference and the guide's adapter composition. | isLoading:any survives the reviewed full browser campaign. The independent model rejects it, widened refetch options, and an extra optional adapter capability. | Generated row/key write signatures plus exact state fields and refetch parameters. Non-any Promise-array envelope is independent; supplemental before/after equality preserves the full return payload. No independent QueryObserverResult payload semantics are claimed. | +| A fresh sync:undefined argument must fail for sync admission, rather than collateral corruption. | Undefined-sync admission alone already failed the old directive. Adding unrelated getKey:42 to the negative expressions masks it and passes the old full campaign. New paired observations reject that compound corruption. | Unsuppressed TS2769 must point at the exact fresh sync property. The legal neighbor changes only sync and must compile. Every other diagnostic remains fatal. Structural extra properties carried through variables have different TypeScript freshness semantics and are not this rejection claim. | +| Run budget, receiving configuration and diagnostic fidelity must remain effective. | Multiplier10 originally registers 120 runs but 120000ms. Revised registration gives 1200000ms. Source-only TS2322 and declaration-only TS4094 controls reject. | Receiver tsconfig, one compiler per receiver across campaigns, dependency caching, all original diagnostic classes, and event-loop yields between generated histories. No generated-file-only diagnostic filtering. | + +### Lossless finding ledger + +| ID | Supplied claim | Technical verdict / evidence | PR action | Durable value | +| --- | --- | --- | --- | --- | +| E1 | Local-added utilities are unchecked; removing their result intersection survives. | Confirmed by full reviewed-campaign survivor and new TS2344 rejection. | fixed-now: independent LocalCapabilities comparison. | Primary owner and local-utils recipe. | +| E2 | Deleted schemaVersion/markReady examples lost coverage; initialRender/syncMode absent. | Confirmed grammar omissions. No new runtime defect claimed. | fixed-now: generated legal options, exact declared option law, contextual readiness signature and reaches. | Primary grammar; four option/callback mutants. | +| E3 | Unused schema generic silently drops a schema; using never in inputs fixes it. | Refuted as stated. Eighteen compiler probes show current and proposed inputs both accept no-value phantom generic, reject an actual schema value (TS2769), and retain correct schema-input composition. Suggested change has no effect. | refuted: no production edit. | Two phantom-generic paths preserve useful normalization coverage. | +| E4 | Whole-program work repeats; timeout ignores multiplier; check only generated file and share compiler. | Timeout confirmed. Repeated dependency checking is avoidable. Generated-only diagnostics would hide source errors. | fixed-now: semantic builder shared across campaigns, scaled timeout, retained source/declaration diagnostics. | Deterministic affected-file telemetry and budget/source controls. | +| E5 | Fixed refetch/nonexistent-method examples violate law-only policy. | Confirmed standalone concrete expressions in Query driver. | fixed-now: reusable closed capability model replaces them. | Model parameterized by generated rows/keys, not fixed call examples. | +| E6 | Expected Query utils derive from production; shared erasure can pass. | Independent judgment gap confirmed by isLoading:any survivor. Whole-namespace any was overstated: old negative writes already detect some erasure. | fixed-now: independent model plus supplemental transparency relation. | State, namespace and refetch corruption controls. | +| E7 | Named campaigns omit runners; tests can only get 12 runs. | Registration omission confirmed. Hardcoded 12 claim refuted: multiplier 3 helper experiment executes 36 fixed and 36 random checks; normal package test globs already include runners. | fixed-now: package/root test:oracles registration. | Distinguish selection, count, reach and completion. | +| E8 | Browser uses core tsconfig, invalidating browser witness. | Config selection confirmed; current semantic compiler settings match. Both configs compile the same consumer and resolve the same browser/Query source modules. | fixed-now: select receiving package config to prevent drift. | Preserve accurate source-only browser scope. | +| E9 | Any unrelated error can satisfy bare sync directive. | Checker fragility confirmed with the two-factor control above; original admission-only mutant was already detected. | fixed-now: exact unsuppressed property diagnostic and legal same-candidate neighbor. | Reusable admission observation rejects collateral corruption. | +| E10 | Audit depends on inaccessible scratch, mutable hashes, personnel verdicts. | Mixed: versioned record already contains authority, commands, outcomes and limits, and historical hashes match. Exact overlay recipes and stale wording needed improvement. | fixed-now: versioned runnable calibration command, historical-hash clarification, technical-only reviewer assessment. | Others can reproduce decisive results without prior scratch files. | + +Loss audit: **10 supplied items = 9 fixed-now + 1 refuted**. No raw item, +footnote, or suggested fix was dropped. Proposed remedies rejected as ineffective +or unsafe remain recorded in their original item. No deferrals or unresolved +product decisions remain in this scoped evaluation. + +The review had high signal for missing observations and grammar dimensions, +and clearly disclosed that its findings were unexecuted. Its weaker points were +the ineffective input-never proposal, generated-only diagnostics suggestion, +and overstatements about multiplier use, compiler settings, and missing evidence. +Execution supports the oracle repairs, not a newly discovered production bug. +Personnel recommendations remain in the local review ledger. + +### Reproducible calibration + +From the repository root, with the workspace dependencies installed: + +```sh +# Unmodified current fixed campaign: must succeed. +node scripts/calibrate-persisted-option-types.mjs none + +# Reviewed full campaigns: both accept these wrong implementations. +node scripts/calibrate-persisted-option-types.mjs local-utils 4ea6b7663 +node scripts/calibrate-persisted-option-types.mjs query-status 4ea6b7663 +node scripts/calibrate-persisted-option-types.mjs collateral-sync 4ea6b7663 + +# Current owner rejects each at the named comparison. +node scripts/calibrate-persisted-option-types.mjs local-utils +node scripts/calibrate-persisted-option-types.mjs query-status +node scripts/calibrate-persisted-option-types.mjs collateral-sync + +# Exact original production, without touching source files on disk: rejects TS2769. +node scripts/calibrate-persisted-option-types.mjs parent +ORACLE_RECEIVER=browser node scripts/calibrate-persisted-option-types.mjs parent + +# Retained source and declaration diagnostic fidelity: reject TS2322 / TS4094. +node scripts/calibrate-persisted-option-types.mjs source-error +node scripts/calibrate-persisted-option-types.mjs declaration-error + +# Budget must report 120 runs and two 1200000ms registrations. +ORACLE_REGISTRATION_ONLY=1 TANSTACK_DB_ORACLE_RUNS_MULTIPLIER=10 node scripts/calibrate-persisted-option-types.mjs none + +# Observe builder invalidation and worker progress through both campaigns. +ORACLE_COMPILER_WORK=1 ORACLE_ALL_CAMPAIGNS=1 node scripts/calibrate-persisted-option-types.mjs none +``` + +Calibration failures intentionally exit nonzero. Classify the reported compiler +constraint or missing exact admission rejection, rather than treating any +nonzero exit as a killed mutant. The script creates disposable files under +`.context` from versioned sources; it needs no existing scratch evidence and +never mutates production on disk. Its optional second argument pins only the +historical owner; production starts from the current checkout unless the +selected overlay replaces it. To reproduce the precise historical whole tree, +run the command from a checkout of the reviewed commit with this calibration +script available. + +The command also supports sync-regression, local-regression, widened-key, +erased-utils, blanket-never, contextual-key, missing-undefined, extra-read-field, +undefined-sync, query-refetch, query-namespace, schema-version, initial-render, +sync-mode and contextual-sync. The widened-key control requires normal generated +sampling; the other observed controls reject at seed 2259 and reduced path +`0:0:0:0:0:0:0:0:0:0:0`. The property and replay interface are the same as the +normal receiver runners. Explicit replay starts at the requested generated path and bypasses normal +campaigns and fixed examples. Fast-check 3.23 can also visit remaining sibling +shrinks after a passing colon-path replay, despite numRuns: 1; a calibrated +repaired replay visited 14 histories. Failing minimal replays stop after the +first rejected history. No single-history claim is made for successful replays. + +### Follow-up evidence and requirement audit + +- **Law:** The original composition law remains, with independent capability, + option and contextual-callback observations from the public references above. +- **Reach:** Each core campaign compiles 44 constructions per shape; browser + compiles 45. Across four 12-history campaigns this is 2136 generated constructions, + plus 534 fixed-replay constructions outside generated reach. Every prior state + and each new local-capabilities, configured-options, on-demand-options, + network-first-options, contextual-sync and phantom-schema-argument state + reaches 12 histories per campaign; query-adapter reaches 12 on browser. +- **RED / mutants:** Original production still fails at public composition. + Local utility removal, Query state widening and collateral-error masking are + demonstrated false greens on the reviewed owner and reject on the expanded + owner. Twenty-one original/hostile/collateral controls reject, including source and + declaration-only controls. Outcomes are compiler comparison failures or the + exact missing-admission observation, not timeouts or setup failures. +- **GREEN:** Both receiver campaigns and neighboring core type contracts pass + on unchanged PR production (11 core tests and two browser campaigns, no type + errors). Both named package commands also pass at multiplier 2, with 24 + histories per declared state in each campaign. Browser's command filters to + the changed type property; its 99 older runtime tests are not rerun by that + check. Semantic affected-file instrumentation records 290 files for the first + core history and 1 for every later history across both campaigns. Declaration + diagnostics are still collected separately, so this count does not claim all + compiler work is eliminated. The worker heartbeat advances 24 times across + the 24 generated histories. Formatting, ESLint, lint:changed and diff checks pass. +- **Limits:** Source compilation with TS 5.9.3 / Vitest 3.2.4 / fast-check 3.23.2 and the + stated finite grammar. No runtime, package/declaration publication, or other + host/compiler-version claim. Declaration diagnostics are retained for source + fidelity, not proof of emitted artifact consumption. + +The independent follow-up challenge found sibling S1: the initial builder +rewrite omitted declaration diagnostics. An exported anonymous class with a +private field survives that gate but fails TS4094 when declaration diagnostics +return. The final gate retains them and the declaration-error recipe. This was +an oracle-repair regression, not a production defect. + +ORC-001/002 now cover independent local/Query capability and option laws. +ORC-003 prose follows the revised grammar, model, driver and exact diagnostic +cut. ORC-004 reconstruction retains all 30 old forms and adds configured/phantom +forms; option ablations and the documented calibration controls distinguish +their contribution. ORC-005/006 observe real receivers and reject the controls +above. ORC-007 retains campaign parity and the shared direct replay interface. +ORC-008 remains inapplicable to a static model. ORC-009's type-history mapping +is unchanged. ORC-010 retains unexpected source/declaration diagnostics and +original shrinking evidence; no disk restoration is needed for the overlays. +ORC-011's shared Query fault is answered by the independent capability model. +ORC-012 is this versioned evidence. ORC-013 observes legal option neighbors and +invalid sync with a same-candidate legal neighbor. ORC-014 retains the real +browser/Query receiving source paths, with no runtime provider claim. +ORC-015 measures every new declared state. ORC-016 contains no standalone fixed +API examples; fixed regressions remain shared model inputs. + +### Calibration portability follow-up + +CodeRabbit review5656171283 of892bf55b8 identified four Minor items, preserved +as C2-1 through C2-4 in source order. C2-1 corrected missing spaces in E7's +numbers. C2-2 removed eager parent-history loading from unrelated calibrations. +C2-3 restricted transform/target validation to the selected mutation. C2-4 added +the missing changed-source guard for the two Collection getter controls. + +The law here is reproduction integrity: selecting one control must require +only that control's inputs, and a requested semantic edit must actually apply. +Executing the helper's actual setup prefix with filesystem/process overlays +reproduced three failures: `none` attempted unavailable parent history; `none` +failed an unrelated changed overload target; and a selected getter control +constructed an unchanged overlay after both its signature and return-helper +targets disappeared. The same probes now avoid all historical reads for `none` +and report `Missing mutation target: missing-undefined` for the no-op control. +Real commands then confirm `none` registers normally, `parent` rejects TS2769, +and `missing-undefined` rejects the public-read equality with TS2344. +No production, grammar, model, observation or compiler-gate edit was needed. + +All four items are fixed-now. The script's lazy parent getter and selected-only +transform loops preserve the intended overlays and make independent controls +usable when unrelated historical sources drift. The review accurately found +portable-calibration weaknesses; no production claim follows from those setup +failures. The previous owner hash and oracle evidence remain unchanged. + +## Adversarial round two and reserved utility decision + +Reviewed head: `39065e399894913874d5f6965f75322cbac82ad7`. The containing +follow-up commit identifies the repaired tree. Three fresh reviewers examined +enforcement, authority, and compiler infrastructure. This section supersedes +the earlier current-grammar counts and the claim that production contains only +the schema-free return correction. The original experiments remain historical. + +### Law, experiment, inference, and repair + +| Law and authority | Decisive experiment | Repair | +| --- | --- | --- | +| Schema input accepts output, so already-transformed rows remain legal input. Authority: schema guide, Core Concepts and Critical Design Principle. | A real persisted Collection with input `string \| number` and output `number` accepts an output row. Restoring the old negative directive gives TS2578. The old disjoint grammar also allowed output-optional/input-required fields. | Generate the superset relation, retain exact input equality, and observe legal output insertion. No production change for this finding. | +| Public read types retain declared readonly user properties. Authority: schema-free createCollection return type and WithVirtualProps. | A getter mutant strips user-property readonly while retaining virtual modifiers. It passes the old path-0 history. A schema-free readonly row passes production and rejects the mutant at the shared published-row equality. | Generate independent modifiers and eight structural mutable/readonly neighbors across schema and sync modes. Declare and measure their reach. | +| Local persistence owns its utility names and rejects caller collisions. Authority: the user's decision, “let's just throw an error as those names are reserved,” now documented in the SQLite guide. | Before the guard, both real receivers accept a reserved caller property and resolve persistence. TypeScript permits a caller leadership return type of `'caller'`, while runtime returns the persistence leadership object. | Reject acceptMutations, getLeadershipState and forceReloadSubset before persistence resolution. The guard uses the existing utility object's keys. Synced wrapping preserves adapter utilities because that path adds none of these local capabilities. | + +The runtime law is an explicit contract decision, rather than an inference from +the previous overwrite behavior. It applies to a present reserved property, +including inherited/non-enumerable properties and undefined values. The wrapper +throws InvalidPersistedCollectionConfigError. This is runtime admission, not a +new compile-time rejection. Successful local results cannot retain a conflicting +caller signature because the conflicting construction does not return. + +The existing option owner now includes a runtime receiving driver for this +namespace boundary. Its independent rule uses reserved names and sync presence. +The production driver records return/throw, error identity, persistence resolver +calls, caller-function calls, and preservation of admitted utilities. Each +history crosses schema presence, both sync modes, each reserved name, their +union, a generated subset, ordinary names, three descriptor forms, and +undefined/function values. A rejected configuration must do zero resolver work. +No Collection is created and no SQLite operation or sync starts. + +### Lossless round-two ledger + +| ID | Claim | Technical verdict and PR action | Durable value | +| --- | --- | --- | --- | +| R2-A1 | Caller utility preservation conflicts with local reserved-name overwrites. | Confirmed type/runtime mismatch. User chose rejection. fixed-now: runtime guard, shared generated admission judgment, real core/browser RED and GREEN, guide and changeset. | Explicit admission contract and reserved-name controls. | +| R2-A2 | Phantom schema generic acceptance lacks a guide-level meaning. | Refuted as a current correctness defect. Existing public signatures accept this source form; executable prose now labels the compatibility basis. | Distinguish source compatibility from runtime schema semantics. | +| R2-A3 | Exact Query utility equality also rejects future typed additions. | Refuted as a current defect. The current API remains the independent expected shape. Prose clarifies that an intentional documented addition updates the model. | Distinguish a current namespace check from a prohibition on API evolution. | +| R2-A4 | The transformation grammar misses the documented update-safe input domain. | Confirmed. fixed-now: compatible transform, optionality relation, and legal output admission. Exact input equality still rejects reversed schema roles. | Schema-output-rejection and blanket-never controls. | +| R2-A5 | The header implies a generated persistence utility reference page that does not exist. | Confirmed citation issue. fixed-now: cite the exported PersistedCollectionUtils interface directly. | Trace the law to the actual public API. | +| R2-E1 | Readonly user fields are absent from the grammar. | Confirmed old-owner survivor. fixed-now: generated modifiers and measured structural neighbors. | Readonly-row control, including a schema-free witness. | +| R2-S1 | Concurrent calibration processes can overwrite the same scratch runner. | Found during reconciliation: core/browser parent controls shared a filename, causing a missing-campaign setup error. fixed-now: receiver and process ID in type-runner names, process ID in runtime bundles. Repeated concurrent controls both reject TS2769. | Calibration isolation across receivers and owner versions. | + +Loss audit: six supplied items plus one reconciliation finding equal five +fixed-now and two refuted. No deferrals, unresolved policy decisions, or known +in-scope counterexamples remain in this bounded option review. Technical reviewer +assessment: the enforcement review demonstrated a concrete survivor and supplied +an independent schema-free witness. The authority review verified the contract +conflict and runtime mismatch. The infrastructure review accurately separated +executed controls from source inspection and found no additional supported bug. + +### Evidence block and reproduction + +- **Law:** Input/output and modifier preservation follow the existing public + contracts. Reserved local utility rejection follows the explicit decision + above. The runtime comparison does not import production's merge/classifier. +- **Reach:** Each type shape now compiles 52 core or 53 browser constructions. + Four default campaigns cover 48 generated histories and 2520 constructions, + plus 630 fixed replay constructions outside reach. Every type state reaches + 12 histories per campaign, including schema-compatible-input, readonly-input, + readonly-output and mutable-fields. Four runtime campaigns cover 48 histories + and 6912 wrapper constructions; all ten admission states reach 12 histories + in each campaign, including schema-collision. +- **RED:** Before production edits, both real entry points fail admission at + the intended return/throw comparison: a reserved property is admitted, no + config error appears, and the resolver runs once. Schema-free and + schema-bearing histories both demonstrate this. The type owner rejects the + original schema-free overloads with TS2769 on both receivers. +- **GREEN:** The type campaigns and nine neighboring core type tests pass. + The final expanded admission campaigns pass on both receivers with no source + errors. Three existing runtime checks also pass: local loopback utilities, + manual acceptMutations, and terminal-failure fencing of direct acceptance. + Formatting, ESLint, lint:changed and diff checks pass. +- **Mutants:** The readonly-stripping getter rejects on both receivers. The + obsolete schema-output rejection fails TS2578, and blanket schema erasure + fails the exact type laws. Runtime controls reject the old production, + removed guard, own-properties-only guard, truthiness-only guard, and a guard + missing forceReloadSubset. Failures identify admission or type comparisons, + not setup errors or timeouts. The initial concurrent-runner setup error is + R2-S1 above and is not counted as a mutant rejection. +- **Weight and limits:** The production bundle grows 64 gzip bytes and 29 + brotli bytes against the merge-base on fetched origin/main. Reusing the + existing utility object and inlining its acceptance callback avoids a second + name registry and helper. The new rejection branch accounts for the growth. + Type evidence remains source-only on TS 5.9.3. Runtime evidence stops at + wrapper admission and does not establish database I/O, schema parsing, + Collection mutation behavior, native hosts, or published package resolution. + +From each receiving package, the normal persisted-options.test.ts command runs +both type and admission campaigns. To select admission alone, add +`-t 'reserved local utility'`. Direct runtime replay uses seed 2259, path +`0:0:0`, and property `persisted-options.utilities.core` or `.browser`. + +The following repository-root commands use only versioned inputs: + +```sh +# Old grammar accepts the readonly-stripping getter. +TANSTACK_DB_ORACLE_PATH=0 node scripts/calibrate-persisted-option-types.mjs readonly-row 39065e399 + +# Current grammar rejects these controls. Use the browser receiver for readonly-row too. +TANSTACK_DB_ORACLE_PATH=0:0:0:0:0:0:0:0:0:0:0:0:0 node scripts/calibrate-persisted-option-types.mjs readonly-row +ORACLE_RECEIVER=browser TANSTACK_DB_ORACLE_PATH=0:0:0:0:0:0:0:0:0:0:0:0:0 node scripts/calibrate-persisted-option-types.mjs readonly-row +TANSTACK_DB_ORACLE_PATH=0:0:0:0:0:0:0:0:0:0:0:0:0 node scripts/calibrate-persisted-option-types.mjs schema-output-rejection +TANSTACK_DB_ORACLE_PATH=0:0:0:0:0:0:0:0:0:0:0:0:0 node scripts/calibrate-persisted-option-types.mjs blanket-never + +# Runtime controls bundle the same shared admission model and the real core entry. +node scripts/calibrate-persisted-option-types.mjs utility-parent +node scripts/calibrate-persisted-option-types.mjs utility-no-guard +node scripts/calibrate-persisted-option-types.mjs utility-own-only +node scripts/calibrate-persisted-option-types.mjs utility-truthy +node scripts/calibrate-persisted-option-types.mjs utility-missing-name +``` + +The runtime controls use esbuild and write isolated bundles under .context. +They leave tracked production files unchanged. A failure must show the intended +comparison and selected input, rather than merely exit nonzero. + +ORC-001/002 tie the revised input rule and new admission policy to their actual +authorities. ORC-003/004 retain adjacent prose and add modifier, schema and +property-descriptor neighbors. ORC-005/006 now include real wrapper admission +and the calibrated rejection boundary. ORC-007/009/010 preserve campaign parity, +replay, source/type diagnostics and isolated calibration artifacts. ORC-008 is +still inapplicable: neither model adds a state machine. ORC-011 retains the +independent Query formulation and identifies no new shared semantic-fault +hypothesis. ORC-012 is this versioned audit. ORC-013 checks ordinary local and +synced collisions beside rejected local collisions. ORC-014 uses both real +receiving exports without transferring a SQLite/provider premise. ORC-015 +measures the new type and admission states. ORC-016 keeps all observations in +shared judgments over generated inputs, without standalone regression answers. diff --git a/docs/contributing/oracle-reviews/pr-2266-collection-key-type.md b/docs/contributing/oracle-reviews/pr-2266-collection-key-type.md new file mode 100644 index 0000000000..5823e1301c --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-2266-collection-key-type.md @@ -0,0 +1,186 @@ +# Collection key type and SortedMap restore review + +Reviewed product head: `c385f6eed162fca2f9a997f02ecababfff077294` on +`sortedmap-merge-restore`, based on `origin/main` at +`b8d8ee07c95f51d84c880f6bb7371a84d2858394`. Sources: PR #2266, an internal +code review (10 findings), a code-simplifier pass (7 proposals), and two +CodeRabbit reviews (one finding and one nitpick). A scratch ledger was kept +during evaluation; this record keeps the verdict-critical oracle evidence in +the repository. + +## Contract and scope + +Two owners change, and one owner is new. + +**Grouped result keys (new owner).** A Collection key is a string or a +number: the `TKey` constraint of every Collection type, and the +`InvalidKeyError` that optimistic mutations throw for any other key. A grouped +live query that groups by one field used the raw group value as the key, so a +`null`, `undefined`, boolean, or bigint group value produced a key outside +that type. Grouping by a nullable field is a valid query. Maintainer decision +(2026-10-27): the grouped key matches the declared key type; the type does not +widen. Such a group value now keys its group in the namespace that +`normalizeValue` reserves, which no string group value occupies. The law fixes +the key's type, uniqueness, and stability, not its format. `sync.write` now +throws `UndefinedKeyError` or `InvalidKeyError` for a key outside the type, +through the check that mutations already used. + +**SortedMap (repaired owner).** Maintainer decision: SortedMap promises what +Collection state uses, not full `Map` behavior, and is no longer exported. +Promised: Map ownership for Collection keys, including `NaN` as one key after +every other number; key or comparator order with key tie-breaking; a row +changed in place before restore sorts by its current value; after a placed row +changes in place, each key stays in the views once with its current value, and +a later write or delete affects only its own key; a deferred delete during +iteration yields the starting order once; a restore of k deferred keys among n +others makes at most k * (ceil(log2 k) + ceil(log2 (n + 1)) + 2) comparator +calls, and none with nothing deferred. Not promised: immediate writes or +`clear()` during iteration, the order after a placed row changes in place +without a write, `-0` keys, keys outside the key type, a throwing comparator, +and the work of key order without a comparator. + +**Load-subset replay refinement (fixed regression added).** A named replay of +issue #2224's trace (a failed include demand during a marker replay) joins the +existing campaign. PR #2229 had fixed that defect, so the replay is green; it +needs no model change. + +The keyed `MultiSet.consolidate` rewrite and the Collection commit-loop cleanup +keep their observable behavior. The existing MultiSet consolidation oracle and +the Collection oracles own them; this PR does not change those owners. + +## RED, GREEN, and mutant evidence + +Grouped result key oracle +(`packages/db/tests/query/group-result-key-oracle.property.test.ts`): + +- RED on the parent commit: all 5 tests fail at the first checkpoint with + keys `undefined`, `null`, `false`, and `10n`. +- GREEN: 5 of 5. +- Mutants, all assertion failures: an encoding by `String(value)` fails + because `null` and `"null"` (and `true` and `"true"`) share a key; an + encoding of nullish values only fails because boolean keys remain. An + encoding of only the internal D2 key, with the public key unchanged, passes + every test: the Collection keys rows by the public group key, so the change + is equivalent within this domain. That part of the repair was removed. +- Reach in each campaign of 80 histories (fixed seed / random): a nullish + group published 38 / 32; a group beside a string that prints like it 21 / + 18; a key kept across a change 49 / 53. + +Sync write key witness (`packages/db/tests/collection.test.ts`): RED 4 of 4 +(`undefined`, `null`, boolean, bigint keys were staged); GREEN 4 of 4. The +witness also found that `InvalidKeyError` threw a `TypeError` for a bigint +key, because its development message used plain `JSON.stringify`; the message +now shows the bigint. + +SortedMap oracle (`packages/db/tests/SortedMap-oracle.test.ts`): + +- Before this PR's oracle repair, a mutant that sorted every key at each + restore passed all 30 tests. The work law closes that gap. +- A `NaN` key RED: with comparator order, rewriting a placed `NaN` key + duplicated it and deleting it removed the last key, because the slot check + used `===`. This was a regression inside this PR; `main` spliced at the + binary-search index. GREEN after slots compare by SameValueZero. +- GREEN: 7 of 7, and 21 of 21 supplemental examples in `SortedMap.test.ts`. +- Mutants, all assertion failures at the named checkpoint: + +| Mutant | Rejection | +| --- | --- | +| Sort every key at each restore | 108 comparator calls against a bound of 7 (with delete-only restores made free) | +| Sort kept keys with the dirty keys appended | 34 calls against a bound of 7 (same control) | +| Sort again with nothing deferred | calls at a clean ordered read | +| No scan fallback for a changed row | a neighbour is deleted | +| Match slots by `===` | a placed `NaN` key removes the last key | +| Keep dirty keys' old slots | order and membership mismatch | +| Apply deferred deletes immediately | a prune skips an entry | +| Place a deferred row by its value at the write | order mismatch | +| Replace the key array instead of writing in place | the supplemental live-iterator example fails | + +- Reach in each campaign of 100 histories (lowest across two local runs): + burst restores 54; an entry yielded after a prune deleted one 30; a placed + `NaN` key written without deferral 7; a restore whose bound is below n 19; a + changed placed row later written 11; a deferred row changed before restore + 11. The last three apply to comparator orders only. + +## Oracle guide audit + +| Requirement | Grouped result key oracle | SortedMap oracle | +| --- | --- | --- | +| ORC-001 | Law, authority (`TKey`, `InvalidKeyError`, maintainer decision), and limits are in the opening prose. | Law, authority (existing contract plus the maintainer's scope decision), and the not-promised list are in the opening prose. | +| ORC-002 | `expectedGroups` partitions plain rows by type and value with `NaN` equal to `NaN`. It does not import the compiler, value identity, or key encoding. | A native Map of numbers and a fresh full sort derive every observation. The work bound is a formula in n and k, not production's algorithm. | +| ORC-003 | Literate file: contract, model, grammar, driver, and refinement check each have prose beside the code. | Same structure; the three Collection-use laws sit in the opening prose and beside the driver branches that check them. | +| ORC-004 | Reconstruction: the probe that found the bug (nullish groups beside strings) and the bigint and `undefined` counterexamples are pinned or regenerate in the first runs. Ablation: without nullish values the nullish reach state and RED vanish; without lookalike strings the `String(value)` mutant survives; without booleans and bigints the nullish-only mutant survives; without changes the key-stability state is unreachable. Range: 1 to 6 rows over 16 group values, up to 4 changes; marginal cases include an empty source after deletes and a `NaN` group. Exclusion: a move or delete with no rows writes nothing; Dates, `-0`, and objects are outside the domain. | Reconstruction: four fixed histories (main's original, a mutated neighbour, a deferred delete before a burst, a placed `NaN` rewrite) replay through the shared driver. Ablation: without `mutate` two reach states and two mutant kills vanish; without the work counter the full-sort mutants survive (shown on the earlier oracle); without `prune` the iteration law has no generated history; without `NaN` keys the `===` mutant survives. Range: keys -3..3 and `NaN`, values -3..3, up to 40 actions, bursts of 17 to 41 keys. Exclusion: `mutate` targets only present keys; deletes during iteration are always deferred; keys outside the key type are not generated, because `sync.write` rejects them. | +| ORC-005 | Public `createLiveQueryCollection` over a mock sync source; `entries()` and `size` after preload and after each committed change. | SortedMap's own methods are the entry point (it is internal); every view, `size`, `has`, and `get` after each action or chosen checkpoint, comparator calls at each ordered read. Collection sync paths are pinned in `SortedMap.test.ts`. | +| ORC-006 | Mutants above, classified as assertion failures; one equivalent within the domain. | Mutants above, all assertion failures; a calibration test feeds wrong orders, a lost tie, a stale value, duplicates, and a missing key to the comparison. | +| ORC-007 | Fixed seed 92604118 and an unseeded campaign share the property, driver, comparison, and budget; `group-result-key.groups` is registered for seed and path replay. | The existing `sorted-map.*` fixed and unseeded campaigns and replay interface carry the new actions. | +| ORC-008 | Stateless partition; no model state. | `deferred`, `changedPlaced`, and `orderPromised` are kept: a changed deferred row and a changed placed row look alike until the next ordered read, where one must sort exactly and the other only keeps membership. The write-only `placed` set was removed. | +| ORC-009 | Group value, key, and publication use glossary terms; "lookalike" is a model-only label for a string that prints like a non-string value. | "Deferred", "placed", and "changed in place" are local SortedMap terms with no glossary counterpart; sync transaction and publication keep glossary meanings. | +| ORC-010 | `withOracleCleanup` cleans both Collections and preserves the primary failure. | No resources; fast-check reports the shrunk history. | +| ORC-011 | No shared semantic fault between the type-and-value partition and production's value identity was named inside the domain. Dates, whose identity is their timestamp, are excluded rather than modelled. | No shared fault between a full sort and incremental restore was named. | +| ORC-012 | This record. | This record. | +| ORC-013 | The lookalike strings distinguish a type-preserving encoding from `String(value)`; the boolean and bigint values distinguish a complete encoding from a nullish-only one. | The `restore-bound-below-order-size` premise is reached in 19 or more histories per campaign, and the full and presorted re-sorts exceed the bound there. | +| ORC-014 | The mock sync source is a controlled provider; the claim covers only the compiler's keying. Real adapters are not claimed. | No controlled premise is carried to a real provider. | +| ORC-015 | Three declared states, counts above. | Six declared states (three for comparator orders only), counts above. | +| ORC-016 | Pinned histories run the shared driver and comparison without their own expectations. | Example tests moved to `SortedMap.test.ts`; the oracle file keeps the property, its fixed histories as inputs, and checker calibration. | + +## Review items + +| Item | Disposition at the reviewed head | +| --- | --- | +| CR1 NaN unplace | Fixed: slots compare by SameValueZero; generated `NaN` keys and the `===` mutant above. An interim scope note had wrongly called `NaN` keys unpromised. | +| CR2 O(k n) deferred deletes | Fixed: dirty keys leave by membership in one pass; the work law and supplemental splice bounds reject per-key work. | +| CR3 throwing comparator | Not promised (maintainer scope decision); a comparator that throws is a programming error. | +| CR4 rows mutated without a write | Accepted design (maintainer decision A): their order is not promised; membership is, and is generated. | +| CR5 test gap for NaN and work | Fixed by the `NaN` dimension and the comparator-call work law. | +| CR6 reach on pending runtime keys | Superseded: runtime nullish keys no longer reach SortedMap. | +| CR7 boolean and bigint keys | Fixed at the producer: grouped keys and `sync.write` keep keys in the key type. | +| CR8 per-operation allocation | Fixed: immediate writes keep the binary-insert path. | +| CR9 absent delete skips restore | Fixed: delete restores order first. | +| CR10 removed 512 threshold | Fixed: the threshold remains. | +| S1 vacuous work bounds | Fixed: the comparator-call law bounds the merge pass that the splice counter cannot see. | +| S2 drop the `indexOf` value parameter | Refuted: immediate inserts and the restore pass values not yet in the searched array. | +| S3 model mirror of pending keys | Stale after the redesign. | +| S4 stale comment | Fixed. | +| S5 per-commit allocation | Duplicate of CR8 and CR10. | +| S6 history narrative in the coverage map | Fixed. | +| S7 `clear()` and live iterators | Superseded by the scope decision; main's live-iterator example remains. | +| CodeRabbit: `unplace` miss check for an `undefined` key | Valid when raised; its premise is removed, because an `undefined` key no longer reaches SortedMap. | +| CodeRabbit: extend an oracle for invalid sync keys | Partly valid: SortedMap is not the owner and the no-effect assertion already existed; the coverage map now states why this is a fail-fast invariant witness. | + +Siblings found during evaluation: a `-0` key reported as `-0` (now not +promised), an immediate write that lost its slot when the comparator threw +(not promised), and live iterators that skipped or repeated entries across +immediate writes (superseded by the scope decision). The removed `forEach` and +public export were approved by the maintainer. + +Loss audit: 17 raw review items = 10 fixed, 1 accepted design, 1 not promised +by decision, 2 superseded, 1 refuted, 1 stale, and 1 duplicate; plus 2 +CodeRabbit items (1 premise removed, 1 partly valid and documented) and 3 +siblings (2 not promised, 1 superseded). No source claim is omitted. + +## Limits + +The grouped key owner does not cover Dates, `-0`, objects, or multi-field +groups, which the aggregate select oracle owns, nor real sync adapters. +SortedMap does not promise the items listed under scope. Shipped-byte effects +were measured with a local esbuild approximation of `pnpm size:compare`, +because a registry 403 prevented the install that `size:compare` needs; they +are not part of this oracle verdict. + +## Update: merge with `origin/main` at `7e46f81ad` + +The branch merged `origin/main` at `7e46f81ad` in `26896c42a`. That change +(#2237) requires each registered property to name its owner, so +`group-result-key.groups` now has an owner entry and a replay test name in +`oracle-replay-manifest.ts`. No law, model, grammar, or comparison changed. +The replay manifest tests (40) and both oracles pass after the merge. + +A third CodeRabbit finding, on `ea4cf8792`, was valid: the guide note did not +say that a string with the reserved `\u0000tanstack-db:` prefix gets an +escaped key, or that a live query's `getKey` replaces grouped keys. The note +now says both. + +Shipped bytes: `pnpm size:compare` against the merge-base `b8d8ee07c` now +runs, with a local-only override for one dependency that the registry proxy +blocks. The gzip difference is -56 bytes for `app`, -38 to -61 bytes for the +other scenarios, and -2 bytes for the `@tanstack/db` package bundle. These +numbers replace the esbuild approximation in the limits above. diff --git a/docs/contributing/oracle-reviews/pr-861-compound-join-relevance.md b/docs/contributing/oracle-reviews/pr-861-compound-join-relevance.md new file mode 100644 index 0000000000..e1b2ee617b --- /dev/null +++ b/docs/contributing/oracle-reviews/pr-861-compound-join-relevance.md @@ -0,0 +1,15 @@ +# Historical assessment of the original compound-join implementation + +This assessment applied to discarded merge `9838e88f78a8571f214874a587c6373318030b2f`, +combining original PR head `82d50c00e580aeeabd54756b9efe61ca750e53cd` with main +`4070864414f26212dcef20e2537057bff8e21535` on 2026-10-05. + +That implementation failed special-value tuple equality and omitted additional +join conditions from query identity. Its implementation and example tests were +removed before the replacement. Its readiness verdict does not apply to the +current branch. + +The [archived assessment](https://github.com/TanStack/db/blob/ee490df80d200f701df805faf81d8105ba492527/docs/contributing/oracle-reviews/pr-861-compound-join-relevance.md) +preserves the old diagnostics and revision-specific receipts. The +[replacement evidence](2026-10-05-compound-joins.md) describes the current design, +oracle owners, replay commands, and coverage limits. diff --git a/docs/contributing/oracle-reviews/progressive-source-readiness-tla/InitialReadiness.tla b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/InitialReadiness.tla new file mode 100644 index 0000000000..5e2293410f --- /dev/null +++ b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/InitialReadiness.tla @@ -0,0 +1,369 @@ +---------------------------- MODULE InitialReadiness ---------------------------- +(***************************************************************************) +(* Initial-query readiness over one root source and an optional lazy *) +(* source (ARCHITECTURE.md law 10, with laws 6 and 7). *) +(* *) +(* The model checks the frozen design grammar for PR #2115. Every element *) +(* names the frozen preservation property (P1..P12) or grammar rule (G-name) *) +(* that it encodes. See README.md for the trace table and the exclusions. *) +(* *) +(* Two consumers compute readiness by their own rules: the live query *) +(* (read by isReady, preload, Suspense, queryOnce, and hydration) and a *) +(* skipInitial Effect. Each rule runs atomically after every step, as the *) +(* graph run does in production. The law itself is `Covered`, which is *) +(* stated independently of both rules. *) +(* *) +(* Weakening flags each encode one plausible wrong design. The safe *) +(* configurations set every flag to FALSE. *) +(***************************************************************************) +EXTENDS Naturals + +CONSTANTS + RootMode, \* "eager" or "onDemand" (P9: a source with no syncMode is eager) + HasLazy, \* the query has a lazy-join or include child source + Ordered, \* the root demand is an ordered window with one continuation (P8) + Resume, \* the root resumes from complete local rows (P11) + MaxAcq, \* bound on acquisitions + MaxDisturb, \* bound on truncates, rekeys, and must-refetches + \* Weakenings (each one plausible wrong design) + OldRule, \* gate a non-lazy on-demand root on Collection readiness + IgnoreEager, \* never gate on Collection readiness + EffectNoTakeover,\* Effect releases a truncated hold without adopting the replacement + EffectAbortLatch,\* Effect treats an aborted hold as a failure (shared latch) + AbortIsFailure, \* live query fails lazy demand that a truncate aborted + SettleWithoutRows,\* progressive load settles during a reset without rows + RetryNoRenew, \* a retried stream keeps the rejected snapshot + Recompute, \* readiness is recomputed instead of latched + WindowEarly \* the window continuation is issued after a readiness pass + +ASSUME RootMode \in {"eager", "onDemand"} + +Ids == 1..MaxAcq +Status == {"none", "pending", "settled", "aborted", "failed"} +Cause == {"none", "real", "stale"} +Phase == {"buffering", "resetting", "established", "errored"} + +VARIABLES + started, \* a subscriber or preload exists (P7) + acq, \* acquisitions: source, status, rows visible, current, Effect hold + nextId, + chain, \* number of window continuations issued for the current root demand + phase, \* progressive producer phase (onDemand root only) + snap, \* the established-snapshot deferred: "pending" | "resolved" | "rejected" + srcReady, \* root Collection readiness (markReady) + liveCause, \* why the live query errored, if it did + effCause, \* why the Effect was disposed by failure, if it was + disposed, \* cleanup of the consumers' owner + liveReady, effectReady, + coveredAtReady, rowsAtReady, \* history: law and soundness at the live transition + disturb, errors + +vars == <> + +Empty == [src |-> "root", st |-> "none", rows |-> FALSE, cur |-> FALSE, held |-> FALSE] +Pending(s, h) == [src |-> s, st |-> "pending", rows |-> FALSE, cur |-> TRUE, held |-> h] + +HasCur(a, s) == \E i \in Ids : a[i].cur /\ a[i].src = s +RootRowsVisible(a, ready) == + IF RootMode = "eager" THEN ready + ELSE \E i \in Ids : a[i].cur /\ a[i].src = "root" /\ a[i].rows + +\* G-lazy: lazy demand is issued in the same graph run that makes root rows visible. +NeedsLazy(a, ready) == HasLazy /\ RootRowsVisible(a, ready) /\ ~HasCur(a, "lazy") +AddLazy(a, n, ready) == IF NeedsLazy(a, ready) THEN [a EXCEPT ![n] = Pending("lazy", TRUE)] ELSE a +AfterLazy(a, n, ready) == IF NeedsLazy(a, ready) THEN n + 1 ELSE n +CanAlloc(k) == nextId + k - 1 <= MaxAcq + +--------------------------------------------------------------------------- +(* The law (P1), stated without either consumer's rule. *) +Covered == + /\ started /\ ~disposed + /\ RootMode = "eager" => srcReady + /\ RootMode = "onDemand" => HasCur(acq, "root") + /\ (HasLazy /\ RootRowsVisible(acq, srcReady)) => HasCur(acq, "lazy") + /\ (Ordered /\ RootMode = "onDemand") => chain = 1 + /\ \A i \in Ids : acq[i].cur => acq[i].st = "settled" + +\* Evidence soundness (P2): every current settled acquisition shows its rows. +RowsVisible == \A i \in Ids : (acq[i].cur /\ acq[i].st = "settled") => acq[i].rows + +\* The shared helper sourcePermitsInitialReadiness (G-permit), and its weakenings. +RootPermits == + IF OldRule THEN srcReady + ELSE RootMode = "onDemand" \/ IgnoreEager \/ srcReady + +LiveRule == + /\ started /\ ~disposed /\ liveCause = "none" + /\ RootPermits + /\ \A i \in Ids : acq[i].cur => acq[i].st = "settled" + +\* Effect: its holds are tracked promises; an aborted hold releases (G-hold). +EffectRule == + /\ started /\ ~disposed /\ effCause = "none" + /\ RootPermits + /\ \A i \in Ids : acq[i].held => acq[i].st \in {"settled", "aborted"} + +UpdateConsumers == + /\ liveReady' = IF Recompute THEN LiveRule' ELSE (liveReady \/ LiveRule') + /\ effectReady' = IF Recompute THEN EffectRule' ELSE (effectReady \/ EffectRule') + /\ coveredAtReady' = IF ~liveReady /\ liveReady' THEN Covered' ELSE coveredAtReady + /\ rowsAtReady' = IF ~liveReady /\ liveReady' THEN RowsVisible' ELSE rowsAtReady + +--------------------------------------------------------------------------- +Init == + /\ started = FALSE + /\ acq = [i \in Ids |-> Empty] + /\ nextId = 1 + /\ chain = 0 + /\ phase = IF Resume THEN "established" ELSE "buffering" + /\ snap = IF Resume THEN "resolved" ELSE "pending" + /\ srcReady = (Resume /\ RootMode = "onDemand") + /\ liveCause = "none" /\ effCause = "none" + /\ disposed = FALSE + /\ liveReady = FALSE /\ effectReady = FALSE + /\ coveredAtReady = TRUE /\ rowsAtReady = TRUE + /\ disturb = 0 /\ errors = 0 + +\* P7: acquisition starts only with a subscriber. +Start == + /\ ~started /\ CanAlloc(2) + /\ started' = TRUE + /\ LET a1 == IF RootMode = "onDemand" + THEN [acq EXCEPT ![nextId] = Pending("root", TRUE)] ELSE acq + n1 == IF RootMode = "onDemand" THEN nextId + 1 ELSE nextId + IN /\ acq' = AddLazy(a1, n1, srcReady) + /\ nextId' = AfterLazy(a1, n1, srcReady) + /\ UNCHANGED <> + /\ UpdateConsumers + +\* Buffered initial sync: fetchSnapshot installs rows for a root load. +DeliverRoot(i) == + /\ started /\ ~disposed /\ RootMode = "onDemand" /\ phase = "buffering" + /\ acq[i].cur /\ acq[i].src = "root" /\ acq[i].st = "pending" /\ ~acq[i].rows + /\ CanAlloc(1) + /\ LET a1 == [acq EXCEPT ![i].rows = TRUE] + IN /\ acq' = AddLazy(a1, nextId, srcReady) + /\ nextId' = AfterLazy(a1, nextId, srcReady) + /\ UNCHANGED <> + /\ UpdateConsumers + +DeliverLazy(i) == + /\ started /\ ~disposed + /\ acq[i].cur /\ acq[i].src = "lazy" /\ acq[i].st = "pending" /\ ~acq[i].rows + /\ acq' = [acq EXCEPT ![i].rows = TRUE] + /\ UNCHANGED <> + /\ UpdateConsumers + +\* Applied settlement (P2): a load settles after its rows are visible. A +\* settled first window segment issues its continuation in the same run (P8). +NeedsContinuation(i) == + Ordered /\ acq[i].src = "root" /\ chain = 0 /\ ~WindowEarly + +SettleWith(i, a0) == + LET a1 == [a0 EXCEPT ![i].st = "settled", ![i].rows = TRUE] + cont == NeedsContinuation(i) + a2 == IF cont THEN [a1 EXCEPT ![nextId] = Pending("root", TRUE)] ELSE a1 + n2 == IF cont THEN nextId + 1 ELSE nextId + IN /\ acq' = AddLazy(a2, n2, srcReady) + /\ nextId' = AfterLazy(a2, n2, srcReady) + /\ chain' = IF cont THEN 1 ELSE chain + +Settle(i) == + /\ started /\ ~disposed /\ CanAlloc(2) + /\ acq[i].cur /\ acq[i].st = "pending" /\ acq[i].rows + /\ SettleWith(i, acq) + /\ UNCHANGED <> + /\ UpdateConsumers + +\* Outside buffering, a progressive load follows the established snapshot (G-snap). +SettleOnSnapshot(i) == + /\ started /\ ~disposed /\ RootMode = "onDemand" /\ CanAlloc(2) + /\ phase # "buffering" /\ snap = "resolved" + /\ acq[i].cur /\ acq[i].src = "root" /\ acq[i].st = "pending" + /\ SettleWith(i, acq) + /\ UNCHANGED <> + /\ UpdateConsumers + +\* Weakening: the pre-fix Electric reset path resolved without rows. +SettleEarly(i) == + /\ SettleWithoutRows + /\ started /\ ~disposed /\ RootMode = "onDemand" /\ phase = "resetting" + /\ acq[i].cur /\ acq[i].src = "root" /\ acq[i].st = "pending" + /\ acq' = [acq EXCEPT ![i].st = "settled"] + /\ UNCHANGED <> + /\ UpdateConsumers + +\* A rejected snapshot fails its waiting load. The failure is real only if +\* the stream is errored now (P6); otherwise it is stale (P10). +FailOnSnapshot(i) == + /\ started /\ ~disposed /\ RootMode = "onDemand" + /\ phase # "buffering" /\ snap = "rejected" + /\ acq[i].cur /\ acq[i].src = "root" /\ acq[i].st = "pending" + /\ acq' = [acq EXCEPT ![i].st = "failed"] + /\ LET c == IF phase = "errored" THEN "real" ELSE "stale" + IN /\ liveCause' = IF liveCause = "none" THEN c ELSE liveCause + /\ effCause' = IF effCause = "none" /\ acq[i].held THEN c ELSE effCause + /\ UNCHANGED <> + /\ UpdateConsumers + +WindowContinuation == + /\ WindowEarly /\ Ordered /\ chain = 0 /\ CanAlloc(1) /\ ~disposed + /\ \E i \in Ids : acq[i].cur /\ acq[i].src = "root" /\ acq[i].st = "settled" + /\ acq' = [acq EXCEPT ![nextId] = Pending("root", TRUE)] + /\ nextId' = nextId + 1 /\ chain' = 1 + /\ UNCHANGED <> + /\ UpdateConsumers + +\* A real lazy load failure (P6). +FailLazy(i) == + /\ started /\ ~disposed + /\ acq[i].cur /\ acq[i].src = "lazy" /\ acq[i].st = "pending" + /\ acq' = [acq EXCEPT ![i].st = "failed"] + /\ liveCause' = IF liveCause = "none" THEN "real" ELSE liveCause + /\ effCause' = IF effCause = "none" THEN "real" ELSE effCause + /\ UNCHANGED <> + /\ UpdateConsumers + +\* P3: a truncate aborts the current acquisition of a source; the replacement +\* takes over its hold. Root truncate also restarts the window chain. +Obsolete(a, s) == + [i \in Ids |-> IF a[i].cur /\ a[i].src = s + THEN [a[i] EXCEPT !.cur = FALSE, !.rows = FALSE, + !.st = IF a[i].st = "pending" THEN "aborted" ELSE a[i].st] + ELSE a[i]] +AbortedPending(s) == \E i \in Ids : acq[i].cur /\ acq[i].src = s /\ acq[i].st = "pending" +AbortedHeld(s) == \E i \in Ids : acq[i].cur /\ acq[i].src = s /\ acq[i].st = "pending" /\ acq[i].held + +TruncateSrc(s, held) == + /\ acq' = [Obsolete(acq, s) EXCEPT ![nextId] = Pending(s, held)] + /\ nextId' = nextId + 1 + /\ chain' = IF s = "root" THEN 0 ELSE chain + /\ liveCause' = IF AbortIsFailure /\ s = "lazy" /\ AbortedPending(s) /\ liveCause = "none" + THEN "stale" ELSE liveCause + /\ effCause' = IF EffectAbortLatch /\ AbortedHeld(s) /\ effCause = "none" + THEN "stale" ELSE effCause + +Truncate(s) == + /\ started /\ ~disposed /\ disturb < MaxDisturb /\ CanAlloc(1) + /\ HasCur(acq, s) + /\ s = "root" => RootMode = "onDemand" + /\ TruncateSrc(s, ~EffectNoTakeover) + /\ disturb' = disturb + 1 + /\ UNCHANGED <> + /\ UpdateConsumers + +\* Lazy join keys change: the old demand is obsolete; new demand is a new hold. +Rekey == + /\ HasLazy /\ started /\ ~disposed /\ disturb < MaxDisturb /\ CanAlloc(1) + /\ HasCur(acq, "lazy") + /\ acq' = [Obsolete(acq, "lazy") EXCEPT ![nextId] = Pending("lazy", TRUE)] + /\ nextId' = nextId + 1 + /\ effCause' = IF EffectAbortLatch /\ AbortedHeld("lazy") /\ effCause = "none" + THEN "stale" ELSE effCause + /\ disturb' = disturb + 1 + /\ UNCHANGED <> + /\ UpdateConsumers + +--------------------------------------------------------------------------- +(* Progressive producer (onDemand root). *) +MustRefetch == + /\ RootMode = "onDemand" /\ phase \in {"buffering", "resetting"} /\ ~srcReady + /\ disturb < MaxDisturb /\ CanAlloc(1) + /\ phase' = "resetting" /\ snap' = "pending" + /\ IF started /\ ~disposed /\ HasCur(acq, "root") + THEN TruncateSrc("root", ~EffectNoTakeover) + ELSE UNCHANGED <> + /\ disturb' = disturb + 1 + /\ UNCHANGED <> + /\ UpdateConsumers + +\* Up-to-date: the full sync swaps in and the Collection becomes ready. +UpToDate == + /\ RootMode = "onDemand" /\ phase \in {"buffering", "resetting"} + /\ phase' = "established" /\ snap' = "resolved" /\ srcReady' = TRUE + /\ UNCHANGED <> + /\ UpdateConsumers + +\* A stream error rejects the snapshot and every load waiting on it. +StreamError == + /\ RootMode = "onDemand" /\ phase # "errored" /\ errors < 1 + /\ phase' = "errored" /\ snap' = "rejected" + /\ LET waiting(i) == started /\ ~disposed /\ phase # "buffering" /\ acq[i].cur + /\ acq[i].src = "root" /\ acq[i].st = "pending" + IN /\ acq' = [i \in Ids |-> IF waiting(i) THEN [acq[i] EXCEPT !.st = "failed"] ELSE acq[i]] + /\ liveCause' = IF (\E i \in Ids : waiting(i)) /\ liveCause = "none" THEN "real" ELSE liveCause + /\ effCause' = IF (\E i \in Ids : waiting(i) /\ acq[i].held) /\ effCause = "none" + THEN "real" ELSE effCause + /\ errors' = errors + 1 + /\ UNCHANGED <> + /\ UpdateConsumers + +\* onError retries: the stream recovers and the snapshot is renewed (P10). +Retry == + /\ phase = "errored" + /\ phase' = IF srcReady THEN "established" ELSE "resetting" + /\ snap' = IF RetryNoRenew THEN snap ELSE IF srcReady THEN "resolved" ELSE "pending" + /\ UNCHANGED <> + /\ UpdateConsumers + +\* Eager root: markReady after the full dataset applies (P9). +MarkReadyEager == + /\ RootMode = "eager" /\ ~srcReady /\ CanAlloc(1) + /\ srcReady' = TRUE + /\ IF started /\ ~disposed + THEN /\ acq' = AddLazy(acq, nextId, TRUE) + /\ nextId' = AfterLazy(acq, nextId, TRUE) + ELSE UNCHANGED <> + /\ UNCHANGED <> + /\ UpdateConsumers + +\* Cleanup aborts every pending acquisition and runs no first-ready callback. +Cleanup == + /\ started /\ ~disposed + /\ disposed' = TRUE + /\ acq' = [i \in Ids |-> IF acq[i].st = "pending" THEN [acq[i] EXCEPT !.st = "aborted"] ELSE acq[i]] + /\ UNCHANGED <> + /\ UpdateConsumers + +Next == + \/ Start \/ Rekey \/ MustRefetch \/ UpToDate \/ StreamError \/ Retry + \/ MarkReadyEager \/ Cleanup \/ WindowContinuation + \/ \E i \in Ids : DeliverRoot(i) \/ DeliverLazy(i) \/ Settle(i) \/ SettleOnSnapshot(i) + \/ SettleEarly(i) \/ FailOnSnapshot(i) \/ FailLazy(i) + \/ \E s \in {"root", "lazy"} : Truncate(s) + +Spec == Init /\ [][Next]_vars + +--------------------------------------------------------------------------- +(* Properties *) +TypeOK == + /\ acq \in [Ids -> [src : {"root", "lazy"}, st : Status, rows : BOOLEAN, + cur : BOOLEAN, held : BOOLEAN]] + /\ phase \in Phase /\ snap \in {"pending", "resolved", "rejected"} + /\ liveCause \in Cause /\ effCause \in Cause + +\* P1 (only if): the live query becomes ready only when the law holds. +ReadyMeansCovered == liveReady => coveredAtReady +\* P1 (if): when the law holds and nothing failed, the live query is ready. +ReadyWhenCovered == (Covered /\ liveCause = "none") => liveReady +\* P2: at the readiness transition, settled demand shows its rows. +RowsAtReady == liveReady => rowsAtReady +\* P4: both consumer rules agree while neither has failed. +ConsumersAgree == + (liveCause = "none" /\ effCause = "none" /\ ~disposed) => (liveReady = effectReady) +\* P3, P10: an obsolete acquisition or a stale snapshot never causes failure. +NoSpuriousFailure == liveCause # "stale" /\ effCause # "stale" +\* P6: a failed consumer never becomes ready after its failure. +FailureNeverReady == + [][/\ (~liveReady /\ liveReady') => liveCause' = "none" + /\ (~effectReady /\ effectReady') => effCause' = "none"]_vars +\* P7: nothing is acquired and nothing is ready before a subscriber. +DeferredAcquisition == ~started => (nextId = 1 /\ ~liveReady /\ ~effectReady) + +\* P5: readiness is monotone. Cleanup adds no first-ready transition. +Monotone == [][/\ liveReady => liveReady' + /\ effectReady => effectReady']_vars +NoReadyAfterCleanup == [][disposed => (/\ liveReady' = liveReady + /\ effectReady' = effectReady)]_vars +============================================================================= diff --git a/docs/contributing/oracle-reviews/progressive-source-readiness-tla/README.md b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/README.md new file mode 100644 index 0000000000..12fab24c2d --- /dev/null +++ b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/README.md @@ -0,0 +1,132 @@ +# Initial-readiness design grammar: TLA+ control + +This directory holds the bounded TLA+ control for the initial-query readiness +grammar of PR #2115. It checks the internal consequences of the grammar's +transition rules. It does not show that production refines the model. Model D +in `packages/db/tests/query/includes-temporal-oracle.test.ts` and the receiving +witnesses listed in `../progressive-source-readiness.md` own that claim. + +## Frozen preservation list + +The user confirmed these properties before extraction. S marks a +source-stated property, and I marks an analyst-inferred property. + +| ID | Property | Basis | +| --- | --- | --- | +| P1 | A consumer is initially ready exactly when every reachable current demand is settled and every eager source is ready. | S, ARCHITECTURE.md law 10 | +| P2 | A load settles only when its rows are visible or the source owns their absence. | S, law 7 | +| P3 | An obsolete or aborted acquisition neither satisfies nor blocks readiness. A truncate replacement takes over the hold. | S, law 6 and replay rules | +| P4 | Live-query readiness and the end of Effect `skipInitial` suppression happen at the same point. Readers of live-query status (`isReady`, preload, Suspense, `queryOnce`, hydration) inherit it. | I | +| P5 | Initial readiness is monotone across later truncate, rekey, and reset. | I | +| P6 | A real load failure errors a live query and disposes an Effect. It never produces readiness. | S, AGENTS.md | +| P7 | Without a subscriber or preload, nothing is acquired. | S, law 15 | +| P8 | An ordered window stays unpublished until its request chain settles. | S | +| P9 | An eager source gates readiness. A source with no `syncMode` is eager. | S | +| P10 | After `onError` retries a stream error, later loads settle when the stream recovers. | I, from J1 | +| P11 | A source that resumes from complete local rows settles its loads at once. | S, Electric oracle | +| P12 | Freshness after readiness is outside this law. | S, a limit | + +## Grammar to model trace + +| Grammar item | TLA+ element | +| --- | --- | +| Source mode and Collection readiness (P9) | `RootMode`, `srcReady`, `MarkReadyEager` | +| Acquisition: status, visible rows, current, Effect hold | `acq` records | +| Shared rule `sourcePermitsInitialReadiness` | `RootPermits` | +| The law, independent of both consumer rules (P1) | `Covered` | +| Live-query rule | `LiveRule`, latched by `UpdateConsumers` | +| Effect rule: tracked holds that release on settle or abort | `EffectRule`, `held` | +| G-lazy: lazy demand starts in the graph run that shows root rows | `AddLazy` in `Start`, `DeliverRoot`, `SettleWith`, `MarkReadyEager` | +| G-window: the continuation starts when the first segment settles (P8) | `NeedsContinuation`, `chain` | +| Applied settlement (P2) | `Settle` requires rows, `SettleOnSnapshot` installs rows | +| G-snap: a progressive load outside buffering follows the established snapshot | `SettleOnSnapshot`, `FailOnSnapshot`, `snap` | +| Truncate with replacement takeover (P3) | `Truncate`, `TruncateSrc`, `MustRefetch` | +| Rekey of lazy demand (P3) | `Rekey` | +| Real failures (P6) | `FailLazy`, `StreamError` | +| Retry and renew (P10) | `Retry` | +| Complete resume (P11) | `Resume` in `Init` | +| Deferred acquisition (P7) | `started`, `Start` | +| Cleanup | `Cleanup`, `NoReadyAfterCleanup` | + +Properties: `ReadyMeansCovered` and `ReadyWhenCovered` (P1), `RowsAtReady` (P2), +`NoSpuriousFailure` (P3, P10), `ConsumersAgree` (P4), `Monotone` (P5), +`FailureNeverReady` (P6), `DeferredAcquisition` (P7). P8, P9, and P11 enter +through `Covered` and the configurations. + +## Boundary matrix and exclusions + +Each axis lists the values in the model. Excluded values give a reason and +their owner. + +- **Consumers:** live query, `skipInitial` Effect. Status readers are not + separate rules. They read live-query status, and the exposure witnesses + check them in each framework. `toArrayWhenReady` and `stateWhenReady` + resolve through `preload()`. The audit found that they resolved on first + data, and the user chose to make them follow readiness. +- **Source modes:** an eager root; an on-demand root that buffers + progressively; an on-demand root that resumes from complete rows; an + on-demand lazy source. Excluded: an eager lazy source, whose readiness is + the eager rule (Model D eager shapes). +- **Shapes:** direct, ordered window with one continuation, lazy child. + Excluded: two on-demand roots (#2117), and include children as distinct + from lazy joins. Both use the same `AddLazy` rule in this abstraction. +- **Lifecycle events:** start, delivery, settle, truncate, rekey, + must-refetch, up-to-date, stream error, retry, real lazy failure, and + cleanup. At most two disturbances and one stream error. Excluded: unload + and restart (cleanup and restart oracle), and changes after readiness + (P12). +- **Checkpoints:** the readiness transition (history variables) and every + state (invariants). + +## Runs + +TLC 2.19 (rev 5a47802) through the Field Lab container, with `-deadlock`: + +| Configuration | Result | +| --- | --- | +| `safe-progressive-lazy` | no error, 11,426 distinct states | +| `safe-progressive-ordered` | no error, 1,713 distinct states | +| `safe-eager-lazy` | no error, 135 distinct states | +| `safe-resume-lazy` | no error, 1,679 distinct states | + +Each weakened configuration turns on one wrong design and checks only its +target property: + +| Configuration | Wrong design | Counterexample | Violated | +| --- | --- | --- | --- | +| `weak-old-rule` | gate an on-demand root on Collection readiness (#2113) | Start, DeliverRoot, Settle | `ReadyWhenCovered` | +| `weak-ignore-eager` | never gate on Collection readiness | Start | `ReadyMeansCovered` | +| `weak-effect-no-takeover` | Effect releases a truncated hold without its replacement | Start, MustRefetch | `ConsumersAgree` | +| `weak-effect-abort-latch` | Effect treats an aborted hold as a failure | Start, MustRefetch | `NoSpuriousFailure` | +| `weak-abort-is-failure` | live query fails truncated lazy demand (on `main`) | UpToDate, Start, SettleOnSnapshot, Truncate | `NoSpuriousFailure` | +| `weak-settle-without-rows` | a progressive load settles during a reset without rows | MustRefetch, Start, SettleEarly | `RowsAtReady` | +| `weak-retry-no-renew` | a retry keeps the rejected snapshot (J1) | Start, StreamError, Retry, FailOnSnapshot | `NoSpuriousFailure` | +| `weak-recompute` | readiness is recomputed, not latched | Start, UpToDate, SettleOnSnapshot, Cleanup | `Monotone` | +| `weak-window-early` | the continuation starts after a readiness pass | Start, UpToDate, SettleOnSnapshot | `ReadyMeansCovered` | + +## Encoding receipt + +The first safe run found a counterexample to `ReadyMeansCovered` in +`safe-progressive-lazy` and `safe-resume-lazy`: Start, SettleOnSnapshot. The +model made root rows visible at settlement but did not issue their lazy +demand. That broke G-lazy, so it was an encoding failure, not a grammar +failure. `SettleWith` now calls `AddLazy`. The failed run also ablates G-lazy: +without it, the law fails. + +## Limits + +- The model has one root and one lazy source, at most seven acquisitions, + and at most two disturbances. +- `Monotone` latches readiness through cleanup. In production, cleanup + moves the status to `cleaned-up`. The property claims only that cleanup + adds no first-ready transition. +- A load that is waiting when a stream error arrives fails, even if + `onError` later retries. The model and production agree on this. Whether + such a failure is real under P6 is a product decision. +- The model has no fairness and no liveness property. + +## Run + +```sh +~/.claude/skills/field-lab/scripts/tlc . -deadlock -config safe-progressive-lazy.cfg InitialReadiness +``` diff --git a/docs/contributing/oracle-reviews/progressive-source-readiness-tla/safe-eager-lazy.cfg b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/safe-eager-lazy.cfg new file mode 100644 index 0000000000..67ec479f72 --- /dev/null +++ b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/safe-eager-lazy.cfg @@ -0,0 +1,27 @@ +SPECIFICATION Spec +CONSTANTS + RootMode = "eager" + HasLazy = TRUE + Ordered = FALSE + Resume = FALSE + MaxAcq = 7 + MaxDisturb = 2 + OldRule = FALSE + IgnoreEager = FALSE + EffectNoTakeover = FALSE + EffectAbortLatch = FALSE + AbortIsFailure = FALSE + SettleWithoutRows = FALSE + RetryNoRenew = FALSE + Recompute = FALSE + WindowEarly = FALSE +INVARIANT TypeOK +INVARIANT ReadyMeansCovered +INVARIANT ReadyWhenCovered +INVARIANT RowsAtReady +INVARIANT ConsumersAgree +INVARIANT NoSpuriousFailure +INVARIANT DeferredAcquisition +PROPERTY Monotone +PROPERTY NoReadyAfterCleanup +PROPERTY FailureNeverReady diff --git a/docs/contributing/oracle-reviews/progressive-source-readiness-tla/safe-progressive-lazy.cfg b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/safe-progressive-lazy.cfg new file mode 100644 index 0000000000..79d6878e36 --- /dev/null +++ b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/safe-progressive-lazy.cfg @@ -0,0 +1,27 @@ +SPECIFICATION Spec +CONSTANTS + RootMode = "onDemand" + HasLazy = TRUE + Ordered = FALSE + Resume = FALSE + MaxAcq = 7 + MaxDisturb = 2 + OldRule = FALSE + IgnoreEager = FALSE + EffectNoTakeover = FALSE + EffectAbortLatch = FALSE + AbortIsFailure = FALSE + SettleWithoutRows = FALSE + RetryNoRenew = FALSE + Recompute = FALSE + WindowEarly = FALSE +INVARIANT TypeOK +INVARIANT ReadyMeansCovered +INVARIANT ReadyWhenCovered +INVARIANT RowsAtReady +INVARIANT ConsumersAgree +INVARIANT NoSpuriousFailure +INVARIANT DeferredAcquisition +PROPERTY Monotone +PROPERTY NoReadyAfterCleanup +PROPERTY FailureNeverReady diff --git a/docs/contributing/oracle-reviews/progressive-source-readiness-tla/safe-progressive-ordered.cfg b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/safe-progressive-ordered.cfg new file mode 100644 index 0000000000..6034c755e2 --- /dev/null +++ b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/safe-progressive-ordered.cfg @@ -0,0 +1,27 @@ +SPECIFICATION Spec +CONSTANTS + RootMode = "onDemand" + HasLazy = FALSE + Ordered = TRUE + Resume = FALSE + MaxAcq = 7 + MaxDisturb = 2 + OldRule = FALSE + IgnoreEager = FALSE + EffectNoTakeover = FALSE + EffectAbortLatch = FALSE + AbortIsFailure = FALSE + SettleWithoutRows = FALSE + RetryNoRenew = FALSE + Recompute = FALSE + WindowEarly = FALSE +INVARIANT TypeOK +INVARIANT ReadyMeansCovered +INVARIANT ReadyWhenCovered +INVARIANT RowsAtReady +INVARIANT ConsumersAgree +INVARIANT NoSpuriousFailure +INVARIANT DeferredAcquisition +PROPERTY Monotone +PROPERTY NoReadyAfterCleanup +PROPERTY FailureNeverReady diff --git a/docs/contributing/oracle-reviews/progressive-source-readiness-tla/safe-resume-lazy.cfg b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/safe-resume-lazy.cfg new file mode 100644 index 0000000000..9a294b4f12 --- /dev/null +++ b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/safe-resume-lazy.cfg @@ -0,0 +1,27 @@ +SPECIFICATION Spec +CONSTANTS + RootMode = "onDemand" + HasLazy = TRUE + Ordered = FALSE + Resume = TRUE + MaxAcq = 7 + MaxDisturb = 2 + OldRule = FALSE + IgnoreEager = FALSE + EffectNoTakeover = FALSE + EffectAbortLatch = FALSE + AbortIsFailure = FALSE + SettleWithoutRows = FALSE + RetryNoRenew = FALSE + Recompute = FALSE + WindowEarly = FALSE +INVARIANT TypeOK +INVARIANT ReadyMeansCovered +INVARIANT ReadyWhenCovered +INVARIANT RowsAtReady +INVARIANT ConsumersAgree +INVARIANT NoSpuriousFailure +INVARIANT DeferredAcquisition +PROPERTY Monotone +PROPERTY NoReadyAfterCleanup +PROPERTY FailureNeverReady diff --git a/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-abort-is-failure.cfg b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-abort-is-failure.cfg new file mode 100644 index 0000000000..dff9494521 --- /dev/null +++ b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-abort-is-failure.cfg @@ -0,0 +1,19 @@ +SPECIFICATION Spec +CONSTANTS + RootMode = "onDemand" + HasLazy = TRUE + Ordered = FALSE + Resume = FALSE + MaxAcq = 7 + MaxDisturb = 2 + OldRule = FALSE + IgnoreEager = FALSE + EffectNoTakeover = FALSE + EffectAbortLatch = FALSE + AbortIsFailure = TRUE + SettleWithoutRows = FALSE + RetryNoRenew = FALSE + Recompute = FALSE + WindowEarly = FALSE +INVARIANT TypeOK +INVARIANT NoSpuriousFailure diff --git a/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-effect-abort-latch.cfg b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-effect-abort-latch.cfg new file mode 100644 index 0000000000..216c0b1879 --- /dev/null +++ b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-effect-abort-latch.cfg @@ -0,0 +1,19 @@ +SPECIFICATION Spec +CONSTANTS + RootMode = "onDemand" + HasLazy = TRUE + Ordered = FALSE + Resume = FALSE + MaxAcq = 7 + MaxDisturb = 2 + OldRule = FALSE + IgnoreEager = FALSE + EffectNoTakeover = FALSE + EffectAbortLatch = TRUE + AbortIsFailure = FALSE + SettleWithoutRows = FALSE + RetryNoRenew = FALSE + Recompute = FALSE + WindowEarly = FALSE +INVARIANT TypeOK +INVARIANT NoSpuriousFailure diff --git a/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-effect-no-takeover.cfg b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-effect-no-takeover.cfg new file mode 100644 index 0000000000..7c63e713c4 --- /dev/null +++ b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-effect-no-takeover.cfg @@ -0,0 +1,19 @@ +SPECIFICATION Spec +CONSTANTS + RootMode = "onDemand" + HasLazy = TRUE + Ordered = FALSE + Resume = FALSE + MaxAcq = 7 + MaxDisturb = 2 + OldRule = FALSE + IgnoreEager = FALSE + EffectNoTakeover = TRUE + EffectAbortLatch = FALSE + AbortIsFailure = FALSE + SettleWithoutRows = FALSE + RetryNoRenew = FALSE + Recompute = FALSE + WindowEarly = FALSE +INVARIANT TypeOK +INVARIANT ConsumersAgree diff --git a/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-ignore-eager.cfg b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-ignore-eager.cfg new file mode 100644 index 0000000000..3cb3641d30 --- /dev/null +++ b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-ignore-eager.cfg @@ -0,0 +1,19 @@ +SPECIFICATION Spec +CONSTANTS + RootMode = "eager" + HasLazy = FALSE + Ordered = FALSE + Resume = FALSE + MaxAcq = 7 + MaxDisturb = 2 + OldRule = FALSE + IgnoreEager = TRUE + EffectNoTakeover = FALSE + EffectAbortLatch = FALSE + AbortIsFailure = FALSE + SettleWithoutRows = FALSE + RetryNoRenew = FALSE + Recompute = FALSE + WindowEarly = FALSE +INVARIANT TypeOK +INVARIANT ReadyMeansCovered diff --git a/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-old-rule.cfg b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-old-rule.cfg new file mode 100644 index 0000000000..855ebb1e5f --- /dev/null +++ b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-old-rule.cfg @@ -0,0 +1,19 @@ +SPECIFICATION Spec +CONSTANTS + RootMode = "onDemand" + HasLazy = FALSE + Ordered = FALSE + Resume = FALSE + MaxAcq = 7 + MaxDisturb = 2 + OldRule = TRUE + IgnoreEager = FALSE + EffectNoTakeover = FALSE + EffectAbortLatch = FALSE + AbortIsFailure = FALSE + SettleWithoutRows = FALSE + RetryNoRenew = FALSE + Recompute = FALSE + WindowEarly = FALSE +INVARIANT TypeOK +INVARIANT ReadyWhenCovered diff --git a/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-recompute.cfg b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-recompute.cfg new file mode 100644 index 0000000000..d0aa46b93f --- /dev/null +++ b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-recompute.cfg @@ -0,0 +1,19 @@ +SPECIFICATION Spec +CONSTANTS + RootMode = "onDemand" + HasLazy = FALSE + Ordered = FALSE + Resume = FALSE + MaxAcq = 7 + MaxDisturb = 2 + OldRule = FALSE + IgnoreEager = FALSE + EffectNoTakeover = FALSE + EffectAbortLatch = FALSE + AbortIsFailure = FALSE + SettleWithoutRows = FALSE + RetryNoRenew = FALSE + Recompute = TRUE + WindowEarly = FALSE +INVARIANT TypeOK +PROPERTY Monotone diff --git a/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-retry-no-renew.cfg b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-retry-no-renew.cfg new file mode 100644 index 0000000000..9eccc8fd7a --- /dev/null +++ b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-retry-no-renew.cfg @@ -0,0 +1,19 @@ +SPECIFICATION Spec +CONSTANTS + RootMode = "onDemand" + HasLazy = FALSE + Ordered = FALSE + Resume = FALSE + MaxAcq = 7 + MaxDisturb = 2 + OldRule = FALSE + IgnoreEager = FALSE + EffectNoTakeover = FALSE + EffectAbortLatch = FALSE + AbortIsFailure = FALSE + SettleWithoutRows = FALSE + RetryNoRenew = TRUE + Recompute = FALSE + WindowEarly = FALSE +INVARIANT TypeOK +INVARIANT NoSpuriousFailure diff --git a/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-settle-without-rows.cfg b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-settle-without-rows.cfg new file mode 100644 index 0000000000..aaeea1e612 --- /dev/null +++ b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-settle-without-rows.cfg @@ -0,0 +1,19 @@ +SPECIFICATION Spec +CONSTANTS + RootMode = "onDemand" + HasLazy = FALSE + Ordered = FALSE + Resume = FALSE + MaxAcq = 7 + MaxDisturb = 2 + OldRule = FALSE + IgnoreEager = FALSE + EffectNoTakeover = FALSE + EffectAbortLatch = FALSE + AbortIsFailure = FALSE + SettleWithoutRows = TRUE + RetryNoRenew = FALSE + Recompute = FALSE + WindowEarly = FALSE +INVARIANT TypeOK +INVARIANT RowsAtReady diff --git a/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-window-early.cfg b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-window-early.cfg new file mode 100644 index 0000000000..091827c4b1 --- /dev/null +++ b/docs/contributing/oracle-reviews/progressive-source-readiness-tla/weak-window-early.cfg @@ -0,0 +1,19 @@ +SPECIFICATION Spec +CONSTANTS + RootMode = "onDemand" + HasLazy = FALSE + Ordered = TRUE + Resume = FALSE + MaxAcq = 7 + MaxDisturb = 2 + OldRule = FALSE + IgnoreEager = FALSE + EffectNoTakeover = FALSE + EffectAbortLatch = FALSE + AbortIsFailure = FALSE + SettleWithoutRows = FALSE + RetryNoRenew = FALSE + Recompute = FALSE + WindowEarly = TRUE +INVARIANT TypeOK +INVARIANT ReadyMeansCovered diff --git a/docs/contributing/oracle-reviews/progressive-source-readiness.md b/docs/contributing/oracle-reviews/progressive-source-readiness.md new file mode 100644 index 0000000000..0544eeb2ea --- /dev/null +++ b/docs/contributing/oracle-reviews/progressive-source-readiness.md @@ -0,0 +1,316 @@ +# Source Collection readiness and initial-query readiness + +Base: `f6857fee9`. This record covers the working-tree change that adds Model D +to `packages/db/tests/query/includes-temporal-oracle.test.ts`. + +## Report and law + +A user reported ([TanStack/db#2113](https://github.com/TanStack/db/issues/2113)) that `useLiveSuspenseQuery` over a progressive Electric +collection suspends until the full sync completes, even when the query's rows +are present. The cause was in core. `allRequiredSourcesReady()` exempted an +on-demand source from its Collection readiness only on the lazy side of a join. +A progressive source reports `syncMode: 'on-demand'` but stays `loading` until +its full sync, so a query rooted on it waited for the whole dataset. + +Law (ARCHITECTURE.md, initial readiness and law 10): initial-query readiness +requires every reachable demand to be covered by a settled request and every +source that serves no subset demand to be Collection-ready. An on-demand +source's own Collection readiness does not gate it. An Effect with +`skipInitial` ends suppression at the same point. + +Authority: ARCHITECTURE.md law 10 already stated readiness as demand coverage. +The [TanStack DB 0.5 post](https://tanstack.com/blog/tanstack-db-0.5-query-driven-sync) +defines progressive mode: "First query loads immediately (on-demand)" while the +collection "syncs full dataset in background". The source readiness gate came +from the earlier `allCollectionsReady()` rule and had no separate authority. + +The same review found a related Effect defect. An Effect with `skipInitial` over +an on-demand source that is already ready did not wait for its initial subset +load, so the subset rows arrived as `enter` events. + +## Bug-class boundary + +- **Contract:** initial-query readiness and Effect initial suppression. +- **Histories:** every legal order of delivery, settlement, source readiness, + and one later change for a source whose Collection readiness lags its + settled subsets (Model D grammar). +- **Production paths:** a live-query Collection preload and `createEffect` + over direct and lazy-join on-demand roots and two eager roots, one with + `syncMode: 'eager'` and one with the default. The live query + also covers a single-result root (the reported `findOne` trace), an ordered + window, and an include child. +- **Observations:** `isReady()`, preload settlement, visible rows, Effect + events, and acquisitions at each step checkpoint. + +## Witnesses and calibration + +Original witness: the issue's standalone script passes with the fix and fails +on the original build with `queryReady: false, preloadResolved: false`. Its +shape is the `find-one` cell. Live query `direct: [deliver, settle, source-ready]` fails at +checkpoint 2 on the original code with `ready: false, preloadSettled: false` +while its row is visible. That is the reported behavior. + +Mutants, each run against Model D. Every outcome is an assertion failure at the +named checkpoint: + +| Mutant | Rejecting cells | Checkpoint | +| ------------------------------------------------ | -------------------------------------- | ---------- | +| Live query: lazy-only exemption (original) | 4 `direct`, 4 `find-one`, 4 `ordered` | 2–3 | +| Live query: ignore source readiness | 6 `eager-root`, 6 `eager-default` | 0 | +| Effect: suppress until source readiness | `direct`, `join` with a mid-sync change | 3 | +| Effect: ignore source readiness | 4 `eager-root`, 4 `eager-default` | 1 | +| Effect: on-demand exempt, no initial participant | 24 `direct`/`join` | 1–2 | +| Live query: exempt every source not set `eager` | 6 `eager-default` | 0 | +| Effect: no participant for lazy demand | 12 `join` | 1–2 | +| Effect: no participant for the root initial load | 12 `direct` | 1–2 | + +The last three come from the second review round. Before the `eager-default` +shape existed, the "not set `eager`" mutant passed Model D, because its eager +fixture always set `syncMode` explicitly; only the deferred-acquisition oracle +rejected it. Three more mutants survive and are equivalent within the +contract: + +- Exempting an on-demand source only while it is `loading` differs only for an + idle, errored, or cleaned-up source. An errored or cleaned-up source puts the + live query in error first. A preload or subscriber starts an idle source's + sync run, and a deferring query has its own readiness rule (law 15). +- Tracking initial loads without `skipInitial` cannot differ, because + `initialLoadComplete` starts `true` without `skipInitial` and never resets. + The production guard was redundant and is removed. +- Treating a rejected initial load as success cannot differ, because the + rejection calls `onSourceError`, which disposes the Effect. + +Receiving witnesses (ORC-014): + +- `electric-live-query.test.ts`, "a live query is ready once its subset + loads": a real Electric progressive source supplies the premise "subset + settled, Collection loading". It fails on the original code by assertion. +- "a skipInitial effect over an on-demand source suppresses its subset rows": + real Electric on-demand. It fails on the original code with `[1]`. +- "a skipInitial effect suppresses only its subset rows" (progressive) passes + on the original code. Electric buffers progressive changes until the full + sync, so it cannot supply a change between settlement and readiness. It + rejects the no-participant mutant. +- `useLiveSuspenseQuery.test.tsx`, "releases with useLiveQuery isReady when a + lagging source's subset settles": fails on the original code. + +## Requirement outcomes + +- **ORC-001:** The law, its authority, and its limits are in the Model D prose + and the architecture document. +- **ORC-002:** `expectedLiveQueryObservation` and `expectedEffectObservation` + compute the result from four model facts and the source mode. They import no + production classifier. +- **ORC-003:** The prose beside the model, grammar, driver, and projection + explains each one. The opening law list adds law 7. +- **ORC-004 and ORC-007:** Not applicable. The grammar is a fixed, exhaustive + enumeration (108 cells), not a generated property. Exclusion: `settle` before + `deliver` would contradict applied settlement, so it is not generated. +- **ORC-005:** Real Collections, `createLiveQueryCollection`, `preload()`, and + `createEffect`. The observations are public. +- **ORC-006:** See the mutant table. +- **ORC-008:** Each fact is needed. `delivered` changes rows, `settled` changes + on-demand readiness, `sourceReady` changes eager readiness, and + `writtenAfterReadiness` changes Effect events. Each is shown by a mutant kill + above. +- **ORC-009:** "Lagging source" is a model label for an on-demand source whose + `markReady()` comes after its settled subsets. `source-ready` maps to + `markReady()`, and `settle` maps to resolving every acquisition the consumer + issued. +- **ORC-010:** Cleanup uses `finishTemporalCleanup`, which keeps the primary + mismatch and each cleanup failure. +- **ORC-011:** Not applicable. No reviewer named a fault that the model and + production could share. +- **ORC-013:** The eager root and the mid-sync change are the nearby witnesses + that reject the two wrong boundaries. +- **ORC-014:** See the receiving witnesses. + +## Review rounds + +Four fresh reviewers attacked this change with the oracle-review skill. The +enforcement and law reviewers found no defects, but they gave +source-inspection verdicts and did not execute the mutants or tensions they +claimed to check. The mutant reviewer reported every mutant killed by all 29 +React tests, including Effect mutants that the React file does not exercise. +That is a setup failure, not a comparison result, so the author reran its +mutant ideas against Model D. That run produced the second-round rows above. + +## Code-review round + +A `/code-review` pass found two regressions in the first Effect repair and two +in Electric that the new readiness rule exposed: + +- **Effect hold latch.** The first repair put initial loads on the + ordered-repair gate, whose failure flag never clears. A truncate during the + initial load, or a lazy-join rekey before the first settle, aborts the + pending acquisition without disposing the Effect, so the Effect went silent + for good. Model D's Effect lane now crosses each history with a `truncate` + or `rekey`, and its fixture rejects aborted acquisitions. RED: 30 disturbed + cells failed at the first write after readiness. The repair gives initial + loads their own hold: an aborted load releases it, and a truncate replacement + takes it over. Two narrower repairs are rejected. Keeping the shared latch + but passing the truncate replacement fails 10 rekey cells. Releasing without + the takeover fails 80 truncate cells. +- **Electric reset settlement.** Outside its buffered initial sync, a + progressive load installed no rows yet resolved at once. During a + must-refetch before the first ready, the query became ready and empty. The + phase matrix in `electric-live-query.test.ts` fails on the original Electric + code in both reset phases and passes on the original core rule. The repair + settles such a load when the collection is first ready. A first version that + also waited during a resumed stream failed six existing Electric oracle tests + that pin the complete-resume contract, and was narrowed to resets only. + The resumed-stream phase is the neighboring witness. + +## High-effort review round + +A second `/code-review` pass (head `2812be07f`) made ten findings. Outcomes: + +- **Electric progressive loads now wait on the established snapshot.** The + separate reset waiter resolved on any abort, leaked its listener, and did + not follow the swap's applied receipt. Progressive streams now reuse the + `fullSnapshotReady` deferred. A must-refetch renews it, the up-to-date + commit resolves it with its receipt, a stream abort rejects it, and a + complete resume starts it resolved. The listener and abort findings are + fixed by removing that code path. For the receipt window, neither a + persisting transaction nor a held durable write made the swap's row + invisible after up-to-date. The repair covers that window by construction, + and the persisted test asserts the law ("ready implies the row is visible"). + A witness that reaches the window is still missing. +- **Refuted: early release of the Effect's hold.** An initial load's result + rejects only on cleanup, which disposes the Effect first (law 14), on demand + retirement, or on unsubscribe. Re-acquisition after detachment forwards the + replay's completion. +- **Law wording.** The law said "every source that serves no subset demand", + but the code checks for eager sources. The law now names eager sources. +- **Model independence.** Model D's mode rule mirrors production's + classification. Each on-demand cell now compares its consumer with a + metamorphic twin over a source that is ready from its start. Under the + original rule, the twin is ready while the lagging consumer is not, so the + twin alone rejects that design. +- **Naming and duplication.** The Effect's source check is now + `sourcesPermitInitialReadiness`, and both Effect holds share one replacement + handoff while keeping their own failure policies. +- **Open design decision.** A progressive source drops txids until its full + sync. A mutation on a ready progressive query can therefore wait for that + sync in `awaitTxId`. `useLiveQuery` already rendered rows in this window + before this change; Suspense now does too. + +## Third review round + +A third high-effort review (head `19354ffce`) made ten findings: + +- **Fixed: progressive loads after an `onError` retry.** A stream error + rejected the shared snapshot deferred, and only full-log on-demand streams + renewed it on retry. Every later progressive load rejected with the old + error. RED: a new Electric test fails on this branch and passes on `main`. + Progressive streams now take the same retry renewal. +- **Fixed: disturbed live-query and `queryOnce` lanes.** Model D now crosses + every on-demand consumer with a truncate or a parent rekey. This found a + defect that already exists on `main`: a truncate of a lazy source during + the initial load aborted its pending demand, and the live query entered + error. 80 cells failed at the first checkpoint after the truncate. The + lazy demand now follows the truncate replacement, as the Effect hold does. +- **Fixed by construction:** a failed initial load no longer reads the + Effect's subscription before it exists, a disposed Effect takes no new + hold, and Electric's progressive snapshot getter is required, without + dead fallbacks. +- **Cleanup:** the Effect class JSDoc sits on the class again, and the new + helper and test use no `any`. +- **Not changed:** a lazy demand settlement can schedule two graph runs. The + second run is idempotent, and avoiding it needs another parameter on the + hold. +- **Tracked elsewhere:** `awaitTxId` during the full sync (#2120) and the + narrower source-capability design (#2118). + +## Predicted neighbors + +The law predicts behavior for other readiness consumers. Each prediction now +has a witness that fails on the original rule: + +- `queryOnce` resolves when the subset settles (Model D's `query-once` lane: + the original rule fails 4 cells). +- A live-query observer hands a hydration seed off at subset settlement + (`live-query-observer.test.ts`). +- Network-first initial rendering cannot combine with an on-demand source. + The persisted-readiness oracle rejects that configuration, so the new rule + cannot bypass the network-first gate. +- A ready progressive query's rows update only at the full sync's swap + (`electric-live-query.test.ts`, a pinned limit). + +## Adapter audit + +The author checked when each on-demand adapter calls `markReady()` compared +with its subset loads: + +- **query-db:** `markReady()` waits for startup retention maintenance, and + `createQueryFromOpts` holds every subset load until that maintenance + finishes. A settled subset therefore implies the maintenance completed. +- **PowerSync on-demand:** calls `markReady()` while sync starts, so its + readiness does not lag. +- **TrailBase on-demand:** `load` does not wait for `recordApi.subscribe`, and + `markReady()` follows the subscription. A live query can now become ready + before the change listener is attached. The visible rows are unchanged. The + fetch-versus-listen gap that the adapter's comment describes already applied + to core subset loads; readiness no longer hides it. +- **RxDB:** has no on-demand mode. + +## Exposure, grammar, and async-hold round + +After three review rounds kept finding defects, the author audited every +producer and consumer of the readiness signal. The author designed the +grammar from the boundary matrix, checked it with TLA+, and listed every +outcome of every async hold. + +- **Grammar.** `progressive-source-readiness-tla/` holds a bounded TLA+ + model of the frozen grammar, its trace table, the boundary matrix, and the + exclusions. Four safe configurations pass. Nine weakened configurations + each produce the expected counterexample: the original rule, ignoring + eager readiness, both Effect hold defects, the lazy-truncate failure, + settling during a reset without rows, a retry without renewal, + recomputed readiness, and a late window continuation. One encoding + failure (lazy demand missing after a snapshot settle) was corrected, with a + receipt. +- **Consumers.** The shared framework conformance suite now has a lagging + on-demand source and the readiness law for a list query, a `findOne` query, + and the infinite query. The original rule fails each new scenario in every + driver (13 failures, all assertions). Solid's Suspense keeps its own + witness, because the shared suite has no Suspense surface. +- **Fixed: `toArrayWhenReady()` and `stateWhenReady()`.** They resolved as + soon as the Collection held a row, so a live query's read could resolve + before readiness. They now resolve through `preload()`. Model D's two new + read lanes start the read after `deliver`. The original shortcut fails 236 + cells, all at that checkpoint. +- **Producers.** Witnesses under a real live query show that query-db, + PowerSync, TrailBase, and the SQLite persisted wrapper settle a load only + with its rows visible, reject real failures, and install no rows for a + released demand. These adapters call `markReady()` early, so the witnesses + check applied settlement, not the readiness rule. PowerSync resolves an + aborted acquisition instead of rejecting it; the generation check ignores + it. +- **TrailBase widened.** An `it.fails` witness shows that a query can now be + first ready before the change subscription starts. The original rule + passes it. +- **Async holds.** Six holds by eight outcomes: 12 new tests, 8 of 11 mutants + killed, and 3 equivalent survivors. A progressive load that is waiting when + a stream error arrives rejects, even if `onError` retries; a test pins + this. + +## Open cells + +- TrailBase on-demand: a change committed between a subset fetch and the + change subscription can be missed. The owner is + `packages/trailbase-db-collection/tests/ORACLE.md`. This predates the change. + +- Effect `skipInitial` over an ordered window or an include child has no lane. +- Vue, Svelte, and Angular have no Suspense surface in the shared suite. +- A persisted follower tab's load may settle before the leader's rows apply + in that tab. Only the two-tab OPFS e2e suite can show this; it has not run. +- Electron IPC, two on-demand roots, and include children apart from lazy + joins are not exercised. +- Whether a load that was waiting at a retried stream error is a real failure + is a product decision. +- The Electric e2e progressive suite has no run receipt (see the coverage map). +- A rejected acquisition or a source error while a source lags is outside + Model D. +- Before the full sync, an Electric progressive query's rows do not receive + live changes. The source contract owns that behavior, not this law. diff --git a/docs/contributing/oracle-reviews/proxy-revert-replaced-object.md b/docs/contributing/oracle-reviews/proxy-revert-replaced-object.md new file mode 100644 index 0000000000..3655802080 --- /dev/null +++ b/docs/contributing/oracle-reviews/proxy-revert-replaced-object.md @@ -0,0 +1,194 @@ +# Draft revert after replacing a nested object + +Reviewed source: branch `fix-proxy-revert-changed-key`, based on `origin/main` +`fe284ccbd`. The commit that adds this record is the reviewed head. + +## Contract and evidence + +A draft reports a key as changed only when the key's final value differs from +its original under draft equality. The authority is the revert law in the +opening prose of `packages/db/tests/proxy-revert-oracle.property.test.ts`: "a +write that makes a value structurally equal to its original again is a revert, +and a fully reverted draft reports no change." A native mutator method (`push`, +`set`, `reverse`) marks a value changed without a revert check; the +native-operation owner, `proxy-native-methods-oracle.property.test.ts`, owns +that law. + +The random campaign of the revert oracle failed on `main` with seed +`-266044985`, path `163:0:2:3`, in the nested round-trip property. The shrunk +history is: + +1. Original row `{ f: { a: 0 } }`. +2. `draft.f = {}`. +3. `draft.f.a = 0`. + +`getChanges()` reported `f`, although the final `f` equals the original. The +nested proxy for the replaced object compared the nested write with its own +snapshot (`{}`), not with the row's original, and marked the edge `f` as +assigned. The root then reported every assigned key without a value check. The +production code failed; the model and the law were correct. + +The fix makes `getChanges()` compare an assigned key with the original row, +unless a native mutator changed that key or the key is absent from the +original. A tracker records which keys a native mutator changed; an assignment +to the key clears that record. `withFlatChangeTracking` compares each final +field with the original, so it does not have this defect. + +The fixed campaign did not reach the failing history, because the round-trip +grammar replaced the field only through random intermediate operations. The +grammar now has a `replace` mode that assigns a new object without `a` and then +restores `a` through a nested write. + +## Results + +| Check | Original code | Fixed code | +| --- | --- | --- | +| Pinned: replaced object restored by a nested write | Fails (`expected [ 'f' ] to deeply equal []`) | Passes | +| Pinned: replaced object restored, symbol key kept | Fails | Passes | +| Nested round trips, fixed seed `2026101` | Fails with the `replace` mode | Passes | +| Nested round trips, random | Fails | Passes | +| Proxy suites (`tests/proxy*`, revert oracle) | 4 failures | 478 passed, six consecutive runs | + +## Mutants + +| Mutant | Outcome | +| --- | --- | +| Report every assigned key without a value check (the original code) | Assertion failure: both pinned cases and both round-trip campaigns | +| Compare an assigned key after converting a Set to an array | Assertion failure: pinned case "an array replaced by an empty Set is a change" (the random partial-revert campaign also catches it) | +| Ignore the native-mutator record | Assertion failure: pinned case "reversing [-0, 0] is a change although the result is draft-equal" (the random typed-array campaign also catches it) | +| Remove the `hasOwn` check for an assigned object | Removed from the fix: equivalent within the domain, because an object never equals an absent value | + +## ORC-012 requirement audit + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | Applicable. The law and its authority are above. The claim is limited to `getChanges()` of `createChangeProxy` drafts. | +| ORC-002 | Applicable. The expected changes come from the revert oracle's spec model, which applies operations to plain-data specs and never reads a draft. | +| ORC-003 | Applicable. The revert oracle's opening prose states the law, the model, the grammar, and the checkpoint. The `replace` mode has a comment beside its generator. | +| ORC-004 | Applicable. The new mode reconstructs the failing history in the fixed campaign. The pinned cases cover the shrunk history and a variant with a symbol key. | +| ORC-005 | Applicable. The driver writes through the production draft and reads `getChanges()`, the public observation. | +| ORC-006 | Applicable. The mutant table above classifies each mutant. | +| ORC-007 | Applicable. The revert oracle runs a fixed seed and a random campaign with direct replay through `TANSTACK_DB_PROXY_REVERT_SEED` and `_PATH`. | +| ORC-008 | Inapplicable. The model is a spec state with no new state. | +| ORC-009 | Applicable. "Native mutator" means a method call that the draft forwards to the value (`createModifyingMethodHandler`, Map and Set `set`, `add`, `delete`). A read under a frozen key is not one: it records the key as assigned, so the value check decides. | +| ORC-010 | Applicable. The drafts hold no resources, and failures report the history and the observation. | +| ORC-011 | Applicable. The native-operation owner is a second formulation for the native-mutator exception, and it caught the first fix. | +| ORC-012 | This record. | +| ORC-013 | Inapplicable. No threshold law. | +| ORC-014 | Inapplicable. No controlled provider. | + +## Follow-up review: native writes mixed with assignments + +A review of the fix found two cases where the native-mutator record outlived +the value it described. Both also fail on `main`. + +1. A native write that an assignment replaces could leave its mark on an + ancestor. After `draft.f.arr.pop()`, `draft.f.g = {}`, `draft.f.arr = [0]`, + and `draft.f.g.a = 0` on `{ f: { arr: [0], g: { a: 0 } } }`, the root + still held a native mark for `f` and reported it. The assignment cleared + the mark on `f`, but not the root's mark that stood for it. +2. A mutator called through a handle that the callback had replaced marked + the parent's edge native, although the edge no longer held that array. + +The fix keeps the design. A native mark propagates only along edges that +still hold the child, and clearing the last mark under an edge clears the +ancestor marks above it. A read under a frozen key now records the key as +assigned without a native mark, so a read that writes nothing reports no +change, and a write through the raw copy is still found by the value check. + +Why the oracles missed these: the revert grammar wrote only by assignment +and delete, so it never reached a key a native mutator had changed, and the +native-methods oracle calls one method on a fresh row, never mixed with +assignments, nested replacement, or retained handles. The law was right; its +grammar did not reach the histories. The revert oracle's last block now +generates array mutators mixed with assignments, a nested object, and a +retained handle, and pins three histories one and two levels deep. A +focused case nests the native site three levels deep. The frozen-key table +gains an object read. + +| Check | `main` | `e3178d2a6` | Fixed code | +| --- | --- | --- | --- | +| Native histories, fixed seed `2026101` and random | Fail | Fail | Pass | +| Pinned: native write restored by an assignment | Fails | Fails | Passes | +| Pinned: mutator through a detached handle | Fails | Fails | Passes | +| Pinned: restoring the last native write clears every ancestor | Fails | Fails | Passes | +| Native write three levels deep restored | Fails | Fails | Passes | +| Object read under a frozen key | Fails | Fails | Passes | + +| Mutant | Outcome | +| --- | --- | +| Clear only the nearest ancestor's mark | Assertion failure: the three-level case | +| Never clear ancestor marks | Assertion failure: both native campaigns and the ancestor pinned case | +| Propagate a native mark past a replaced edge | Assertion failure: both native campaigns and the detached-handle case | +| Mark an edge that no longer holds the child | Assertion failure: the fixed native campaign, the detached-handle case, and a detachment test | +| Mark a frozen-key read native | Assertion failure: the frozen object read | +| Record nothing on a frozen-key read | Assertion failure: freeze or fix a key, then a nested write | +| Keep the native mark when `checkParentStatus` removes a reverted edge | Survives, so the fix does not add that clearing. The path runs only after the child has reverted, which already cleared the child's marks, so a stale ancestor mark can only report an equal value under a native write the model still treats as live. The native Limits permit that report. | +| Convert Sets to arrays on the alias comparison | Equivalent: an unassigned key holds a copy of its original, so both sides are Sets and draft equality already compares them in order. The fix removes the conversion. | + +Open, recorded rather than decided: + +- An assignment of a value draft-equal to the current one is a no-op, so a + handle taken before it stays attached and a later mutator through it changes + the row. Native JavaScript would detach the handle. The revert oracle's + native model follows the set trap and declares this in its Limits. +- Draft equality treats `-0` as `0`, as `deepEquals` and the query `eq` model + do. The native mark is what reports reversing a typed `[-0, 0]`. Replacing + the mark with a sign-aware value check would also stop `push` then `pop` + from reporting an equal array, and would make `draft.x = -0` over `0` a + change. That is a law change, not part of this fix. + +## Follow-up review: false negatives from the native mark (2026-10-09) + +Reviewed head `c5880a308`. The review had eight findings, with runs against +this branch and `origin/main`. + +**Decision.** A false positive (reporting a key whose value equals its +original) only repeats the stored value; the UI does not change. A false +negative loses a write. When correctness and code size trade off, this owner +leans toward false positives (maintainer decision, 2026-10-09). + +**What the probes showed.** On `c5880a308`, six probes reported nothing +where `main` reports the key: a `[-0, 0]` typed array reversed through a +`Map.get` value, Map and Set iteration values, a `subarray` view, a frozen +draft, and a sibling revert after the reverse. Every one reorders signed +zeros, which draft-equality rule 1 calls equal, so none is a change under the +stated law. Each still shows a native write that the native mark did not +follow. With ordinary values, the branch reports these writes. + +**Change.** The production fix is withdrawn. `src/proxy.ts` matches `main` +again, so `getChanges()` reports every assigned key, including a replaced +object restored through nested writes. That is a permitted false positive. +The laws now require every changed key and permit an extra report only for a +written key, with its final value: + +- the generated and pinned histories check `reported ⊇ changed`, and that + each extra key was written and reports its final value; +- the native block permits `f` whenever it equals its original; +- pinned witnesses require a real native write through a Map value, Map and + Set iteration values, a typed-array view and a frozen draft to be reported, + and a nested revert inside a Map or Set to keep the container's other write. + +The frozen-key read test that this branch added is removed; on `main` such a +read reports the key, which the row law permits. + +| Mutant on `main`'s proxy | Result | +| --- | --- | +| Report an assigned key only when it differs under draft equality | 14 proxy tests fail | +| Revert check by JSON, so Maps and Sets look equal | 4 proxy tests fail | +| Any Map or Set write counts as a revert | Equivalent: the parent re-checks the key by value | + +| Finding | Verdict | Action | +| --- | --- | --- | +| 1. Handles that share a value lose the native mark | Confirmed mechanism; no change under rule 1 | The mark is removed with the fix. Witnesses use real writes. | +| 2. A frozen draft loses later native writes | Same as 1 | Same | +| 3. An element assignment erases a parent `reverse()` | Not reproduced with ordinary values | Removed with the fix | +| 4. A sibling revert cancels a native change | Same as 1 (signed zeros). `main` reports `{}` there too, which rule 1 permits | Same | +| 5. Stale native marks on ancestors | Mechanism of the fix | Removed with the fix | +| 6. A same-value `defineProperty` clears the mark | Mechanism of the fix | Removed with the fix | +| 7. Every assigned key is deep-compared | Cost of the fix | Removed with the fix | +| 8. Production code grows | Confirmed (+62/−28) | Now 0 lines | + +**Limits.** Rule 1 stays: a reorder of signed zeros is not a change. Map and +Set mutators are still not generated together with assignments. + diff --git a/docs/contributing/oracle-reviews/subscription-reentrancy-guard-audit.md b/docs/contributing/oracle-reviews/subscription-reentrancy-guard-audit.md new file mode 100644 index 0000000000..6a33e6e47c --- /dev/null +++ b/docs/contributing/oracle-reviews/subscription-reentrancy-guard-audit.md @@ -0,0 +1,118 @@ +# Subscription reentrancy guard audit + +Reviewed production head: `33a194941c8d51f8f98babb999fef2987dd6ff8b`. +Source review: GAP-01 in the local code-weight audit. Mutation calibration ran +against the unchanged reviewed head. An independent review later found three +product bugs and led to a production repair. Each mutant came from +the audit's `subs-guard-mutants.py`, was installed alone, and was restored before +the next run. This record describes the new oracle's scope and the evidence that +supports it. The lossless finding ledger stays outside the repository. + +## Contract and observation boundary + +The Collection lifecycle grammar owns logical demand, physical acquisition, +sync-run cleanup, replay, and publication. At a direct snapshot's synchronous +return, a callback that retires the demand or subscription must prevent a local +snapshot publication. This law includes a loader that releases the original +request predicate while the subscription combines it with another predicate. +Adapter unload follows an accepted acquisition exactly once. A released demand +must not retain a rejected replay error while a peer +keeps the replay open. A stale acquisition settlement must not erase the loading +status of demand waiting for restart setup. + +The companion oracle records return values, result-hook calls, row callbacks, +exact unload options, abort-listener removal, subscription status, delegated +replay calls, and acquisition release at explicit microtask cuts. The +released-error check uses one narrow white-box retention observation because +the downstream public failure filter masks that reference while a peer remains +pending. These checks cover the bounded callback and timing histories in the +companion file. They do not claim every callback composition or provider effect. + +## Mutation calibration + +Initial baseline: 16/16 companion tests passed. The following 11 mutants failed by +assertion at their intended checkpoint: + +| Mutant | Distinguishing observation | +| --- | --- | +| M5 | Delegated replay starts after status-reentrant release removed its only demand. | +| M10 | An unoptimized fallback read continues after its callback unsubscribes. | +| M12 | The request abort listener is not removed after acquisition release. | +| M13 | Obsolete settlement changes `loadingSubset` to `ready` before queued restart setup. | +| M15 | Releasing a requested predicate fails to unload the acquisition when the subscription adds its own predicate. | +| M16 | A user operand's `Symbol.toPrimitive` unsubscribes during snapshot evaluation, but the request continues. | +| M17 | A loader-reentrant release or unsubscribe still invokes the result hook. | +| M18 | A status-reentrant release still lets the snapshot request return true. | +| M23 | An obsolete replay setup releases an acquisition before current setup reaches it. | +| M24 | A loader starts a newer replay, yet the obsolete setup releases its next demand. | +| M25 | A rejected released demand's Error remains retained while a peer is pending. | + +Three mutants survived the initial companion's 16 tests: + +- **M1.** Cleanup inside `loadSubset` reached the generation-mismatch branch: + a throw-at-branch probe failed one test. Cleanup removes a `starting` demand + and cancels its tentative acquisition. With M1 deleted, the following demand + membership check releases that record, the generation check suppresses + adapter unload, and `requestSnapshot` returns false. The only difference is + an internal release marker on an unreachable record. +- **M6.** A restart history with two detached demands reached the + replaced-state branch: a throw-at-branch probe produced an uncaught exception + from the queued restart setup. With M6 deleted, removed demands are skipped. + Remaining detached demands fail the current-replay check before acquisition. + The stale setup's completion check cannot publish after cleanup discarded its + replay state. +- **M8.** A throw in the `releaseAttempted` true branch remained unreached in + 538 focused subscription, lifecycle, and replay tests. Call-site inspection + shows logical removal before release, replay detachment before unload, + cancellation rather than release for tentative acquisitions, and unsubscribe + copying only active acquisitions before it clears ownership. A nested adapter + callback cannot acquire the same record through these public paths. + +These are bounded equivalence and reach arguments. They are not a request to +remove any production guard. SUBS-09 owns a separate code-weight decision. + +## Independent review and product repair + +Review of the initial oracle found three legal histories that its green run +missed. Two callbacks can release the requested predicate while a direct +snapshot runs. `onUnoptimized` then performed a fallback read, and snapshot +evaluation still published a row. The original code returned `true` in both +cases. The new matrix cells failed against that code and passed after both +post-callback checks tested demand ownership. + +The third history combines a subscription predicate with a requested predicate. +The loader released the original predicate before it returned. The original +code had not yet registered the predicate mapping, so it recorded no unload, +returned `true`, and published a row. A pinned oracle case failed at the exact +unload assertion. Registering the mapping before loader entry makes it pass. + +The final companion has 20 passing tests. The three new cases are product-bug +witnesses. The initial 11 mutant kills and three survivor arguments remain +claims about the reviewed head. The final production diff retains all guards +from that mutation sweep. + +A later external review described both ownership actions as covered at every +callback phase. Its claim exposed one omitted cell: `status:loadingSubset` +unsubscribe. A controlled probe reached that callback on the repaired head and +observed `false`, no publication, and one exact acquisition unload. The matrix +now preserves that already-correct history as its tenth cell. + +## Oracle guide review + +| Requirement | Outcome | +| --- | --- | +| ORC-001 | The existing lifecycle grammar and Collection cleanup/replay contract authorize the ownership, status, and publication laws above. The companion's limits are stated above and in the coverage map. | +| ORC-002 | Expected results come from owner retirement, sync-run fencing, and resource symmetry. The assertions do not call production classifiers or transition helpers. The M25 retention check observes an internal reference but does not derive its expected value from that field. | +| ORC-003 | The contract and expected law appear in the companion's opening prose; the bounded snapshot matrix is in the lifecycle grammar; each test drives a real Collection; assertions name the return, callback, resource, or microtask checkpoint. Replay histories are pinned in the companion. | +| ORC-004 | Not triggered: the matrix is bounded enumeration, not an important generated property. It reconstructs ten callback/action cells shown in the grammar. A separate case crosses loader reentry with a combined predicate. The existing generated lifecycle owner retains its fixed and random campaigns. | +| ORC-005 | Positive callback counters, load counts, and nested-replay flags prove reach. Exact return values, callbacks, resource effects, and status are compared at synchronous or queued-microtask cuts. The retention test states its white-box limit. | +| ORC-006 | Eleven separate wrong production mutants failed by assertion. M1 and M6 branch probes reached their guards but the deletion mutants survived. The M8 true-branch probe stayed unreached in 538 focused tests. None of these outcomes was a timeout or setup failure. | +| ORC-007 | Not triggered: this companion adds no important generated property. The primary lifecycle-history owner already runs fixed and random campaigns with direct replay. | +| ORC-008 | Not triggered: no state was added to or removed from the pure lifecycle reducer. Callback phase and action are bounded scenario inputs, not a new state machine. | +| ORC-009 | The companion uses the glossary's logical demand, acquisition, sync run, replay, cleanup, settlement, and publication terms. Callback phase is a test schedule cut, not production state. | +| ORC-010 | No shrinking or capture layer was added. The test adapters do not throw during unload or cleanup; `finally` retires every subscription and Collection and settles held promises. A future throwing-cleanup scenario needs separate primary and cleanup diagnostics. | +| ORC-011 | The direct predicate-release fixture and result-hook release token exercise ownership through different entry points. Loader, result-hook, status, unoptimized fallback, and snapshot-evaluation reentry challenge the same no-stale-snapshot law. The combined-predicate loader case crosses predicate identity with reentry. No second provider implementation is claimed. | + +The coverage map owns remaining callback combinations and the SUBS-09 decision. +The 11 killed mutants establish sensitivity at the stated checkpoints; they do +not prove the full legal history space. diff --git a/docs/contributing/oracle-reviews/utils-unit-retirement-round-7.md b/docs/contributing/oracle-reviews/utils-unit-retirement-round-7.md new file mode 100644 index 0000000000..5bdfa6d9b0 --- /dev/null +++ b/docs/contributing/oracle-reviews/utils-unit-retirement-round-7.md @@ -0,0 +1,116 @@ +# Deep equality unit retirement: Date and RegExp + +Two source-isolated Field Lab loss audits compared the Date and RegExp blocks +of `packages/db/tests/utils.test.ts` at frozen head `1265abeb5` with the +existing `utils-oracle.property.test.ts`. The proposed deletion said that the +oracle's copy/change and cross-type laws already covered both blocks. The +audits found three missing primitive boundaries before either block was +removed. No production behavior was changed for this transfer. + +| Retired block and source observation | Frozen receiver | Receiving law and checkpoint | +| --- | --- | --- | +| Date: separately allocated 2023-01-01 Dates are equal; 2023-01-02 differs. | Date copy/change properties and a fixed Date-time triple covered the law, but not those exact operands. | The fixed 2023 triple reconstructs both results; the existing generated Date copy/change property checks neighboring timestamps at `deepEquals` return. | +| Date: a Date differs from its ISO string. | No Date/string pair; the broad cross-type description had compressed away this boundary. | A fixed 2023 probe and generated finite Dates compare with their own ISO text in both directions. | +| Date: a Date differs from its numeric timestamp. | No Date/number pair, even when the number encoded the same instant. | The same fixed and generated law compares with `getTime()` in both directions. | +| RegExp: separate `/abc/g` and `/test/i` copies are equal; changed flags or source differ. | Source/flags fixed triples and a generated copy/changed-flags property covered the law, but not those exact operands. | Fixed `/abc/g`, `/abc/i`, `/def/g`, and `/test/i` checks reconstruct each return; generated RegExps vary source and flags. | +| RegExp: `/abc/g` differs from its source string `abc`. | The receiver had RegExp/object and number/string pairs, but no RegExp/string pair. | A fixed source-text probe and generated RegExps compare with their own source text in both directions. | + +The `deepEquals` documentation names Date and RegExp as supported value +classes. The old examples supply the more precise rule that their primitive +encodings are not coerced into those classes. The receiving model assigns +expected inequality from those distinct value classes; it does not call the +production classifier. The fixed operands preserve every old assertion, and +the generated properties extend each missing boundary across finite Dates and +valid RegExps. A deliberately coercing Date/number, Date/string, or +RegExp/string comparator fails the receiving checker at its forward inequality +assertion. Both directions are checked for the real production function. + +The two legacy blocks contained six tests. After transfer, the focused oracle +and remaining unit file passed 208 tests with no type errors. The generator +uses finite Date timestamps from -1,000,000,000,000 through 2,000,000,000,000 +milliseconds and the existing valid RegExp source/flag grammar; fixed and +seedless campaigns use the same properties. This establishes the direct +`deepEquals` return for those pairs. It does not establish cross-realm values, +RegExp subclasses, arbitrary prototype changes, every cross-type pair, or a +public change-event trace. Those need their own owner or receiving witness. + +## Oracle guide audit + +| Requirement | Evidence and limit | +| --- | --- | +| ORC-001 | Existing `deepEquals` documentation and retained examples supply the class-preserving law; the direct value classes and return checkpoint are explicit. | +| ORC-002 | Separate constructors and primitive encodings determine expected copy, change, and cross-type outcomes without production classification. | +| ORC-003 | The receiver explains its law, bounded grammar, production call, and return comparison beside the checks. | +| ORC-004 | Every removed operand and outcome has a fixed reconstruction; generated finite Dates and RegExps vary the new cross-type boundaries. | +| ORC-005 | Each check calls exported `deepEquals` and observes the immediate boolean return. No public publication claim is made. | +| ORC-006 | Injected primitive-coercing comparators fail the exact inequality assertion for all three newly recovered boundaries. | +| ORC-007 | `utilsProperty` runs the same property with fixed and random seeds. The source/flags property replayed a captured failing seed/path directly, as recorded below. | +| ORC-008 | Not triggered: the value model has no mutable state. | +| ORC-009 | Date, RegExp, ISO text, epoch number, and source text are distinct value classes in this model; no production storage state is modeled. | +| ORC-010 | The direct check owns no cleanup resource; its assertion is the failure checkpoint. | +| ORC-011 | The source-isolated audits found a shared broad-category omission. The old exact examples and generated checks now enforce each primitive boundary; no independent second production formulation is claimed. | +| ORC-012 | This ledger records the source assertions, recovered losses, receiving checks, execution, limits, and pending exact-commit review. | +| ORC-013 | A primitive with matching displayed or numeric content distinguishes class-preserving equality from a plausible coercing design. | +| ORC-014 | Not triggered: the direct call has no provider or host boundary. | + +## Exact-commit hostile review and follow-up + +At committed head `c4c07da42`, the fresh reviewer confirmed that all six +retired assertions had fixed receiving witnesses and found no production bug +in this transfer. It found two broader oracle weaknesses: the generated +RegExp law varied flags but not source within a pair, and the two newly +registered cross-type properties had shifted existing ordinal replay IDs. +The generated source law now compares a same-flags, changed-source neighbor. +The two added properties are registered after the original properties, so +their old selectors retain their meanings. A focused run passed 139 oracle +tests with no type errors. + +For ORC-007 calibration, a temporary wrong model rule expected an arbitrary +RegExp to equal its changed-source neighbor. The fixed campaign failed at the +`deepEquals` return comparison after one test with seed `20260911` and shrink +path `0:0`. Direct replay through `UTILS_ORACLE_PROPERTY=34`, +`UTILS_ORACLE_SEED=20260911`, and `UTILS_ORACLE_PATH=0:0` reproduced that +same assertion failure. Restoring the model rule made the direct replay pass. +The temporary rule is absent from the final oracle. This demonstrates replay +fidelity for the changed-source property, not every property in the file. + +At `ae0b67fd2`, a fresh hostile reviewer confirmed all six retired assertions +and found no production mismatch in this transfer. It caught a replay defect: +the two new cross-type registrations preceded the existing RegExp, Float32Array, +and Duration registrations, shifting saved selectors 34–36. Moving the new +registrations after them restored 34 to RegExp source and flags, 35 to +Float32Array, and 36 to Duration; the new properties are 37 and 38. A temporary +wrong expected result for the RegExp changed-source neighbor failed on direct +replay of selector 34, seed `20260911`, path `0:0`, after one test at the +forward `deepEquals` return. Removing the temporary rule made that replay pass +with only the selected generated property active. Fresh review of the next +committed head remains pending. + +At committed head `be5b7a3c4`, a fresh hostile reviewer found no blocker in +the six-test Date and RegExp transfer. All six retired assertions still have +fixed witnesses and broader generated laws. The owner passed 139 tests with +no type errors. Direct replays confirmed that selectors 34–38 name the +recorded properties, including selector 34 with seed `20260911` and path +`0:0`. The result covers direct `deepEquals` calls. It does not establish +public change events, cross-realm values, RegExp subclasses, or arbitrary +cross-type pairs. Ordinal selectors can shift if future registrations are +inserted ahead of them. + +## Frozen-head ledger recheck + +At `210651d207eefe9bbc025c780f6259e470225c9e`, the hostile reviewer +read the six retired Date/RegExp assertions, their source, the direct +`deepEquals` implementation, and the receiving oracle. Source inspection +found no missing operand or expected result in the bounded transfer. This +is a frozen-head no-loss verdict by inspection; it is not a new mutant kill +or a fresh committed-head execution of subsequent working-tree changes. + +## Exact code-commit review of the narrowed transfer + +At `24a61a8d5b6cef06d488c490e39f351b4d55fb68`, a fresh hostile reviewer +checked the committed six retired Date/RegExp assertions against their +fixed witnesses, generated laws, and production `deepEquals` path. It found +no loss at the direct-return checkpoint. The committed oracle and remaining +unit file passed 208 tests with no type errors. The scoped files and commit +check were clean. This verdict does not establish public change events or +cross-realm behavior. diff --git a/docs/contributing/oracle-rubric.md b/docs/contributing/oracle-rubric.md new file mode 100644 index 0000000000..df249ca78d --- /dev/null +++ b/docs/contributing/oracle-rubric.md @@ -0,0 +1,1175 @@ +# Oracle Testing Rubric + +A test needs two things: something to try, and a way to tell whether it worked. + +In an ordinary example-based test, we supply both: these inputs should produce this result. That works well for individual cases. But as operations interact—insert, update, delete, retry, reconnect—the combinations grow beyond the examples we can reasonably write. + +**What if we could generate those combinations and still know the right answer?** + +That is the practical value of oracle-based testing. An oracle supplies the judgment independently of how we choose the test cases. It might be another implementation whose behavior should match, an executable specification, a simple reference that recomputes the answer, or a rule relating several executions. + +For an incremental query engine, production might maintain complex indexes while the reference simply filters and sorts an array. Or we might compare compatible queries against another database. Either way, we can explore many histories without hand-calculating every expected result. + +The payoff is broader bug detection and a stable check during refactoring. The challenge is making sure the reference, generated cases, and observations actually represent the behavior we promise. + +Start with [one small oracle](#build-one-small-oracle). Follow the later sections when your contract needs [richer state](#keep-only-state-that-can-matter), [controlled timing](#generate-histories-that-reach-the-problem), or [more observations](#observe-what-the-contract-promises). The [review card](#a-review-card) is a short way to apply the guide to an existing test. Historical cases and research are collected in the [companion notes](oracle-test-notes.md). Use the [project glossary](glossary.md) for terms shared with production code. + +For help discovering or challenging the law itself, use the repository's +[oracle-authoring](https://github.com/TanStack/db/blob/main/.agents/skills/oracle-authoring/SKILL.md) or +[oracle-review](https://github.com/TanStack/db/blob/main/.agents/skills/oracle-review/SKILL.md) skill and its +[design instruments](instruments/index.md). These aids support inquiry; the +numbered requirements below continue to define guide conformance. + +## How to interpret this guide + +The numbered requirements in this section are the complete checklist for an +`oracle-rubric.md` conformance audit. Repository policies such as `AGENTS.md` and +the coverage map apply separately. Later sections explain the requirements and +give techniques and examples. They do not create additional requirements unless +they cite one of these IDs. + +`MUST`, `SHOULD`, and `MAY` have their usual specification meanings: + +- `MUST` is required when the stated trigger applies. +- `SHOULD` is expected when the trigger applies. A review may accept an + explicit, technically grounded reason for doing something else. +- `MAY` is optional. + +Only these uppercase keywords inside numbered requirement blocks define guide +conformance. Later prose may use lowercase contract terms such as `must` and +`may`, but it does not create a new guide requirement unless it cites an ORC +ID. Words such as “useful,” “ask,” and “consider” remain explanatory. + +An **important generated property** is a generated property used to protect a +reusable product law, claim coverage beyond named examples, or demonstrate a +bug-class repair. A fixed regression, bounded enumeration, or diagnostic stress +probe is not an important generated property merely because it uses test data. + +The executable oracle MUST keep its contract, model, history grammar, +production driver, and refinement check visible in the oracle file or in +companion modules that the file names directly. Pull-request evidence and a +versioned review record tied to the exact reviewed head may supplement those +five responsibilities, but cannot replace them. Unless a requirement says +otherwise, other conformance evidence MAY live in the executable test, an +oracle-file comment, the pull-request description, or a versioned review record +linked from the change. Reviewers MUST score the stated obligation, not the +presence of a preferred heading, class, comment template, or helper. + +### ORC-001: Contract authority and limits + +- **Trigger:** A test computes or constrains an expected product result. +- **Obligation:** The oracle MUST identify the promised law, its authority, and + the limits of the claimed result. +- **Acceptance evidence:** A reviewer can trace the expected result to an + approved API, architecture document, established contract, or design + decision, and can identify what the oracle does not establish. +- **Not required:** One particular comment format or a complete list of every + behavior outside the subsystem. + +### ORC-002: Independent judgment + +- **Trigger:** An oracle compares production with an expected result. +- **Obligation:** The expected result MUST be derived independently of the + production semantic machinery whose behavior it judges. +- **Acceptance evidence:** The model, alternate implementation, or relation + does not import or reproduce the relevant production classifier, transition, + comparator, or state machine without a separately justified trusted base. +- **Not required:** Independence of the production driver. Exercising the real + production entry point is expected and does not violate this requirement. + +### ORC-003: Distinguishable oracle responsibilities + +- **Trigger:** Any oracle or generated-history test. +- **Obligation:** The oracle MUST combine explanatory prose and executable code + as a literate program. Opening prose MUST state the promised law and its + limits before test mechanics. Prose beside the model MUST explain why its + independent rule predicts the promised result. The oracle MUST explain its + legal histories, production path, public observations, and comparison + checkpoint beside the relevant code. These explanations and the five + responsibilities MUST remain visible in the executable oracle or directly + named companion modules. The contract, model, history grammar, production + driver, and refinement check MUST remain distinguishable. Changes to the + executable law MUST update its prose in the same change. +- **Acceptance evidence:** A reviewer can learn the subsystem contract by + reading the prose and code together. The explanations connect the law to the + model and the model to observations at the named checkpoint. A compact oracle + may use one opening comment and short explanations beside its code. +- **Does not satisfy:** Code alone, five headings without explanations, or + explanations available only in a pull-request description or separate review + record. +- **Not required:** Five headings, five classes, five files, one fixture per + law, a long essay, or a review card embedded in every oracle. + +### ORC-004: Generated-history grammar controls + +- **Trigger:** A generated property claims coverage of a legal history or input + grammar. +- **Obligation:** The change MUST account for reconstruction, ablation, range, + and exclusion for that grammar. +- **Acceptance evidence:** The evidence names every known valid witness within + the claimed scope and shows that each can be reconstructed; accounts for the + semantic contribution of each declared axis, rule, or overlap under ablation; + states the bounded domains and marginal cases; and names a nearby invalid + history or state the grammar rejects. +- **Evidence location:** These controls MAY be executable calibration checks, + comments, pull-request evidence, or a versioned review record tied to the exact + reviewed head. Stable executable checks are preferred when the claim is cheap + to preserve. +- **Not required:** One test per control, a Cartesian product of unrelated axes, + or a permanent source mutant. + +### ORC-005: Production path and observation + +- **Trigger:** Any oracle used as product evidence. +- **Obligation:** The test MUST exercise the named production entry point and + MUST compare the promised public observation at the named checkpoint. +- **Acceptance evidence:** A positive execution witness shows that the intended + path and comparison ran. The recorder can represent the violations relevant + to the law, including duplicates, omissions, order, or partial output when + those are contractual. +- **Conditional information sufficiency:** When a concrete missing-input + hypothesis concerns a public result owed at the named checkpoint, the + evidence MUST show whether the production path receives the needed source + facts by then. If it does not, mark the claim unresolved. A green comparison + after later delivery MUST NOT be cited as proof of that earlier result. +- **Not required:** Observation of internal facts that the contract does not + expose, or an exhaustive search for indistinguishable histories without a + concrete information-gap hypothesis. For ORC-012, evidence may record that no + concrete missing-information hypothesis was identified; this does not claim + an exhaustive search. + +### ORC-006: Checker calibration + +- **Trigger:** An important generated property or a claimed oracle repair. +- **Obligation:** The evidence MUST name at least one plausible wrong answer or + design and demonstrate that the relevant comparison rejects it. +- **Acceptance evidence:** A wrong-result control, hostile fixture, fault + injection, or production mutant reaches the intended checkpoint and preserves + the distinguishing failure. +- **Not required:** Modifying production, checking in a mutant, or running a + production mutation campaign for every oracle. +- **Conditional obligation:** When a mutant is run, the evidence MUST classify + its outcome as assertion failure, timeout, setup failure, runtime failure + after setup outside the intended comparison, an unreached path, survival, or + equivalence within the tested domain. + +### ORC-007: Fixed and random campaigns with direct replay + +- **Trigger:** An important generated property. +- **Obligation:** The package's oracle campaign MUST execute the same property, + generators, observation recorder, refinement check, and run budget twice: + once with a documented fixed seed and once without a seed. A replay interface + MUST accept both the seed and shrink path and MUST run the requested replay + directly. When the property uses `fc.commands`, the interface MUST also accept + the commands arbitrary's reported `replayPath` and pass it back to + `fc.commands`. +- **Acceptance evidence:** The normal test command reaches both campaigns, each + campaign records its execution, and a checked replay command reproduces a + captured seed-and-path failure, including the command replay path when the + property uses `fc.commands`. +- **Does not satisfy:** Fixed examples do not replace the fixed-seed campaign. + Different generators, observation recorders, refinement checks, or budgets do + not establish campaign parity. +- **Not required:** Running either normal campaign before an explicit replay. + +### ORC-008: Stateful-model minimality + +- **Trigger:** A change introduces, removes, combines, or splits state in a + stateful reference model. +- **Obligation:** The change MUST explain whether a legal next action can + distinguish states the model proposes to treat as equal. +- **Acceptance evidence:** A distinguishing history justifies retained state, + or a reasoned argument shows why removed state cannot change a promised + observation or action's legality. +- **Not required:** An executable mutant for every model field or application of + this requirement to a stateless recomputation. + +### ORC-009: Vocabulary mapping + +- **Trigger:** A model combines or splits production concepts, or introduces a + model-only term that could be mistaken for a production concept. +- **Obligation:** The oracle MUST map the differing concepts explicitly and MUST + use the project glossary's term for a shared concept. +- **Acceptance evidence:** A reviewer can translate model actions, states, and + checkpoints to production boundaries without guessing. +- **Not required:** A mapping entry for plainly local data holders whose meaning + and observation are already unambiguous. + +### ORC-010: Failure fidelity and cleanup + +- **Trigger:** Shrinking, capture, or cleanup can replace or erase an oracle + failure. +- **Obligation:** The harness MUST preserve the original violated law and + checkpoint, retain distinguishable secondary cleanup diagnostics, and release + its resources. +- **Acceptance evidence:** The failure report separates the primary mismatch + from each cleanup failure and says whether a reduction reproduced the same + violation. +- **Not required:** Throwing cleanup failures as separate top-level exceptions. + An `AggregateError` satisfies this requirement when its `cause` and `errors` + preserve those distinctions. + +### ORC-011: Independent second formulation + +- **Trigger:** A reviewer names a plausible semantic fault that production and + the primary model could share, and a meaningfully different formulation could + distinguish it. +- **Obligation:** The change SHOULD add that formulation. If it does not, the + evidence MUST state why it is impractical or out of scope and track the + remaining risk. +- **Acceptance evidence:** The alternate path states its equivalence or + metamorphic relation, including relevant differences in ordering, projection, + duplicates, and empty results. +- **Not required:** A second formulation without a named shared-fault + hypothesis. +- **Does not satisfy:** Agreement between two structurally identical paths does + not establish independent evidence. + +### ORC-012: Review evidence + +- **Trigger:** A change claims that an oracle is new, repaired, or comprehensively + audited against this guide. +- **Obligation:** The review evidence MUST record the outcome of every other + applicable numbered requirement and MUST identify the requirements that do + not apply. +- **Acceptance evidence:** Each applicable requirement has concrete evidence or + an explicit unresolved gap. Each non-applicable requirement has a reason tied + to its trigger. +- **Conditional bug-class closure:** When a change claims to eliminate a bug + class, the review evidence MUST name its contract × history × production-path + × observation boundary, identify the original and adjacent distinguishing + witnesses, show a relevant wrong design rejected at the intended checkpoint, + and list unresolved in-scope cells with their coverage-map owner. A known + reachable counterexample prevents a closure claim. One oracle need not own + every boundary. +- **Evidence location:** At closeout, verdict-critical evidence outside the + executable oracle MUST live in a versioned review record tied to the exact + reviewed head. An editable pull-request description alone does not satisfy + this requirement. +- **Not required:** Copying the layer-comparison questions or review card into + every test, answering them in their printed order, or placing the audit in the + oracle file. + +### ORC-013: Distinguishing witness for a reusable boundary law + +- **Trigger:** A fixed or generated oracle is cited as protecting a reusable + conditional, threshold, or range law beyond its named examples. +- **Obligation:** The evidence MUST show a legal witness that reaches the law's + premise and a nearby in-domain witness whose observation at the claimed + checkpoint distinguishes the stated law from a plausible wrong boundary or + consequence. If those witnesses are unavailable, the claim MUST be limited + to the tested cases, and the reusable-law gap and its owner MUST be recorded. +- **Acceptance evidence:** The test or review record names the wrong rule and + shows which existing case rejects it. For a numeric threshold, cases on + opposite sides are insufficient if a plausible misplaced threshold still + produces every expected result in the tested matrix. +- **Not required:** Random generation, a permanent mutant, or exhaustive tests + of every adjacent value for a deliberately bounded example. + +### ORC-014: Controlled-premise handoff + +- **Trigger:** A controlled provider or host supplies a premise material to an + oracle result, and that result is cited as evidence for behavior with a real + provider or host. +- **Obligation:** The evidence MUST identify that premise and either point to a + receiving witness in which the real provider or host supplies the same + premise, or mark the cross-boundary claim unresolved with an owner. +- **Shared law:** When the controlled and real environments check the same + law, the receiving witness SHOULD be another production driver for the same + oracle. It + reuses the oracle's law, model, history grammar or examples, and comparison. + It supplies only its production entry point, premise control, and + observation recorder. If such a witness states its own expected results, it + MUST derive them from the oracle's model, or record why it cannot and the + drift risk that follows. An independent second model for the same law is an + ORC-011 formulation and needs that requirement's shared-fault hypothesis. +- **Acceptance evidence:** A reviewer can trace the specific event shape, + ordering, error, or lifecycle condition from the controlled fixture to the + receiving witness. A real-provider test of an unrelated ordinary case does + not establish the handoff. +- **Not required:** An end-to-end run inside every component oracle. An oracle + whose claim is explicitly limited to the controlled boundary does not need a + real-provider witness to satisfy this requirement. + +### ORC-015: Declared reach + +- **Trigger:** A pull request adds or changes an important generated property, + its history grammar, or its production driver, or cites one as evidence for a + bug-class repair. +- **Obligation:** The property MUST declare the states that its changed or + cited laws depend on, and MUST fail when generated histories reach a declared + state fewer times than its stated minimum. Use + [`createOracleReach`](../../packages/db/tests/oracle-reach.ts) or an + equivalent counter that fails the property. A passing run count does not + satisfy this requirement. +- **Acceptance evidence:** The pull request reports each declared state and how + many histories reached it in the normal test run. A state that only a larger + run budget reaches is reported as such. +- **Not required:** A declared state for every modeled transition. Declare the + states that the cited laws need, so that reach stays honest about scope. + +### ORC-016: Law-only oracle files + +- **Trigger:** A file defines an oracle, its input grammar, or a generated + campaign, including a compile-time type oracle. +- **Obligation:** The file MUST NOT contain ordinary example-based unit tests + or standalone concrete type assertions. Fixed regressions MUST be inputs to + the same independent model, production driver, and comparison used by the + broader campaign. They MUST NOT restate their expected answers. A type + oracle MUST apply a reusable independent type law to generated legal inputs. +- **Acceptance evidence:** A reviewer can vary the input without adding an + expectation. Every retained regression and receiving driver uses the shared + judgment. Checker calibration supplies deliberately wrong observations to + that judgment. +- **Does not satisfy:** Renaming a unit test, adding contract prose, enumerating + fixtures with separate assertions, or wrapping fixed expectations in a + property runner. +- **Not required:** Removing supplemental unit tests from ordinary test files, + randomizing a deliberately exhaustive finite grammar, or executing runtime + behavior to establish a source-level type law. + +## A quick start + +One way to supply that judgment is a small reference model. For a simple stateful test, choose a legal action, apply it to production and the reference, and compare the promised result at the agreed point. “After the action returns” may be the right point for one API; a callback during that action may matter for another. + +For example, suppose a query promises IDs in rank order. We expect `[1, 2]`; production returns `[2, 1]`. A length check passes. A membership check passes. An ordered comparison fails. We exercised the query in all three tests, but only one observed the difference we cared about. + +An **oracle** is the rule that judges that behavior. The **generator** chooses what to try. Keep those jobs separate: more executions cannot repair a comparison that ignores the promised order. + +Before writing the loop, answer six questions: + +| Question | What to write down | +| -------------------------------- | ------------------------------------------------------------------------------------------------------ | +| What is promised? | The rule, its source and its limits. | +| What can happen? | Values, actions and relevant action orders. | +| How will we know the answer? | A simpler reference, another implementation, or a relation that must hold. | +| What are we actually exercising? | The production entry point and what the fixture supplies. | +| What will we compare, and when? | Rows, order, events or other promised behavior, at a named checkpoint. | +| Why should we trust this check? | Evidence the path ran, the comparison rejects the relevant wrong answer, and failures can be replayed. | + +These are responsibilities, not six required classes. A small property may answer them in a comment and twenty lines of code. A lifecycle suite may need a separate model and driver. Do not build a framework just to fill the table. + +An evidence card is an optional way to collect this information; it is not a +required file format: + +```text +Law and source: +Domain and legal histories: +Reference or relation: +Production path and checkpoint: +Observed result and known omissions: +How to prove it ran and rejects a wrong answer; how to rerun: +``` + +An intentionally partial oracle is still useful. A rule that rejects duplicate completion cannot prove the rows are correct, but it can protect a real promise. A fixed regression can preserve a valuable history. Prefer an oracle that generalizes a bug's missing distinction where practical; keep the fixed witness when it adds clarity or reach. Randomness is not what makes either test trustworthy. + +Some concurrent contracts allow several results. In those cases, the reference must allow that freedom rather than invent one required order. We will return to that after establishing how to record an execution. + +## Write the oracle as executable subsystem documentation + +ORC-003 requires literate programming for every oracle and generated-history +test. Prose and executable code together teach humans and agents the subsystem +contract. State what the system promises and explain why each observable result +follows from the model. The guidance below explains this requirement. + +This does not make the model the source of product policy. Derive the contract +from an approved API, architecture document, established behavior, or design +decision. The file then keeps that contract, its model, and its production +evidence together. + +Two small examples show the form: + +- [`load-subset-transaction-refinement-oracle.test.ts`](https://github.com/TanStack/db/blob/main/packages/db/tests/query/load-subset-transaction-refinement-oracle.test.ts) + explains when an abort can still cancel an on-demand load. +- [`fifo-retry-oracle.property.test.ts`](https://github.com/TanStack/db/blob/main/packages/offline-transactions/tests/fifo-retry-oracle.property.test.ts) + explains why a ready transaction waits behind a delayed FIFO head. + +### Name oracle files consistently + +Use `oracle` in filenames that own an independent expected-result computation, +state model, differential or metamorphic comparison, or reusable law checker. +A separate module that defines that model, checker, or its history grammar also +qualifies. Identify the mechanism in the code before applying the name. + +An oracle file holds only oracle tests. It names its parts with +`declareOracle({ check: [...], model: [...], grammar: [...] })` from +[`oracle-declaration.ts`](../../packages/db/tests/oracle-declaration.ts), which +imports nothing so that browser and native-app modules can use it: the check +compares production observations with the model or with a second +formulation, and the grammar is the generators that produce legal histories. +Each part is optional. Every test in the file uses a declared part, directly +or through a driver: + +- A generated property or a fixed history runs through the check. +- A model pin steps the model and checks one rule against its authority. +- A calibration shows that the check rejects a wrong answer (ORC-006). +- A grammar calibration shows that the generators reach the states that the + laws need (ORC-004, ORC-015). +- A generated property can state its law inline: its predicate relates + production to inputs that nobody wrote down. A property whose arbitraries + are all constants, or whose predicate ignores its input, checks written + values instead. +- A type-level test compares an inferred type with a type model. A type has no + value to pass, so the file names its model types in + `declareOracle({ typeModel: [...] })`. Each name is a type alias or + interface in the same file, and a type built from one counts as using it. + +A test that uses none of them compares production with a hand-written value. It +is a unit test, even when it asserts a contract, and it belongs in an +ordinary test file beside the oracle. A finite table of hand-written expected +values selected by a test's parameters is not a model. The `oracle/no-unit-test` lint rule +reports such a test. Files named `oracle-*`, such as `oracle-config.ts`, are +the shared oracle machinery and declare no parts. + +An oracle file also registers no test through a helper from an ordinary module. +The rule reports a call to such a helper, because the helper's tests then appear +under the oracle file. The ordinary runner registers those tests, and passes the +shared suite a registrar when the suite needs one. A helper from an oracle-named +module is linted on its own, so the rule does not report a call to it. An +`oracle-` machinery module, such as `oracle-config.ts`, is not linted, so the +rule judges a call to its helpers as it judges an ordinary module's. + +ORC-016 keeps ordinary unit tests and concrete type assertions out of oracle +files. Fixed regressions replay the shared law; supplemental examples live in +ordinary test files. + +An ordinary example or regression does not need this suffix merely because it +asserts a contract. The same applies to concrete type assertions, fixtures, +registration wrappers, and generic runner utilities. A driver that only invokes +production or records observations remains support. Coverage-map inclusion, +parameterization, and contract prose alone do not establish oracle ownership. + +Count owners, model/checker definitions, drivers, and other support separately. +Several files may execute one shared oracle, and one file may contain several +oracle mechanisms. A filename is a discovery aid, not evidence of guide +conformance. Preserve runner suffixes such as `.property.test.ts`, `.test-d.ts`, +and `.spec.ts`. Update imports, test commands, replay selectors, and current +coverage links whenever an oracle file moves. + +### Keep five responsibilities distinguishable + +ORC-003 requires these responsibilities to remain distinguishable even when +they share one file: + +| Layer | What it must answer | +| --- | --- | +| Contract | What does the subsystem promise, and where is the boundary? | +| Model | What is the smallest independent rule that predicts public results? | +| History grammar | Which values, actions, relationships, and schedules can occur? | +| Production driver | Which real entry point and event boundary does the test exercise? | +| Refinement check | Which public observations must agree with the model, and when? | + +Do not create five classes merely to match this table. A short file can use one +opening comment, one pure model function, a generated input, a production +fixture, and assertions. A large state machine can split these layers into +separate modules when that makes each layer easier to review. + +### Lead with the law + +Under ORC-003, state the contract and its limits before introducing test +mechanics. Start with a question or a direct statement of the problem. Explain +why a normal example can miss the failure. + +Put the central law beside the model too. The opening comment supplies context. +The local comment lets a reader check the model without searching the file. +These comments are not duplicates when they serve those separate jobs. + +For example: + +```ts +// Publication is the boundary. An abort before publication rejects the load +// and discards the row. An abort after publication starts resolves the load +// and keeps the row visible. +function expectedOutcome(phase: AbortPhase): ExpectedOutcome { + // ... +} +``` + +### Make the model easy to distrust + +Prefer a pure function or a small state transition. Keep production queues, +caches, classifiers, and helpers out of the expected result. A reader should be +able to challenge the model without first learning the implementation. + +Name partial models honestly. If a model predicts rows but not callback counts, +say so. If several outcomes are legal, return the permitted set or relation. Do +not hide policy in scattered assertions outside the model. + +Check every modeled public fact at each relevant boundary. During the FIFO +rewrite, prose exposed that the first draft modeled calls but checked outbox +ownership only at the end. Moving both observations into one state model made +the test and the documentation agree. + +### Decompose the model by law + +A reference model can become as hard to trust as production. Do not keep adding +state until it becomes a second implementation of the subsystem. + +Treat the oracle as a small graph when the contract has independent laws: + +- A node owns one coherent rule and the least state needed to predict it. +- An edge records a real dependency between two rules. +- The production driver can feed the same action to several nodes. +- The refinement check composes their observations at the named checkpoint. + +For example, demand generations, facade identity, and callback coherence can +use separate models. They share actions, but they do not need one controller +that reproduces every production queue and cache. A failure then names the law +that diverged. A reviewer can also inspect each rule without learning the rest +of the subsystem. + +Do not split state that a legal next action can observe only as a whole. Use the +distinguishing-history test: if two states look equal to the proposed nodes, +can one legal next action produce different promised results? If yes, add the +missing edge or keep that state in one model. + +Decomposition does not mean one model per assertion. Group facts that form one +state machine. Keep independent policies separate. When an integrated promise +spans several nodes, add a relational check across their outputs instead of +merging their internal machinery. + +This graph is a reasoning tool, not a required test framework. Plain functions +and Maps are often enough. + +### Treat the input domain as a grammar + +Before choosing arbitraries, describe the language of legal cases. A useful +grammar separates three kinds of fact: + +- **Dynamics:** actions and transformations that move the system. +- **Constraints:** invariants, interfaces, and forbidden combinations. +- **Boundary conditions:** starting state, scale, provider behavior, and value + domains that limit where the rules apply. + +Freeze the contract first. Mark which rules come from an API or architecture +document and which rules the test author inferred. Then map containment, +overlap, and dependency. Do not force overlapping concerns into a tree merely +to make the generator neat. + +ORC-004 requires four controls before a generated property claims grammar +coverage: + +1. **Reconstruction:** Can the grammar rebuild every known valid witness? +2. **Ablation:** Does removing each axis, rule, or overlap lose a promised case + or admit a forbidden one? +3. **Range:** Does an independent marginal case require only new parameter + values, or does it expose a missing rule? +4. **Exclusion:** Can the grammar reject a nearby invalid history or state? + +Generate adjacent valid forms from this grammar. Do not take a flat Cartesian +product of unrelated axes merely because the tool makes that easy. When the +contract does require a product, add a calibration assertion that proves every +declared cell appears once. + +This approach exposes two common false greens. A grammar that cannot reconstruct +a known bug omits a path. A grammar that generates valid and invalid histories +without distinction makes skips and classifiers carry hidden policy. + +### Use controlled technical English + +Write prose that another agent can parse without asking what a term means: + +- Use active voice and short sentences. +- Give one state or event one name. Do not rotate synonyms. +- Define necessary domain terms when they first appear. +- Keep lowercase `must`, `may`, and `does not` exact when they describe a + product contract. Only the uppercase keywords in the numbered ORC blocks + define guide conformance. +- Use a list for three or more phases, actions, or conditions. +- Avoid metaphors when a boundary or transition has a precise name. +- Explain laws, causes, omissions, and checkpoints. Do not narrate clear code. + +Aim for no more than 25 words in a descriptive sentence. Keep a longer sentence +when splitting it would lose a condition or change its force. Clarity is the +goal. A low word count is not. + +### Use production vocabulary + +The model and production code must use the same term for the same concept. Read +the [project glossary](glossary.md) and the subsystem architecture before naming +model states, actions, or observations. + +Shared vocabulary does not weaken model independence. Reuse a production term, +not its queue, cache, transition helper, or semantic implementation. If the +model deliberately combines or splits production concepts, declare that mapping +beside the model. Never give a model-only convenience the name of a stronger +production concept. + +Check the causal grammar as well as the nouns. Demand starts an acquisition +attempt. Adapter acceptance establishes a physical acquisition and its lease. +Committing source writes produces an applied receipt. Publication exposes one +coherent public snapshot. Adapter acceptance, promise settlement, applied +settlement, and publication are different boundaries even when one synchronous +execution crosses all four. + +Avoid unqualified words that hide those distinctions. Name a `sync run`, +`public snapshot`, `logical subset owner`, or `window-operation generation` +rather than a generic session, state, owner, or generation. When production +renames a concept, update its glossary entry and the models that represent it in +the same change. + +### Audit prose, model, driver, and observations together + +Treat the layers as separate claims. Compare each pair during review: + +1. Does every promise in the prose have a model rule? +2. Does the model retain every distinction that a legal next action can expose? +3. Can the history grammar reach each modeled transition? +4. Does the driver exercise the named production path and event boundary? +5. Does each modeled output reach an assertion at the promised checkpoint? +6. Can the recorder represent duplicate, missing, reordered, or partial output? +7. Does any assertion impose behavior that the contract and model do not state? +8. Do prose, model, driver, and production use the glossary's canonical term + and transition grammar for each shared concept? +9. Does every model-only term declare how it maps to production, or that it has + no production counterpart? + +This comparison is a useful audit instrument. ORC-012 requires outcomes for the +applicable numbered requirements, not these questions or their answers in the +test file. If the prose cannot explain an assertion through the model, the model +may be incomplete. If the model predicts a fact that the test never observes, +the test may be false green. If the driver cannot create a named phase, the +prose claims more reach than the test has. + +### Keep the prose proportional + +ORC-003 requires explanatory prose and five visible responsibilities in every +oracle and generated-history test. A focused regression that is not an oracle +can remain short when its name and setup already state the whole contract, +but it does not replace applicable oracle coverage. + +Mandatory structure does not mean five classes or a long essay. A compact +oracle can state its contract and limits in one opening comment, keep a pure +model beside it, define a small grammar, drive the production entry point, and +compare the promised observations at a named checkpoint. + +As a starting budget, add only prose that helps a reader answer one of the five +layer questions. After the first draft, remove comments that only translate the +next line of code into English. Run the test after the rewrite. A documentation +edit that weakens the executable law changes behavior. + +## Build one small oracle + +We'll build a small reference model as one example of the approach. The following is an **illustrative contract**, not a specification for every TanStack query: + +- Rows have unique integer `id` values and finite numeric `rank` values. +- Rows sort by ascending rank, then ascending ID. +- A nonnegative integer width chooses the first rows; width zero returns none. +- Each action inserts an absent ID, changes the rank of a present ID, or deletes a present ID. +- After an action has been applied, the ordered visible IDs must match a full recomputation. + +Here is the completed card for this example: + +```text +Law: sort by rank then ID; return the first width IDs after each action. +Source: the illustrative contract above. +Domain: finite numeric ranks, unique integer IDs, legal insert/update/delete. +Reference: independent Map, fully sorted after every action. +Production path: the ordered-query entry point, after each action is applied. +Observe: the complete ordered IDs; not row values or callback counts. +Prove it ran: record applied actions and executed comparisons. +Challenge: the comparison must reject [2, 1] when [1, 2] is required. +Replay: retain the action list and width, plus the harness's replay inputs. +``` + +The production path still needs its concrete API name when this card becomes a real integration test. The example does not establish row-value, notification, locale or asynchronous-loading behavior. + +### Start with agreement, then the model + +Our expected answer is an ordered list of IDs. We may ignore internal tree nodes. We may not sort the actual IDs before comparison: doing so would erase the ordering bug. + +The reference can store all rows in a `Map` and sort from scratch. It does not need production's tree, incremental caches or rank maintenance: + +```js +function expectedIds(rows, width) { + return [...rows.values()] + .sort((a, b) => a.rank - b.rank || a.id - b.id) + .slice(0, width) + .map((row) => row.id); +} +``` + +Keep the reference's records separate from production's records. If production mutates a shared object and thereby changes the expected answer too, the two sides can agree for the wrong reason. Here, copying the two numeric fields is enough. Richer values need a copying or identity policy suited to their contract—not an automatic appeal to JSON serialization. + +Independent code also needs independent reasoning. Calling production's comparator from this reference would make comparator defects invisible to this particular comparison. Reuse mundane test mechanics when useful, but name any semantic helper both sides trust. A small model is easier to inspect; it is not correct merely because it is small. + +### Turn examples into histories + +Take two rows: `(id: 1, rank: 2)` and `(id: 2, rank: 1)`, with width two. The expected IDs are `[2, 1]`. Change row 1's rank to zero and they become `[1, 2]`. Delete row 1 and the answer becomes `[2]`. + +That is a history: a starting state followed by actions. The generator can produce many such histories while the same reference computes each answer. + +The integration loop below is pseudocode; the named driver operations are not TanStack APIs: + +```text +create independent model and production fixture +for each generated action: + require action to be legal in the model + apply action through the production driver + apply action to the model + reach the contract's applied checkpoint + compare production's visible IDs with expectedIds(model, width) +finally: + clean up the fixture without hiding the original failure +``` + +For this settled-result law, no intermediate publication claim follows. If atomic publication is also promised, record callbacks during the action; a read afterward cannot recover a transient tear. + +For this example, choose only actions that are legal now: + +| Current model | Available choices | +| -------------------- | ---------------------------------------------------------------- | +| Empty | Insert an unused ID. | +| Contains ID 1 | Insert another ID; update 1; delete 1. | +| Contains IDs 1 and 2 | Insert another ID; update either live ID; delete either live ID. | + +After choosing an action kind, choose its key from the applicable set. This is constructive generation: build a legal case rather than repeatedly discard illegal ones. Another valid approach generates commands with applicability checks and skips those that cannot run. Either way, count executed actions; a long list of skipped commands may do little work. + +Shrinking means reducing a failing input or action list while keeping its failure. Design action dependencies so the smaller histories remain meaningful; don't disable shrinking merely because the test is stateful. The [fast-check model guide](https://fast-check.dev/docs/advanced/model-based-testing/) describes commands and replay; check the installed version before copying an API recipe. + +Pin the structural cases that matter: no rows, width zero, a boundary tie, and repeated changes to one key. Exhaust a small domain where that is cheap. Then randomize values and longer legal histories. Fixed cases, bounded enumeration and random exploration do different jobs; overlap between them is not a defect. + +### Run fixed and random campaigns with direct replay + +A fixed fast-check seed always generates the same cases. This makes a useful +history stable, but it does not explore new histories on later runs. Do not +describe a fixed-seed property as random coverage. + +Under ORC-007, every important generated property runs in two campaigns: + +1. Run a fixed seed that preserves a known useful campaign. +2. Run without a seed so fast-check chooses a new seed. + +When the random campaign fails, retain the reported seed and shrink path. Give the +suite environment variables or another checked replay interface. The replay +input must select both the seed and the path. A seed alone reruns the +campaign but might not stop at the same reduced counterexample. + +`fc.commands` reports another value named `replayPath`. It records which +generated commands were eligible as the model changed. Capture that command +replay path separately from `fc.assert`'s shrink path and pass it back through +the `replayPath` option of `fc.commands`; the assert seed and path alone do not +fully specify a command-model replay. + +When replay inputs are present, run only the requested replay. Do not spend +time on the fixed campaign before reaching the failure the developer asked to +reproduce. + +The fixed and random campaigns must use the same property, generators, +observation recorder, refinement check, and run budget. Only their seed source +may differ. This keeps a random failure eligible for promotion into a pinned +example or fixed campaign. + +[`fifo-retry-oracle.property.test.ts`](https://github.com/TanStack/db/blob/main/packages/offline-transactions/tests/fifo-retry-oracle.property.test.ts) +shows this shape. Its fixed run preserves one scheduler campaign. Its second +campaign uses a random seed by default. To replay its recorded failure, set +`TANSTACK_DB_ORACLE_PROPERTY=offline.fifo-retry` with +`TANSTACK_DB_ORACLE_SEED` and `TANSTACK_DB_ORACLE_PATH`. The fixed run stands +aside, and the random campaign runs the recorded path once. The offline +owners' other properties use the same variables and name their own properties, +such as `offline.leadership.stale-read` and `offline.settlement.retry-decision`. + +Do not use a larger random run count as a substitute for structural reach. +Pinned examples force rare boundaries. Bounded enumeration proves small finite +domains. A stress job increases sampling depth. Record each form separately. + +### Make the comparison prove its usefulness + +Suppose the update test only asserts that the result changed. Returning `[]` passes that assertion. Keep the update—it is a useful cause—but compare against `[1, 2]`. + +Now test the check against a known bad _actual_ answer: + +```js +function sameOrderedIds(actual, expected) { + return ( + actual.length === expected.length && + expected.every((id, index) => actual[index] === id) + ); +} + +sameOrderedIds([1, 2], [1, 2]); // true: the allowed answer +sameOrderedIds([2, 1], [1, 2]); // false: correct members, wrong order +sameOrderedIds([], [1, 2]); // false: an arbitrary changed answer +``` + +In the test harness, assert those outcomes rather than merely calling the function. This calibrates the comparison; a production mutant that reverses results would test whether the whole fixture carries that fault to the assertion. Separately, confirm that the integration test reached the production boundary it names. Checker sensitivity and path reach are different claims: a perfect comparison on an unused code path protects nothing there. + +When production fails, keep the original history and the first mismatching checkpoint. Reduce the history while requiring that same violation, then retain the small witness with the broader property. That gives us both a readable explanation and more ways to encounter the class of failure. + +### When a full reference is not the best fit + +The `Map` works because the answer is cheap to compute independently. Other promises support other judgments: + +| Method | Useful judgment | Important limit | +| ------------------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------- | +| Simple model | Recompute the allowed result or step a small abstract state. | The model can omit a meaningful distinction or encode the wrong rule. | +| Differential comparison | Run equivalent work through another implementation or formulation. | Both paths can share a bug; their semantics must actually agree. | +| Metamorphic relation | Transform an execution and check a relation between the answers. | The transformation's premises must hold. | +| Partial invariant | Check a specific promise such as no duplicate completion. | Passing says nothing about unobserved properties. | +| Recorded regression or snapshot | Preserve a known execution and expected result. | The stored result needs justification; recording it does not make it right. | + +Choose the smallest authority that answers the question. Combining complementary checks can help; making every test compute every possible observation usually does not. + +## Keep only state that can matter + +A reference becomes suspicious when it starts to look like production. But removing fields until it looks simple is not a sound design method either. Which distinctions can we safely discard? + +Consider this **illustrative support model**. An acquisition handle supports a row. Replacing handle `a` with `b` transfers that support. Releasing a retired handle is a legal no-op. We observe support, not whether a provider physically deletes a source row. + +| History | Current supported rows | Live support count | +| ---------------------------------------------------- | ---------------------- | ------------------ | +| Acquire `a` supporting `x` | `{x}` | 1 | +| Acquire `a`, then replace it with `b` supporting `x` | `{x}` | 1 | + +The rows and count look identical. Now release `a`. The first history loses support for `x`; the second retains it through `b`. A model containing only rows and counts cannot answer both correctly. + +This gives the reasoning required by ORC-008: **find two states the model treats +as equal, then try a legal next action that could distinguish them.** If their +promised observations diverge—or an action is legal in only one—the model erased +something relevant. The evidence may be an argument or a preserved witness; a +separate executable test for every state field is not required. + +The witness tells us to retain the ownership distinction here. It does not prove the revised model handles every retry, pending completion or replacement race. Nor does it require every real API to accept retired handles: if the actual contract rejects them, model that rule instead. + +### Know where the expected answer comes from + +There are two questions behind “the model says so.” Who established the behavior? And which state determines the observable? + +An example from the archive makes the risk concrete: a finite model represented scores only up to three, turning `score > 3` into an empty predicate. The model had lost permitted values; that was not a reason to change production semantics. A separate ordering correction concerned which state owned the position: the model used locally edited values where the relevant contract used source state. The case notes retain that distinction; neither historical correction defines today's operator contract. See the [case notes](oracle-test-notes.md#historical-cases). + +Before changing production to satisfy a red test, check the contract, representation and comparison. A genuinely unresolved product choice belongs with its owner. A missing value in a finite model belongs in the model. References—including newly written specifications—can be wrong. + +Review the accusation and suggested fix separately. In one recorded adapter episode, returning a Promise created an acquisition lease even when it later rejected. Clearing ownership on rejection looked like cleanup but reportedly leaked that release obligation. The concern was useful; that repair did not follow from it. Other adapters may use different acquisition rules. + +Projection deserves the same care. Removing internal metadata from a public-value comparison can be valid. Removing a contractual virtual field because it is called “metadata” cannot. Write down one difference the comparison may ignore and one it must retain. + +## Generate histories that reach the problem + +Once the reference can distinguish relevant states, the generator must put production into them. Random payloads alone do not create ownership reuse, failed startup or work after recovery. + +Look for relationships and transitions: same key versus fresh key; one owner versus a surviving sibling; reject before versus after acquisition; release before versus after completion; recover, then perform another operation. Keep healthy peers alive where isolation is part of the promise. A test that ends at “recovery succeeded” may never reveal broken next-use state. + +Fresh disjoint rows can simplify a fixture while excluding the retired-key interaction it needs. Repeated `true` leadership reports do not cover `true → false → true` while earlier work is pending. These are different histories, not merely different random values. + +### Control the event, not just its returned Promise + +Async operations can have several boundaries: + +```text +request invoked → response delivered → writes applied → view published + ↘ returned Promise settles +``` + +That diagram names possible events, not a universal required order. The API contract must say which events precede which others. The test must control and observe the ones its claim depends on. + +If `setWindow` applies work eagerly, delaying its returned Promise does not delay that work. In an earlier harness, an unconditional `await` let a queued microtask repair a same-turn mismatch before the assertion ran. A test that reads only settled state could not expose the earlier observation. Likewise, an equal initial window change can repair startup before startup is checked. + +Put an explicit hold or release at the event you need to control. For example, a fixture testing visibility before application can arrange this illustrative sequence: + +```text +production requests data +fixture receives the response but holds delivery of its writes +test records the visible state while that delivery is held +test releases write delivery and lets production apply it +test records the visible state at the promised applied checkpoint +``` + +The fixture must really hold write delivery. If it delivers the writes and only delays returning from its load function, it controls a different event. Whether the returned Promise settles before or after application is a separate contract question. Record publications during the held interval if the promise concerns what users can see then. A scheduler is helpful only within the boundaries it controls; a seed cannot schedule arbitrary external I/O. The [fast-check scheduler documentation](https://fast-check.dev/docs/advanced/race-conditions/) explains the distinction between an operation's lifecycle and delivery of its result. + +### Treat driver timing helpers as premises + +A production driver often needs helpers such as "wait until the request is +blocked" or "let the message reach the worker". Each helper establishes a +premise. If a helper does less than its name says, every production driver +that uses it can go green without reaching the history. + +In one [recorded case](oracle-reviews/pr-2213-opfs-close.md), a driver loop +shared by the Node and Chromium production drivers gave back a lock in the same +turn in which it submitted close. The close message was still crossing a task +boundary, so the earlier request finished first. Close never ran during the +wait, and both worker mutants passed both production drivers. The loop had a +premise check for "a request waits for the lock", but it had none for "close +arrives during that wait". + +For each timing helper, do one of these: + +- Assert the premise at its checkpoint, for example from a lock inventory or a + delivery counter. +- Show that the history rejects a mutant that it can distinguish only when + the premise holds. This is calibration under + [ORC-006](#orc-006-checker-calibration). + +A helper that waits a fixed time does not prove delivery. It still needs one +of the two checks above. State its time bound beside the helper. + +### One matrix can need two schedules + +The historical four-receipt settlement fixture shows why enumerating more positions is not enough. A receipt here is a completion obligation for a portion of applied work. The tested contract required success to await the relevant receipts and failure not to wait for unrelated later work. + +For each selected receipt, two schedules asked different questions: + +| Question | Hold and release pattern | Observation | +| ----------------------------------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | +| Can settlement ignore this receipt? | Keep it pending; apply every peer. | The request must still await this obligation. | +| Does its rejection wait for a later peer? | Apply earlier receipts; leave later ones pending; reject this one. | Observe the exact rejection while later work remains pending, and compare the intact partial rows with the expected applied prefix. | + +Number the four receipts `r0` through `r3` and select `r1`. In the omission branch, apply `r0`, `r2` and `r3`, hold `r1`, and check that the request remains pending. In the rejection branch, apply only `r0`; hold `r2` and `r3`; reject `r1` with error E. Before releasing either later receipt, check rejection with E and the exact rows from the applied prefix. The fixture's application action must control applied work, not merely rename a Promise resolution. + +Resolving every peer first isolates omission but removes the pending sibling needed to expose delayed rejection. At the terminal receipt there is no later peer, so that cell cannot distinguish this delay. An empty expected prefix at the first receipt is valid; do not require nonempty rows just to prove the test ran. + +The archive reports that an `allSettled` mutant failed by timeout in this fixture. That is evidence about delayed rejection, not proof that every row or error assertion rejected a wrong value. Keep the two claims separate. + +Widening a generator can lose reach in two ways. A rewrite may remove a previously possible complete history, even while adding a dimension. Or a larger domain may make a useful history rarer at the same run budget. Neither means a true superset contains fewer cases; it means possibility and sampling frequency need separate checks. + +### Make the fixture respect the responsibility boundary + +A test provider can accidentally fix the consumer. One reported pagination fixture removed already-delivered rows before applying the requested limit. That supplied progress the consumer had not requested and hid repeated-page behavior. + +Contrast a provider whose job is to gather several backend pages to satisfy one requested offset and limit. Its internal draining loop may be legitimate. The location of code in a test helper does not decide which case it is. State what the request asks, what the provider promises, and which progress production must arrange. + +The same discipline applies to reentry. If a callback conditionally triggers the second operation, assert that the callback and trigger occurred. “If reached, check it” is a useful conditional claim, but it is not evidence that reentry was tested. + +ORC-013 asks a different question from whether the fixture ran: would the +tested cases reject a plausible wrong interpretation of the reusable law? A +conditional assertion needs a legal case where its premise is true. A finite +threshold matrix needs a case close enough to the boundary to distinguish the +promised cut from a plausible displaced cut. The claim can instead remain about +the named cases; record the broader law as open rather than stretching the +matrix's result. + +ORC-014 applies only when evidence crosses from a controlled premise to a +real-provider or host claim. Name the exact premise being transferred. For +example, a controlled callback can judge a component's response to an event; +claiming that a real provider emits that same event under the relevant +conditions needs a receiving witness. A declared controlled-boundary limit is +an honest scope, not a failed component oracle. + +An oracle covers a design space, and each environment supplies one production +driver for it. When Node and a browser must satisfy the same law, put the law, +model, histories, and comparison in one module. Then give each environment a +production driver for its entry point, premise control, and observations. The +production drivers may run in different test runners, but they do not each +restate the expected results. + +The OPFS request-order oracle shows the layout. +[`opfs-request-order-oracle.ts`](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/tests/opfs-request-order-oracle.ts) +owns the law, the model, the fixed examples, the driver loop, and the +comparison. The +[Node driver](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/tests/opfs-request-order-oracle.test.ts) +and the +[Chromium page driver](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/e2e/opfs-request-order.opfs.ts) +supply the entry point, access-handle control, database names, and result +reporting. The Node driver also generates random histories from the same +grammar. Neither driver states expected results, and both feed the same +comparison. The +[Chromium spec](https://github.com/TanStack/db/blob/main/packages/browser-db-sqlite-persistence/e2e/opfs-request-order-oracle.opfs.spec.ts) +checks for page errors, a completed run, one result per example, and no +violations. It states no expected results either. A witness +that restates expected results needs the record that ORC-014 describes. + +## Observe what the contract promises + +Reaching the right state still leaves a choice: what evidence survives the test harness? + +Select observations from the actual promise. Membership, values, order, multiplicity, callback boundaries, downstream views, error identity, settlement timing, durable storage and resource ownership are not interchangeable. No suite needs all of them merely because they exist. + +If an unprojected sort field changes, compare the expected new ordering, not just whether the array changed. If a callback count matters, assert the count before looping through callbacks; a loop over no callbacks checks nothing. If a failed write must not reach storage later, reconstruct persisted data after a later successful operation. The assertion should separate the specific wrong behavior from the allowed one. + +### Let the recorder represent violations + +It is useful to make generated inputs legal. It is dangerous to make recorded outputs incapable of being illegal. + +Suppose production completes the same request twice. A request-keyed map that stores only the last completion collapses the violation. Record both events, then assert the promised count. Keeping the raw list alone is not enough if the final report folds it back into a map and loses the duplicate. + +Similarly, do not reset a delta tracker before checking unchanged anchors, discard negative weights merely because the final relation should be nonnegative, or normalize away contractual order. Normalize only what the chosen comparison permits. + +### Check incremental results and publication separately + +This section and the next are deeper branches for incremental-query and cross-formulation tests. If you are building an ordinary collection test, continue with [testing the test](#test-the-testand-keep-the-same-failure) and return when these comparisons become useful. + +An incremental engine emits changes. A reference often computes a whole result. Compare compatible objects: accumulate the changes, then compare that state with full recomputation at the promised logical point. Inspect individual batches too when the contract makes their boundaries visible. + +DBSP formalizes the computational relation as `QΔ = D ◦ Q ◦ I`: accumulate input changes, run the ordinary query, then take output differences. Equivalently, accumulating the incremental output should agree with querying the accumulated input. This is a useful source of laws for db-ivm. It does not choose whether a client may see a partial publication, when readiness changes, or what cancellation means. [DBSP paper](https://www.vldb.org/pvldb/vol16/p1601-budiu.pdf). + +A final correct snapshot cannot establish that every earlier callback was coherent. The recorded graph-history case used asymmetric changes to expose an old-left/new-right publication that a later settled check would miss. Choose observation points that can distinguish the promised failure, rather than adding delays until the result is stable. + +When scheduling legitimately allows several results, check for **one permitted explanation of the observed history**. Do not approve each field against a different allowed execution: those individually permitted pieces may never coexist. In specification terms, refinement means implementation-visible behaviors are permitted by the specification. A bounded test checks sampled histories; it is not a refinement proof. Nor can an accepted finite prefix prove eventual completion. [Refinement mappings](https://lamport.azurewebsites.net/pubs/abadi-existence.pdf). + +### Compare formulations when a model might share the bug + +Even a plain reference can copy an incorrect semantic assumption. A second formulation gives another way to disagree. + +This is the conditional requirement in ORC-011, not a universal requirement for +every oracle. It applies after a plausible shared semantic fault and a +meaningfully different formulation have been identified. + +For a suitably scoped includes query, compare nested results with per-parent standalone queries or a flat join regrouped by parent. First state how empty parents, duplicates, ordering and projection match. A transformation that changes the answer is not an oracle for equivalence. + +SQLancer names ternary logic partitioning, or TLP, as one such strategy: compare a query with recomposed predicate partitions. Here is a simple illustration of the partition relation, not a verified TanStack recipe: give every occurrence in an input exactly one label—true, false or unknown—according to the same predicate. Recombining all three groups, retaining every occurrence once, recovers that input. NULL behavior, aggregation and ordering need explicit treatment before turning this into a library recipe. It is not a license to split an arbitrary limited query into independently limited pieces. [SQLancer's oracle inventory](https://github.com/sqlancer/sqlancer). + +Metamorphic testing is the broader idea: transform an execution and check a known relation between the answers. Incremental-versus-recompute checks and legal history transformations can serve similar roles. They need not know one answer in advance, but they still need a justified relation. + +Agreement across two paths is not proof of independence. NoREC's database experiments report shared operator faults and a copied join path that concealed a bug; count comparison can also miss wrong rows with the right count. Use differing formulations for the faults they can separate, not as an automatic second vote. [NoREC paper](https://arxiv.org/pdf/2007.08292). + +## Test the test—and keep the same failure + +“The suite passed” compresses several claims. Was the property selected? Did it run? Did it reach the intended path? Did its comparison execute? Would that comparison reject the fault under discussion? + +Treat those as separate evidence. A replay registry can contain a property nobody invokes. A multiplier can be parsed without changing the run count. A no-op assertion can leave a campaign green. Use a positive execution witness and a deliberate wrong-result control where those claims matter. + +Mutation testing changes production code deliberately to challenge tests. Record what happened, not just whether a mutant was “killed”: + +- A value assertion rejected the intended wrong answer. +- A timeout exposed a missing progress obligation. +- Setup failed before reaching the comparison. +- Execution failed outside the intended comparison after setup, such as a crash. +- The changed code never ran. +- The mutant survived, or the change was equivalent within the tested domain. + +These results call for different conclusions. If deleting one clause leaves the same obligation enforced elsewhere, survival does not show the obligation unnecessary. If the test kills a compound change, it may not distinguish each part. Research on mutation testing does not supply a conversion from a score to the probability that this library is correct; the [reading notes](oracle-test-notes.md#research-and-further-reading) preserve those limits. + +### Preserve the violation, not merely a red exit + +Shrinking makes failures explainable by removing irrelevant values and actions. A smaller legal history that fails during setup is not a reproduction of an original torn publication. Require the original failure predicate: the violated law, the relevant observation point, and the distinguishing evidence. + +Keep the original trace beside the reduction. Record which property ran and which checkpoint replay reached. “Ran, but did not reproduce the original mismatch” and “failed before reaching the comparison” are useful outcomes; neither should be labeled successful reproduction. + +Capture evidence before cleanup can change it. The historical publication fix copied the batch list before rollback; that preserved the needed local evidence, not universal deep immutability. An outer copy may still hold mutable values. A serializer may lose object identity or reorder arrays. Choose capture rules for the law and retain an original representation when normalization would erase the disputed distinction. + +Cleanup also has three separate jobs: preserve the primary failure, retain +secondary cleanup diagnostics, and release resources. Reporting the first error +does not prove the other two happened. Avoid letting a cleanup exception replace +the mismatch that started the investigation. “Separate” means distinguishable +in the report; an `AggregateError` may preserve the primary error as its cause +and each cleanup diagnostic in its errors. + +A useful failure report contains: + +```text +Law and source; requested property and actually executed target/path +Code/dependency version; relevant environment +Seed/path and explicit action or schedule trace +Reached checkpoint; expected versus actual observation +Original trace; reduced trace; reproduction result +Primary failure; separate cleanup diagnostics +``` + +Check replay commands against the installed tool. A seed is not a recording of the network, operating system or every Promise. If a challenge influenced the design, keep it as a regression or tuning case; it is no longer a held-out test of that design. + +## Review and maintain the portfolio + +When a reviewer finds a bug beside a green oracle, ask why the test missed it before adding another isolated example. Was the expected answer wrong? Was the history absent? Did the fixture drive another path or supply the missing work? Did the observation admit the wrong answer? Did capture or replay erase the failure? + +These categories overlap. They are prompts for a concrete explanation, not an exhaustive taxonomy or a grade. + +Begin a portfolio audit with known product promises as well as existing test claims. Otherwise ten honest settled-row tests can hide that nobody claimed startup rejection in the first place. Credit a witness at **contract × history × production path × observation**. A green component tested under a mock assumption does not establish that its real provider satisfies that assumption. + +The historical React tie-group test illustrates precise credit. It exercised a real QueryClient/Query DB path with a finite test provider and checked visible prefixes and exhaustion. That could fill a portfolio-level gap without improving another test's weak assertion. It did not establish page-array contents, every framework or arbitrary server ordering. Preserve useful partial tests without lending them their neighbors' scope. + +### Borrow behavioral questions from another project + +A donor regression gives one case; a property gives a relation; a model and +generator give a way to investigate further cases. None makes the donor's +policy a TanStack DB contract. Before porting a test, record the donor's version +or revision and motivating counterexample. Identify the recipient's contract +authority and map the donor's actions and observations to its public boundary. +State which donor assumptions still hold, which change, and which interaction +with existing recipient laws needs a new witness. Try a nearby donor case that +should **not** transfer, such as an automatic-reopen policy for an adapter whose +approved contract requires a new descriptor. This checks whether the mapping +selects behavior or indiscriminately copies a suite. The [IndexedDB donor +survey](oracle-reviews/2026-10-06-indexeddb-donor-survey.md) records both +receiving witnesses and rejected transfers. + +### Simplify machinery without changing the question + +Before removing or combining tests, identify the valid history, expected outcome, production path and observation that will remain. Name the receiving test. “Retry the parent” becoming “delete the parent” changes the behavior, even if the replacement is shorter and green. The archive records exactly that kind of loss during reduction. + +Other reductions are legitimate: an expectation can contradict the adapter contract, or two branches can enforce the same established law. Removing a redundant branch need not remove its law. Keep valuable fixtures and semantic assertions even when their old helper or duplicate implementation goes away. + +Share stable mechanics such as replay parsing or resource cleanup where they are genuinely interchangeable. Keep domain truth small and separately reviewable. Several independent models should not grow into a universal miniature implementation merely to reduce duplication. + +### Vary machinery and premises separately + +When comparing implementation candidates, hold the accepted behavioral law, +legal histories, and observation cuts fixed. An index or cache may be removable +without changing public results; measure its resource cost under a stated +workload separately. If instead a premise changes—delivery may duplicate, +ordering may tie, or the client may see only part of the source—derive which +guarantees still follow. A failure under a changed premise may call for more +provider data or a contract decision rather than another algorithm. + +### Report evidence and open work separately + +Sometimes the right correction is to narrow a claim. A reachable-object count is not a construction count: traversal may itself construct objects, and discarded objects are unseen. The historical repair both renamed that metric and checked source delivery before and after traversal. Renaming alone would omit part of the repair. + +Narrowing evidence does not retire the larger promise. Keep two lines when necessary: + +```text +Observed: 100 irrelevant payload reads versus zero in the recorded probe. +Open: elapsed slowdown is unverified; the extra-work finding remains open + in that historical ledger. +``` + +Likewise, “assertions passed; process exited nonzero” is neither a clean green run nor an absence of passing assertions. An observed compatibility failure and an unresolved support policy can coexist. State both rather than squeezing them into one status. + +A weak observer, a missing protocol signal, and a measurement not yet taken are different limits. None alone grants permission to drop a valid product obligation. The guide does not prescribe a universal run count, fault rate or stopping score. + +### A review card + +This card is an optional presentation for the ORC-012 evidence. For a new oracle +or a claimed repair, ask: + +1. What exact promise authorizes this expected result? +2. If a concrete missing-input hypothesis arises, what source facts does the + production path receive by the promised checkpoint (ORC-005)? +3. Which legal history distinguishes the proposed model from a weaker one? +4. Does the fixture make production do the work being tested? +5. Which concrete wrong answer can the comparison reject—and which can it miss? +6. What proves the path and assertion ran? What did the mutant or fault injection actually show? +7. Can capture, cleanup or shrinking turn this into a different failure? +8. Which larger promises remain outside this test, and where are they tracked? +9. If a reusable boundary law is claimed, which case rejects a nearby wrong + boundary? If a controlled premise supports a real-provider claim, where is + that exact premise received, and does that production driver share the + oracle's model + and comparison? +10. If the result exposes a problem, which [repair location](instruments/adversarial-review.md#locate-the-repair) + does the evidence support? + +### Reusable boundary-law checklist + +Adapter and lifecycle oracles should consider these laws when the contract has +the corresponding boundary. They are prompts, not universal requirements. State +why an inapplicable law does not belong to the owner instead of adding a vacuous +case. + +- **Real-provider conformance:** freeze representative values from each + supported provider version. Prove the fixture accepts those values before it + stands in for that provider. +- **Minimal ambiguity:** include the smallest valid input for every classifier + branch. Rich values that carry several redundant signals do not cover a + one-field collision. +- **Name invariance:** changing a user-controlled name or SQL alias must not + change envelope classification unless the public contract assigns that name + structural meaning. +- **Representation symmetry:** equivalent array/object forms and coexisting + carriers must produce the same public result or the same documented error. +- **Await-boundary transitions:** hold each relevant `await`, change ownership, + leadership, generation, abort, cleanup, or restart state, then release it. + Compare the result with the contract for that transition. +- **Local/transport refinement:** immutable transported data must have the same + meaning on local and remote paths. Live local references must remain local, + and cleanup must receive the exact lifecycle object delivered locally. +- **Partial-construction cleanup:** fail each construction step after it acquires + a resource. Preserve the primary error and prove every acquired resource is + released exactly once. +- **Value-and-work refinement:** when bounded work is promised, check the exact + result and a deterministic work/cardinality measure. Correct rows alone do + not establish the work law. + +Every owner should also state its known omissions beside the contract. The +coverage map tracks open reusable laws; an unchecked item is not evidence that +the neighboring laws are absent. + +The payoff is not a bigger test framework. It is a smaller distance between “this test is green” and a precise account of what that green result protects. diff --git a/docs/contributing/oracle-test-notes.md b/docs/contributing/oracle-test-notes.md new file mode 100644 index 0000000000..15e4e5fc62 --- /dev/null +++ b/docs/contributing/oracle-test-notes.md @@ -0,0 +1,94 @@ +# Oracle guide: cases, terms and sources + +Companion to [Oracle Testing Rubric](oracle-rubric.md). These notes keep evidence and deeper reading off the quick-start path. The historical entries describe archived reports and audits, not current failing tests or reruns performed for this guide. + +The shared terms used here are defined in the +[project glossary](glossary.md). That glossary is the vocabulary owner for +production code, models, tests, and design documents. + +## Historical cases + +These retained case notes come from the September 11–14, 2026 research archive. The Source column preserves archive record titles and line ranges, not links that require a maintainer's local checkout. These are reported historical observations, not independently rerun claims. The public [design-audit account](https://github.com/TanStack/db/issues/1808#issuecomment-5640113102) records the portfolio's method and several of the same scenes. Do not treat that account as independent corroboration. + +| Case | Distinction and evidence status | Source | +| --- | --- | --- | +| Same-turn mismatch repaired by a microtask | Reported harness correction: unconditional awaiting changed the observation point. Cleanup suppression preserved the primary error, but did not establish secondary-diagnostic retention. | *August 12 history* (research archive), lines 284–340 | +| Reachable objects versus construction | Reported repair both renamed the metric and checked source delivery before/after traversal. The decision against a test-only allocation seam was local, not a proof that allocations cannot be measured. | *August 15 history* (research archive), lines 32–96 | +| FIFO waiting versus a test's proposed liveness | Direct historical user choice accepted FIFO waiting. The expectation had to respect that choice; the passage does not establish a final execution result. | *August 24 history* (research archive), lines 247–285 | +| Finite scores omit the predicate's tail | A model limited to scores ≤3 made `score > 3` falsely empty. Truly empty IN was a separate question. | *Early loss report* (research archive), lines 135–139 | +| Source-owned ordering | Reported withdrawn accusation: sorting visible optimistic values modeled the wrong state for the promised position. | *Middle loss report* (research archive), lines 176–178 | +| Ownership after rejected Promise | Reported acquisition-lease obligation remained after Promise rejection; clearing ownership was not a valid repair for that API. A separate identity proposal compared a wrapper rather than the detached snapshot. Neither is a universal adapter rule. | *September 10 history* (research archive), lines 132–158 | +| Four receipt positions, two timing questions | Reported correction retained later pending peers for rejection timing, exact rejection identity and intact partial-prefix rows. The `allSettled` mutant died by timeout; terminal position alone cannot expose waiting for a later peer. | *August 29 history* (research archive), lines 564–611 | +| Helpful repeated-page provider | Reported provider prefilter supplied unrequested progress. This followed a distinct joined-alias underfill finding; they are not one mechanism. | *September 8 history* (research archive), lines 207–225 | +| Old-left/new-right publication | Reported asymmetric update exposed mixed graph state; copying evidence before cleanup preserved the local witness, not a universal deep snapshot. | *September 7 history* (research archive), lines 192–222 | +| Useful sort fixture, permissive assertion | Source audit identified a change to a nonprojected sort field but only an arbitrary-difference assertion. An accepted wrong-output argument, not a fresh mutant run in that audit. | *Framework audit* (research archive), lines 59–72, 82–98 | +| Empty callback loops and lost delta evidence | Source audit identified absent count checks, reset trackers, missing anchors and erased multiplicity. Predicted blind spots were not all executed mutations. | *Adjacent audit* (research archive), lines 56–61, 82–98 | +| Valid input, violation-capable output | Grammar correction separates lawful scenario construction from raw recording and explicit rejection of duplicate completions. The example is analytical. | *Grammar evidence* (research archive), lines 13–25 | +| Count-equal owners distinguished by release | Analytical counterexample motivates sufficient model state under an explicit support contract. Not a report that a provider must evict rows. | *Grammar hostile check* (research archive), lines 77–138 | +| Retry replaced by deletion | Secondary historical report says restoring the retry expectation exposed lost pending delta. Another branch cut retained the first-sort law; deletion itself is not the criterion. | *Middle loss report* (research archive), lines 194–200 | +| Adapter without unload | An invented second-fetch expectation was corrected. Preserve valid laws, not every assertion ever written. | *Early loss report* (research archive), lines 161–165 | +| React tied-rank continuation | Source audit credits exact visible prefixes/exhaustion through a specific integration and finite provider; not page arrays, every framework or generic endpoint behavior. The capped-provider example remains deliberately nonconforming, not an acceptable success contract. | *Framework registrations* (research archive), lines 57–74 | +| Unclaimed startup failure | Analytical audit: a list derived only from ten settled-result suites misses a known startup-rejection promise. | *Grammar hostile check* (research archive), lines 33–74 | +| Two green mocks, untested connection | Conditional component guarantees need a witness for the premise at the real connection. This was a grammar repair, not runtime evidence. | *Grammar evidence* (research archive), lines 6–12 | +| Replay registration versus execution | Source audit distinguished parser membership from dispatch. Some registrations looked stale only because discovery was incomplete; later reported positive controls cannot retroactively prove those old names dead. | *Campaign audit* (research archive), lines 3–21; *completion report* (research archive), lines 39–59 | +| Assertions passed, process failed | The report retained both passing assertions and nonzero exits, with the clean stress gate still blocked. This is a historical status, not the current suite result. | *Completion report* (research archive), lines 30–59 | + +Other retained examples include a narrow two-transition grammar finding no new defect, fresh disjoint rows omitting retired-key interactions, and recovery followed by further work with healthy peers. A no-new-bug run is useful negative evidence within its actual grammar, not evidence of completeness. See *early loss report* (research archive), lines 65–77 and 107–111, and *adjacent audit* (research archive), lines 138–140. + +Three further assertion contrasts are useful during review: production parity does not establish independent normalization; absence of a throw does not establish the terminal effect; eventual retry counts do not establish exact intervals. These were source-audit observations, not all executed mutants. The projection correction also retained a dependent final-state check as well as contractual virtual fields; preserving fields alone does not preserve that observation. See *adjacent audit* (research archive), lines 100–114, and *middle loss report* (research archive), lines 180–182. + +The prior external-review ledger also records three useful distinctions: a leadership flap is not an unchanged report; a storage error belonging to B can expose a different obligation from a mutation error belonging to A; and an observed old-reader/new-record corruption can coexist with an unresolved compatibility policy. Its work-count probe recorded 100 irrelevant payload reads versus zero while leaving the elapsed-time multiplier unverified. These are prior probe records, not benchmarks rerun for this guide. *Ledger* (research archive). + +The decisive continuation in the storage case was that rejected A remained queued and later executed. Another crossed input case combined an own `__proto__` data key with a sibling-object edit to expose change extraction; the baseline already mishandled the key, so it was not wholly a newly introduced bug. Both remind us to vary relationships among inputs and later actions, not just input sizes. + +## Research and further reading + +The source survey was collected on September 11, 2026. It stopped at its twenty-source budget while still finding useful limits. It was not exhaustive; no paper's experiments were rerun. Mutable documentation reflects that retrieval, not a guarantee about this repository's installed versions. + +### Models and the limits of agreement + +- [fast-check model-based testing](https://fast-check.dev/docs/advanced/model-based-testing/): commands, preconditions and replay; warns against copying the system into the model. +- [The Existence of Refinement Mappings](https://lamport.azurewebsites.net/pubs/abadi-existence.pdf): formal work on relating visible behaviors. Its assumptions do not validate an executable library model automatically. +- [The Oracle Problem in Software Testing](https://discovery.ucl.ac.uk/id/eprint/1471263/1/06963470.pdf): a scholarly survey used for vocabulary and abstraction failures, not independent corroboration of every cited experiment. +- [Reliability of life-critical software](https://shemesh.larc.nasa.gov/paper-nonq/nonq-paper.pdf): distinguishes separately written programs from statistically independent failures. Its ultrareliability argument does not estimate the benefit of this team's reference models. + +Keep three ideas separate: a model's structural simplicity, separate construction, and measured overlap of failures. None is a numeric substitute for another. + +### Histories, shrinking and bounded exploration + +- [QuickCheck](https://www.cs.tufts.edu/~nr/cs257/archive/john-hughes/quick.pdf): property testing with programmer-controlled input distributions and ways to inspect them. +- [Hypothesis stateful testing](https://hypothesis.readthedocs.io/en/latest/stateful.html) and [strategy design](https://github.com/HypothesisWorks/hypothesis/blob/master/guides/strategies-that-shrink.rst): applicable actions, value dependencies and constructive shrinking. Neither promises a globally minimal counterexample. +- [SmallCheck](https://pure.york.ac.uk/portal/en/publications/smallcheck-and-lazy-smallcheck-automatic-exhaustive-testing-for-s-2/): exhaustive exploration within bounded depth. Only the institutional abstract was available to this survey; no comparative performance claim is drawn from it. +- [Alloy's tutorial](https://alloytools.org/tutorials/online/maintext-FS-2.html): examples of small bounds missing a counterexample and overconstraint leaving no admissible model. Exhausting an empty domain is not useful success evidence. + +### Scheduling and environment fidelity + +- [CHESS](https://www.usenix.org/legacy/event/osdi08/tech/full_papers/musuvathi/musuvathi_html/index.html): bounded systematic scheduling through modeled interfaces; the environment still owns deterministic inputs and reset. +- [fast-check race-condition guide](https://fast-check.dev/docs/advanced/race-conditions/): what scheduled Promise results do and do not control. +- [FoundationDB system paper](https://www.foundationdb.org/files/fdb-paper.pdf): authors report recovery checks, limits of simulation and bugs caused by stronger-than-real operating-system assumptions. They also report tuning fault injection because too many faults can prevent reaching useful states. This is a separate industrial account, not corroboration of our receipt fixture or a universal fault-rate prescription. + +Deterministic replay within a harness and fidelity to the real provider are separate claims. + +### Relations between executions + +- [Metamorphic testing's original report](https://arxiv.org/abs/2002.12543): deriving related tests when individual answers are unavailable. Only the author abstract was usable in the survey; no effectiveness estimate follows. +- [NoREC](https://arxiv.org/pdf/2007.08292) and [SQLancer](https://github.com/sqlancer/sqlancer): query transformations, different oracle strategies, shared-bug limits and semantic exclusions. Input guidance and correctness judgment remain distinct. +- [DBSP](https://www.vldb.org/pvldb/vol16/p1601-budiu.pdf): accumulation/difference laws for incremental computation, not a choice of client publication policy. The surveyed treatment also has explicit convergence limits for unbounded domains; the guide's finite example does not transfer a convergence guarantee to recursive or unbounded work. +- [Materialize's 2024 QA account](https://materialize.com/blog/qa-process-overview/): different checks for different surfaces. Generic SQL fuzzing did not cover every source, sink or materialized-view behavior; alternative answers can both be valid. This is a dated maintainer account, not a current coverage audit. + +### Mutation evidence and economics + +- [Are Mutants a Valid Substitute for Real Faults?](https://homes.cs.washington.edu/~mernst/pubs/mutation-effectiveness-fse2014.pdf): selected real-fault coupling, with faults outside the studied operators. +- [Are Mutation Scores Correlated with Real Fault Detection?](https://coinse.github.io/publications/pdfs/Papadakis2018hi.pdf): suite size changes how correlations should be read; guidance for improving tests is not a calibrated reliability score. +- [Long Term Effects of Mutation Testing](https://research.google/pubs/long-term-effects-of-mutation-testing/): industrial test-writing effects and fault observations. The survey inspected the institutional abstract, not the full causal study design. + +These sources ask different questions and do not justify pooling their percentages. The survey did not establish comparative long-term maintenance costs, a score-to-reliability conversion, optimal fault rates or a benefit estimate for bespoke holdout mutants. Missing evidence in this bounded survey does not mean no such research exists. + +## Provenance and publication limits + +The guide combines historical reports, source-level audit findings, analytical counterexamples, established methods and clearly labeled illustrations. It does not claim complete coverage of the production state space, prove a minimum oracle architecture or settle new product contracts. + +A supplied [testing-design gist](https://gist.github.com/KyleAMathews/72e0cb6f5f6bd36cac1332ea91893b44) influenced the archived grammar. The record initially treated a partial reading as insufficiently consequential, then corrected that judgment after the full text was considered. It added explicit input/recorder and cross-suite-premise obligations. The later record supersedes the earlier “no changes” assessment; source ingestion itself is not runtime validation. Its webhook account and example APIs were not independently executed for this guide. Its advice to broadly disable shrinking was not adopted. + +The guide's reference and ordered-comparison helpers are dependency-free JavaScript. The TLP partition rendering is an illustration; the archived SQLancer inventory establishes the strategy's broad query/recomposition relation, not an executed implementation of this example. The production loop is explicitly pseudocode. There is no runnable TanStack/fast-check integration recipe yet, and this guide must not be advertised as supplying one. The guide is accompanied by the repository's [coverage and closeout record](oracle-coverage.md). Historical archive titles above preserve provenance without pretending to be public permalinks. Runnable production examples are linked from that record; the guide's illustrative loop is not an executable integration recipe. + diff --git a/docs/errors.md b/docs/errors.md new file mode 100644 index 0000000000..e6b90fc583 --- /dev/null +++ b/docs/errors.md @@ -0,0 +1,2631 @@ +--- +title: Error Codes +id: errors +--- + + + +Production builds of TanStack DB shorten error messages to a code, the inputs +that the error can show, and a link to this page. For example: + +```text +TanStack DB error 17 (key=1, collectionId="todos"): https://tanstack.com/db/latest/docs/errors#error-17 +``` + +A console warning uses the same form with `warning` in place of `error`. +Development builds keep the full messages below. The error classes, their +`name` values, and their fields are the same in both builds. See +[Error Handling](./guides/error-handling.md) for how to catch them. + + + +## Error 2: `DuplicateDbInstanceError` + +Development builds show the full message. For example: + +```text +Multiple instances of @tanstack/db detected! + +This causes transaction context to be lost because each instance maintains its own transaction stack. + +Common causes: +1. Different versions of @tanstack/db installed +2. Incompatible peer dependency versions in packages +3. Module resolution issues in bundler configuration + +To fix: +1. Check installed versions: npm list @tanstack/db (or pnpm/yarn list) +2. Force a single version using package manager overrides: + - npm: "overrides" in package.json + - pnpm: "pnpm.overrides" in package.json + - yarn: "resolutions" in package.json +3. Clear node_modules and lockfile, then reinstall + +To temporarily disable this check (not recommended): +Set environment variable: TANSTACK_DB_DISABLE_DUP_CHECK=1 + +See: https://tanstack.com/db/latest/docs/troubleshooting#duplicate-instances +``` + + + +## Error 3: `InvalidSyncPersistenceCapabilityError` + +Development builds show the full message. For example: + +```text +Invalid sync persistence capability at metadata.persistence: reason. Custom sync wrappers must forward metadata.persistence unchanged. +``` + + + +## Error 4: `CollectionRequiresConfigError` + +Development builds show the full message. For example: + +```text +Collection requires a config +``` + + + +## Error 5: `CollectionRequiresSyncConfigError` + +Development builds show the full message. For example: + +```text +Collection requires a sync config +``` + + + +## Error 6: `InvalidSchemaError` + +Development builds show the full message. For example: + +```text +Schema must implement the standard-schema interface +``` + + + +## Error 7: `SchemaMustBeSynchronousError` + +Development builds show the full message. For example: + +```text +Schema validation must be synchronous +``` + + + +## Error 8: `CollectionInErrorStateError` + +Development builds show the full message. For example: + +```text +Cannot perform operation on collection "collection-1" - collection is in error state. Try calling cleanup() and restarting the collection. +``` + + + +## Error 254: `CollectionHasSubscribersError` + +Development builds show the full message. For example: + +```text +Cannot clean up collection "collection-1" while 2 live query or Effect dependent(s) read it. Clean them up first, or call cleanup({ dependents: true }). collection.subscribers lists them. +``` + + + +## Error 255: `CollectionReloadCrashedError` + +Development builds show the full message. For example: + +```text +Cannot reload collection "collection-1" because it crashed. A crash is not recovered in place; clean it up with { dependents: true } and start it again. +``` + + + +## Error 256: `CollectionReloadDuringSyncEntryError` + +Development builds show the full message. For example: + +```text +Cannot reload collection "collection-1" from inside its sync function or a loadSubset call. Reload after it returns. +``` + + + +## Error 9: `InvalidCollectionStatusTransitionError` + +Development builds show the full message. For example: + +```text +Invalid collection status transition from "pending" to "ready" for collection "collection-1" +``` + + + +## Error 10: `CollectionIsInErrorStateError` + +Development builds show the full message. For example: + +```text +Collection is in error state +``` + + + +## Error 11: `NegativeActiveSubscribersError` + +Development builds show the full message. For example: + +```text +Active subscribers count is negative - this should never happen +``` + + + +## Error 12: `LiveQueryObserverDisposedError` + +Development builds show the full message. For example: + +```text +Cannot subscribe to a disposed LiveQueryObserver +``` + + + +## Error 13: `LiveQueryWindowControllerDisposedError` + +Development builds show the full message. For example: + +```text +Cannot subscribe to a disposed LiveQueryWindowController +``` + + + +## Error 14: `UndefinedKeyError` + +Development builds show the full message. For example: + +```text +An object was created without a defined key: {"name":"item"} +``` + + + +## Error 15: `InvalidKeyError` + +Development builds show the full message. For example: + +```text +getKey returned an invalid key type. Expected string or number, but got null: null. Item: {"name":"item"} +``` + +```text +getKey returned an invalid key type. Expected string or number, but got object: {"id":1}. Item: {"name":"item"} +``` + + + +## Error 16: `DuplicateKeyError` + +Development builds show the full message. For example: + +```text +Cannot insert document with ID "key-1" because it already exists in the collection +``` + + + +## Error 17: `DuplicateKeySyncError` + +Development builds show the full message. For example: + +```text +Cannot insert document with key "123" from sync because it already exists in the collection "collection-1" +``` + +```text +Cannot insert document with key "key-1" from sync because it already exists in the collection "collection-1". This collection uses a custom getKey with .distinct(). The .distinct() operator deduplicates by the ENTIRE selected object (standard SQL behavior), but your custom getKey extracts only a subset of fields. This causes multiple distinct rows (with different values in non-key fields) to receive the same key. To fix this, either: (1) ensure your SELECT only includes fields that uniquely identify each row, (2) use .groupBy() with min()/max() aggregates to select one value per group, or (3) remove the custom getKey to use the default key behavior. +``` + +```text +Cannot insert document with key "key-1" from sync because it already exists in the collection "collection-1". This collection uses a custom getKey with joined queries. Joined queries can produce multiple rows with the same key when relationships are not 1:1. Consider: (1) using a composite key in your getKey function (e.g., `${item.key1}-${item.key2}`), (2) ensuring your join produces unique rows per key, or (3) removing the custom getKey to use the default composite key behavior. +``` + + + +## Error 18: `MissingUpdateArgumentError` + +Development builds show the full message. For example: + +```text +The first argument to update is missing +``` + + + +## Error 19: `NoKeysPassedToUpdateError` + +Development builds show the full message. For example: + +```text +No keys were passed to update +``` + + + +## Error 20: `UpdateKeyNotFoundError` + +Development builds show the full message. For example: + +```text +The key "key-1" was passed to update but an object for this key was not found in the collection +``` + + + +## Error 21: `KeyUpdateNotAllowedError` + +Development builds show the full message. For example: + +```text +Updating the key of an item is not allowed. Original key: "old-key", Attempted new key: "new-key". Please delete the old item and create a new one if a key change is necessary. +``` + + + +## Error 22: `NoKeysPassedToDeleteError` + +Development builds show the full message. For example: + +```text +No keys were passed to delete +``` + + + +## Error 23: `DeleteKeyNotFoundError` + +Development builds show the full message. For example: + +```text +Collection.delete was called with key 'key-1' but there is no item in the collection with this key +``` + + + +## Error 24: `MissingInsertHandlerError` + +Development builds show the full message. For example: + +```text +Collection.insert called directly (not within an explicit transaction) but no 'onInsert' handler is configured. +``` + + + +## Error 25: `MissingUpdateHandlerError` + +Development builds show the full message. For example: + +```text +Collection.update called directly (not within an explicit transaction) but no 'onUpdate' handler is configured. +``` + + + +## Error 26: `MissingDeleteHandlerError` + +Development builds show the full message. For example: + +```text +Collection.delete called directly (not within an explicit transaction) but no 'onDelete' handler is configured. +``` + + + +## Error 27: `QueueCapacityExceededError` + +Development builds show the full message. For example: + +```text +Queue capacity exceeded; the mutation was not admitted +``` + + + +## Error 28: `QueueDisposedError` + +Development builds show the full message. For example: + +```text +Queue has been cleaned up; the mutation was not admitted +``` + + + +## Error 29: `ThrottleCallDroppedError` + +Development builds show the full message. For example: + +```text +Throttle call was dropped because trailing execution is disabled +``` + + + +## Error 30: `DebounceCallDroppedError` + +Development builds show the full message. For example: + +```text +Debounce call was dropped because trailing execution is disabled +``` + + + +## Error 31: `MissingMutationFunctionError` + +Development builds show the full message. For example: + +```text +mutationFn is required when creating a transaction +``` + + + +## Error 32: `OnMutateMustBeSynchronousError` + +Development builds show the full message. For example: + +```text +onMutate must be synchronous and cannot return a promise. Remove async/await or returned promises from onMutate. +``` + + + +## Error 33: `TransactionNotPendingMutateError` + +Development builds show the full message. For example: + +```text +You can no longer call .mutate() as the transaction is no longer pending +``` + + + +## Error 34: `TransactionAlreadyCompletedRollbackError` + +Development builds show the full message. For example: + +```text +You can no longer call .rollback() as the transaction is already completed +``` + + + +## Error 35: `TransactionNotPendingCommitError` + +Development builds show the full message. For example: + +```text +You can no longer call .commit() as the transaction is no longer pending +``` + + + +## Error 36: `NoPendingSyncTransactionWriteError` + +Development builds show the full message. For example: + +```text +No pending sync transaction to write to +``` + + + +## Error 37: `SyncTransactionAlreadyCommittedWriteError` + +Development builds show the full message. For example: + +```text +The pending sync transaction is already committed, you can't still write to it. +``` + + + +## Error 38: `SyncRowReusedWithoutPreviousValueError` + +Development builds show the full message. For example: + +```text +A sync update for key "key-1" wrote a row object that changed in place since it was last written. The change overwrote the row's previous value. Write a new object, or pass the row's previous value as `previousValue`. +``` + + + +## Error 39: `NoPendingSyncTransactionCommitError` + +Development builds show the full message. For example: + +```text +No pending sync transaction to commit +``` + + + +## Error 40: `SyncTransactionAlreadyCommittedError` + +Development builds show the full message. For example: + +```text +The pending sync transaction is already committed, you can't commit it again. +``` + + + +## Error 41: `SyncQueueInvariantError` + +Development builds show the full message. For example: + +```text +Sync queue invariant failed: invariant detail +``` + + + +## Error 42: `OnlyOneSourceAllowedError` + +Development builds show the full message. For example: + +```text +Only one source is allowed in the join clause +``` + + + +## Error 43: `SubQueryMustHaveFromClauseError` + +Development builds show the full message. For example: + +```text +A sub query passed to a join clause must have a from clause itself +``` + + + +## Error 44: `InvalidSourceError` + +Development builds show the full message. For example: + +```text +Invalid source for live query: The value provided for alias "todos" is not a Collection or subquery. Live queries only accept Collection instances or subqueries. Please ensure you're passing a valid Collection or QueryBuilder, not a plain array or other data type. +``` + + + +## Error 45: `InvalidSourceTypeError` + +Development builds show the full message. For example: + +```text +Invalid source for from clause: Expected an object with a single key-value pair like { alias: collection }. For example: .from({ todos: todosCollection }). Got: string +``` + +```text +Invalid source for join clause: Expected an object with a single key-value pair like { alias: collection }. For example: .join({ todos: todosCollection }, ({ todo, todos }) => eq(todo.id, todos.id)). Got: number +``` + +```text +Invalid source for unionAll clause: Expected an object with one or more key-value pairs like { alias: collection }. For example: .unionAll({ todos: todosCollection, events: eventsCollection }). Got: undefined +``` + + + +## Error 46: `JoinConditionMustBeEqualityError` + +Development builds show the full message. For example: + +```text +Join condition must be an equality expression or a nonempty conjunction of equality expressions +``` + + + +## Error 47: `QueryMustHaveFromClauseError` + +Development builds show the full message. For example: + +```text +Query must have a from clause +``` + + + +## Error 48: `InvalidWhereExpressionError` + +Development builds show the full message. For example: + +```text +Invalid where() expression: Expected a query expression, but received a boolean. This usually happens when using JavaScript's comparison operators (===, !==, <, >, etc.) directly. Instead, use the query builder functions: + + ❌ .where(({ user }) => user.id === 'abc') + ✅ .where(({ user }) => eq(user.id, 'abc')) + +Available comparison functions: eq, gt, gte, lt, lte, and, or, not, like, ilike, isNull, isUndefined +``` + + + +## Error 49: `UnsafeAliasPathError` + +Development builds show the full message. For example: + +```text +Unsafe alias path segment "__proto__" is not allowed in .select(). Aliases must not contain "__proto__", "prototype", or "constructor". +``` + + + +## Error 50: `DistinctRequiresSelectError` + +Development builds show the full message. For example: + +```text +DISTINCT requires a SELECT clause. +``` + + + +## Error 51: `FnSelectWithGroupByError` + +Development builds show the full message. For example: + +```text +fn.select() cannot be used with groupBy(). groupBy requires the compiler to statically analyze aggregate functions (count, sum, max, etc.) in the SELECT clause, which is not possible with fn.select() since it is an opaque function. Use .select() instead of .fn.select() when combining with groupBy(). +``` + + + +## Error 52: `UnsupportedFnSelectResultError` + +Development builds show the full message. For example: + +```text +fn.select() cannot return query builder. Child query builders, query expressions, and helpers such as eq(), toArray(), materialize(), concat(toArray()), and caseWhen() are query-construction values. Use them as direct fields in .select() instead. +``` + + + +## Error 53: `UnsupportedRootScalarSelectError` + +Development builds show the full message. For example: + +```text +Top-level scalar select() is not supported by createLiveQueryCollection() or queryOnce(). Return an object from .select(), or use the scalar query inside toArray(...) or concat(toArray(...)). +``` + + + +## Error 54: `HavingRequiresGroupByError` + +Development builds show the full message. For example: + +```text +HAVING clause requires GROUP BY clause +``` + + + +## Error 55: `LimitOffsetRequireOrderByError` + +Development builds show the full message. For example: + +```text +LIMIT and OFFSET require an ORDER BY clause to ensure deterministic results +``` + + + +## Error 56: `CollectionInputNotFoundError` + +Development builds show the full message. For example: + +```text +Input for alias "alias" (collection "collection-1") not found in inputs map. Available keys: key1, key2 +``` + +```text +Input for collection "alias" not found in inputs map +``` + + + +## Error 57: `DuplicateAliasInSubqueryError` + +Development builds show the full message. For example: + +```text +Subquery uses alias "alias" which is already used in the parent query. Each alias must be unique across parent and subquery contexts. Parent query aliases: parent1, parent2. Please rename "alias" in either the parent query or subquery to avoid conflicts. +``` + + + +## Error 58: `UnsupportedFromTypeError` + +Development builds show the full message. For example: + +```text +Unsupported FROM type: string +``` + + + +## Error 59: `UnknownExpressionTypeError` + +Development builds show the full message. For example: + +```text +Unknown expression type: custom +``` + + + +## Error 60: `EmptyReferencePathError` + +Development builds show the full message. For example: + +```text +Reference path cannot be empty +``` + + + +## Error 61: `UnknownFunctionError` + +Development builds show the full message. For example: + +```text +Unknown function: customFn +``` + + + +## Error 62: `JoinCollectionNotFoundError` + +Development builds show the full message. For example: + +```text +Collection "collection-1" not found during compilation of join +``` + + + +## Error 63: `UnsupportedJoinTypeError` + +Development builds show the full message. For example: + +```text +Unsupported join type: OUTER +``` + + + +## Error 64: `InvalidJoinConditionSameSourceError` + +Development builds show the full message. For example: + +```text +Invalid join condition: both expressions refer to the same source "todos" +``` + + + +## Error 65: `InvalidJoinConditionSourceMismatchError` + +Development builds show the full message. For example: + +```text +Invalid join condition: expressions must reference source aliases +``` + + + +## Error 66: `InvalidJoinConditionLeftSourceError` + +Development builds show the full message. For example: + +```text +Invalid join condition: left expression refers to an unavailable source "todos" +``` + + + +## Error 67: `InvalidJoinConditionRightSourceError` + +Development builds show the full message. For example: + +```text +Invalid join condition: right expression does not refer to the joined source "comments" +``` + + + +## Error 68: `InvalidJoinCondition` + +Development builds show the full message. For example: + +```text +Invalid join condition +``` + + + +## Error 69: `UnsupportedJoinSourceTypeError` + +Development builds show the full message. For example: + +```text +Unsupported join source type: custom +``` + + + +## Error 70: `NonAggregateExpressionNotInGroupByError` + +Development builds show the full message. For example: + +```text +Non-aggregate expression 'field' must also appear in GROUP BY clause +``` + + + +## Error 71: `UnsupportedAggregateFunctionError` + +Development builds show the full message. For example: + +```text +Unsupported aggregate function: customAgg +``` + + + +## Error 72: `AggregateFunctionNotInSelectError` + +Development builds show the full message. For example: + +```text +Aggregate function in HAVING clause must also be in SELECT clause: count +``` + + + +## Error 73: `UnknownHavingExpressionTypeError` + +Development builds show the full message. For example: + +```text +Unknown expression type in HAVING clause: custom +``` + + + +## Error 74: `SerializationError` + +Development builds show the full message. For example: + +```text +Cannot write item because its storage cannot represent it: circular reference +``` + + + +## Error 75: `StorageKeyRequiredError` + +Development builds show the full message. For example: + +```text +[LocalStorageCollection] storageKey must be provided. +``` + + + +## Error 76: `InvalidStorageDataFormatError` + +Development builds show the full message. For example: + +```text +[LocalStorageCollection] Invalid data format in storage key "storageKey" for key "key". +``` + + + +## Error 77: `InvalidStorageObjectFormatError` + +Development builds show the full message. For example: + +```text +[LocalStorageCollection] Invalid data format in storage key "storageKey". Expected object format. +``` + + + +## Error 78: `SyncCleanupError` + +Development builds show the full message. For example: + +```text +Collection "collection-1" sync cleanup function threw an error: Cleanup failed +``` + +```text +Collection "collection-1" sync cleanup function threw an error: Cleanup threw +``` + + + +## Error 79: `SyncTransactionAbortedError` + +Development builds show the full message. For example: + +```text +Sync transaction was aborted before application +``` + + + +## Error 80: `CollectionPreloadAbortedError` + +Development builds show the full message. For example: + +```text +Collection preload was abandoned during cleanup +``` + + + +## Error 81: `LoadSubsetOperationAbortedError` + +Development builds show the full message. For example: + +```text +Load subset operation was aborted before its result became visible +``` + + + +## Error 82: `CannotCombineEmptyExpressionListError` + +Development builds show the full message. For example: + +```text +Cannot combine empty expression list +``` + + + +## Error 83: `MissingAliasInputsError` + +Development builds show the full message. For example: + +```text +Internal error: compiler returned aliases without inputs: alias1, alias2. This indicates a bug in query compilation. Please report this issue. +``` + + + +## Error 84: `SetWindowRequiresOrderByError` + +Development builds show the full message. For example: + +```text +setWindow() can only move a window on a query with an ORDER BY clause. Add .orderBy() to your query to enable window movement. +``` + + + +## Error 85: `SetWindowReentrancyError` + +Development builds show the full message. For example: + +```text +setWindow() cannot run reentrantly. Wait for the current window operation to return before starting another one. +``` + + + +## Error 86: `PacedTransactionManualCommitError` + +Development builds show the full message. For example: + +```text +Paced mutations are committed by their strategy. Await the transaction receipt or roll it back instead. +``` + + + +## Error 87: `UnhashableQueryIRError` + +Development builds show the full message. For example: + +```text +Query IR is not stably hashable at query.fnSelect: function select +``` + + + +## Error 175: `DatabaseRequiredError` + +Development builds show the full message. For example: + +```text +IndexedDB collection requires a "db" configuration option. Create a database instance using createIndexedDB() and pass it to the collection. +``` + + + +## Error 176: `ObjectStoreNotFoundError` + +Development builds show the full message. For example: + +```text +Object store "todos" not found in database "app". Available stores: [users, posts]. Add "todos" to the stores array when calling createIndexedDB(). +``` + + + +## Error 177: `NameRequiredError` + +Development builds show the full message. For example: + +```text +IndexedDB collection requires a "name" configuration option. This is the name of the object store within the database. +``` + + + +## Error 178: `GetKeyRequiredError` + +Development builds show the full message. For example: + +```text +IndexedDB collection requires a "getKey" configuration option. This function extracts the unique key from each item. +``` + + + +## Error 233: `DuplicateTransactionIdError` + +Development builds show the full message. For example: + +```text +A Collection already tracks an unsettled transaction with id "tx-1". Give each transaction a unique id. +``` + + + +## Error 244: `SetWindowFindOneLimitError` + +Development builds show the full message. For example: + +```text +setWindow() cannot change the limit of a findOne() query. +``` + + + +## Error 245: `SourceCollectionIdReuseError` + +Development builds show the full message. For example: + +```text +[useLiveQuery] Collection ID "todos" named a different source Collection than one this live query already read. While the hook is mounted, each Collection ID must keep naming one source Collection. Unmount the hook and clean up its previous source and client scope before reusing the ID. +``` + + + +## Error 247: `GroupingOperatorRequiredError` + +Development builds show the full message. For example: + +```text +groupBy() without an aggregate needs groupKeys(), such as .groupBy(groupKeys(({ todo }) => todo.status)). Import groupKeys from @tanstack/db. A query with an aggregate such as count() needs nothing extra. Query IR built by hand passes groupingCompiler to each IR.Aggregate. +``` + + + +## Error 248: `IncludeOperatorRequiredError` + +Development builds show the full message. For example: + +```text +A subquery in select() needs include(), toArray(), or materialize(). Wrap the issues field: issues: include(q.from({ i: issues }).where(...)). Import include from @tanstack/db. Query IR built by hand passes includesRuntime to each IR.IncludesSubquery. +``` + + + +## Error 251: `InvalidPoolOptionError` + +Development builds show the full message. For example: + +```text +A live query's pool option takes pooledLiveQueries from @tanstack/db, or nothing. Pooling is opt-in: pass pool: pooledLiveQueries to share one partition among equality-filtered queries. +``` + +## Other errors and log messages + +These are plain `Error`, `TypeError`, `RangeError`, or +`AggregateError` values, and `console` messages about runtime failures. +Each heading names the source file under `packages/db/src` that writes it. + + + +## Error 88: `client.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +collectionOptions requires a non-empty explicit id so the descriptor is stable across DbClient instances and SSR boundaries. +``` + + + +## Error 89: `client.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +collectionOptions("${id}") requires a factory as its second argument. +``` + + + +## Error 90: `client.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Collection descriptor "${id}" was created from a concrete config that cannot be safely reused across DbClient instances. Use collectionOptions("${id}", (client) => adapterCollectionOptions(...)) or an adapter options creator that supports DbClient materialization. +``` + + + +## Error 91: `client.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Collection descriptor "${id}" materialized a config with id "${materialized.id}". Descriptor and collection ids must match. +``` + + + +## Error 92: `client.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +DbClient is missing the required "${key}" dependency. Pass it explicitly when constructing the client: new DbClient({ ${key} }). +``` + + + +## Error 93: `client.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Dehydrated live query "${dehydratedQuery.queryHash}" has neither a snapshot nor a promise. +``` + + + +## Error 94: `collection/change-events.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +limit cannot be used without orderBy +``` + + + +## Error 95: `collection/changes.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Cannot specify both 'where' and 'whereExpression' options. Use one or the other. +``` + + + +## Error 96: `collection/subscription.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +limit is required +``` + + + +## Error 97: `collection/subscription.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Ordered snapshot was requested but no index was found. You have to call setOrderByIndex before requesting an ordered snapshot. +``` + + + +## Error 98: `event-emitter.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Timeout waiting for event ${String(event)} +``` + + + +## Error 99: `indexes/base-index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Failed to evaluate index expression for key ${key}: ${error} +``` + + + +## Error 100: `indexes/basic-index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Operation ${operation} not supported by BasicIndex +``` + + + +## Error 101: `indexes/btree-index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Operation ${operation} not supported by BTreeIndex +``` + + + +## Error 102: `live-query-options.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +A live query must be a QueryBuilder, LiveQueryCollectionConfig, Collection, undefined, or null. Got: ${typeof value} +``` + + + +## Error 103: `live-query-window-controller.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +useLiveInfiniteQuery: First argument must be either a pre-created live query collection or a query function. Received: ${typeof input} +``` + + + +## Error 104: `live-query-window-controller.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +useLiveInfiniteQuery: Query function must return a query builder. Disabled null or undefined queries are not supported. +``` + + + +## Error 105: `live-query-window-controller.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +useLiveInfiniteQuery: Infinite queries do not support single-result queries. Remove .findOne(). +``` + + + +## Error 106: `live-query-window-controller.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +useLiveInfiniteQuery: Pre-created live query collection must have an ORDER BY (orderBy) clause for infinite pagination to work. Please add .orderBy() to your createLiveQueryCollection query. +``` + + + +## Error 107: `paced-mutations.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Strategy callback called but transaction is in state "${transaction.state}". Expected "pending". +``` + + + +## Error 108: `paced-mutations.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Paced transaction has no strategy-owned commit +``` + + + +## Error 109: `proxy.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +forEach callback must be a function +``` + + + +## Error 110: `query/builder/functions.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +concat(toArray(...)) currently supports only a single toArray(...) argument +``` + + + +## Error 111: `query/builder/functions.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +caseWhen() requires at least two arguments +``` + + + +## Error 112: `query/builder/functions.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +caseWhen() conditions must be expression-like values +``` + + + +## Error 113: `query/ir.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Collection descriptor "${source.alias}" requires a DbClient when the query is consumed. Bind the query through a client-aware API or use a concrete Collection. +``` + + + +## Error 114: `query/builder/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Conditional include branch is missing a field name +``` + + + +## Error 115: `query/builder/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +toArray() must wrap a subquery builder +``` + + + +## Error 116: `query/builder/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Conditional toArray() branch is missing a field name +``` + + + +## Error 117: `query/builder/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +concat(toArray(...)) must wrap a subquery builder +``` + + + +## Error 118: `query/builder/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Conditional concat(toArray(...)) branch is missing a field name +``` + + + +## Error 119: `query/builder/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +toArray() must wrap a subquery builder +``` + + + +## Error 120: `query/builder/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +concat(toArray(...)) must wrap a subquery builder +``` + + + +## Error 121: `query/builder/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +materialize() must wrap a subquery builder +``` + + + +## Error 122: `query/builder/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +caseWhen() requires at least two arguments +``` + + + +## Error 123: `query/builder/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Includes subquery for "${fieldName}" must have a WHERE clause with an eq() condition that correlates a parent field with a child field. Example: .where(({child}) => eq(child.parentId, parent.id)) +``` + + + +## Error 124: `query/builder/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +concat(toArray(...)) for "${fieldName}" requires the subquery to select a scalar value +``` + + + +## Error 125: `query/builder/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Includes subquery for "${fieldName}" must select an object when materializing as a Collection +``` + + + +## Error 126: `query/builder/ref-proxy.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +${name} cannot be used inside expressions (e.g., coalesce(), eq(), not()). Use ${name} directly as a select field value instead. +``` + + + +## Error 127: `query/compiler/evaluators.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +caseWhen() requires at least two arguments +``` + + + +## Error 128: `query/compiler/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +fn.select() cannot consume Collection-valued includes. Use toArray() or materialize() in the upstream select(), or use an expression select() to keep live Collections. +``` + + + +## Error 129: `query/compiler/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Query row has negative multiplicity +``` + + + +## Error 130: `query/compiler/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Query row has no positive contributor +``` + + + +## Error 131: `query/compiler/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Query contributors with the same row key are not congruent +``` + + + +## Error 132: `query/compiler/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Duplicate source alias "${source.alias}" in unionAll query branches. Use distinct aliases in each branch before passing them to unionAll(). +``` + + + +## Error 133: `query/compiler/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Includes subqueries must be at the top level of select(). Found nested includes at "${parentPath}.${key}". +``` + + + +## Error 134: `query/effect.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Source collection '${collectionId}' was cleaned up while effect depends on it +``` + + + +## Error 135: `query/effect.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Source collection '${collectionId}' entered ${status} state +``` + + + +## Error 136: `query/expression-helpers.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +No handler provided for operator: ${name}. Available handlers: ${Object.keys(handlers).join(`, `)} +``` + + + +## Error 137: `query/expression-helpers.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +ORDER BY expression must be a field reference, got: ${clause.expression.type} +``` + + + +## Error 138: `query/expression-helpers.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +extractSimpleComparisons requires a comparison or null check inside 'not' operator. +``` + + + +## Error 139: `query/expression-helpers.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +extractSimpleComparisons requires a field reference for '${arg.name}' operator. +``` + + + +## Error 140: `query/expression-helpers.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +extractSimpleComparisons requires simple field-value comparisons. Found complex expression for 'not(${arg.name})' operator. +``` + + + +## Error 141: `query/expression-helpers.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +extractSimpleComparisons does not support 'not(${arg.name})'. NOT can only wrap comparison operators (eq, gt, gte, lt, lte, in) or null checks (isNull, isUndefined). +``` + + + +## Error 142: `query/expression-helpers.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +extractSimpleComparisons does not support '${e.name}' operator. Use parseWhereExpression with custom handlers for complex expressions. +``` + + + +## Error 143: `query/expression-helpers.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +extractSimpleComparisons requires a field reference for '${e.name}' operator. +``` + + + +## Error 144: `query/expression-helpers.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +extractSimpleComparisons requires simple field-value comparisons. Found complex expression for '${e.name}' operator. +``` + + + +## Error 145: `query/expression-helpers.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +extractSimpleComparisons encountered unknown operator: '${e.name}' +``` + + + +## Error 146: `query/live/bucket-facade-adapter.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Bucket facade row has no public key +``` + + + +## Error 147: `query/live/collection-config-builder.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Source recovery failed +``` + + + +## Error 148: `query/live/collection-config-builder.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +maybeRunGraph called without active sync run. This should not happen. +``` + + + +## Error 149: `query/live/collection-config-builder.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +scheduleGraphRun called without active sync run. This should not happen. +``` + + + +## Error 150: `query/live/collection-config-builder.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Live query result key ${String(key)} changed by ${inserts - deletes} rows in one flush; a key has at most one result row. +``` + + + +## Error 151: `query/live/collection-config-builder.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Could not apply changes: ${JSON.stringify(changes)}. This should never happen. +``` + + + +## Error 152: `query/live/collection-config-builder.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Source collection '${collectionId}' was manually cleaned up while live query '${this.id}' depends on it. Live queries prevent automatic GC, so this was likely a manual cleanup() call. +``` + + + +## Error 153: `query/live/collection-config-builder.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Query '${this.id}' has no collection sources. This should not happen; please report. +``` + + + +## Error 154: `query/live/public-key-reduction.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Canonical query row has negative multiplicity +``` + + + +## Error 155: `query/live/public-key-reduction.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Canonical query row has no positive contributor +``` + + + +## Error 156: `query/live/public-key-reduction.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Query contributors for public key ${serializeValue(left.publicKey)} are not congruent +``` + + + +## Error 157: `query/live/materialized-pipeline.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Materialization bucket row has negative multiplicity +``` + + + +## Error 158: `query/live/materialized-pipeline.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Collection includes require a bucket facade +``` + + + +## Error 159: `query/live/subset-demand-controller.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Subset demand snapshot did not start +``` + + + +## Error 160: `query/live/utils.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Failed to extract collection. Invalid FROM clause: ${JSON.stringify(query)} +``` + + + +## Error 161: `scheduler.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Scheduler detected unresolved dependencies for context ${String(contextId)}. +``` + + + +## Error 162: `transactions.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +A transaction created with createTransaction() cannot mutate collections from multiple DbClient instances. Use dbClient.createTransaction() for explicit client scope. +``` + + + +## Error 163: `transactions.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Transaction is not associated with a TransactionScope. +``` + + + +## Error 164: `transactions.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Transaction is not associated with an ambient scope. +``` + + + +## Error 165: `transactions.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Unhandled mutation combination: ${_exhaustive} +``` + + + +## Error 166: `utils/comparison.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Index comparator must return a number, but returned ${String(result)} +``` + + + +## Error 167: `utils/comparison.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Cannot order Temporal values of different types: ${aTag} vs ${bTag} +``` + + + +## Error 168: `utils/comparison.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +${aTag} has no defined ordering +``` + + + +## Error 169: `utils/cursor.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Only leading-column cursors are supported +``` + + + +## Error 170: `utils/error.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Unknown error +``` + + + +## Error 171: `utils/uuid.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +No secure random number generator available: neither crypto.randomUUID nor crypto.getRandomValues is defined in this environment. +``` + + + +## Error 172: `query/live/collection-config-builder.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Source collection '${collectionId}' crashed +``` + + + +## Error 173: `collection/lifecycle.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +${syncFailure ? Adapter cleanup and local teardown both failed : Multiple local teardown steps failed} +``` + + + +## Error 174: `transactions.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Mutation callback and restoration failed +``` + + + +## Error 179: `indexed-db-wrapper.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +IndexedDB is not available in this environment. Ensure you are running in a browser or provide a custom IDBFactory for testing. +``` + + + +## Error 182: `indexed-db-wrapper.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Failed to open IndexedDB database "${name}": ${error instanceof Error ? error.message : String(error)} +``` + + + +## Error 183: `indexed-db-wrapper.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Database upgrade failed for "${name}": ${error instanceof Error ? error.message : String(error)} +``` + + + +## Error 184: `indexed-db-wrapper.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Failed to open IndexedDB database "${name}": ${errorMessage} +``` + + + +## Error 185: `indexed-db-wrapper.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Cannot create object store "${storeName}": This operation is only allowed during a database upgrade. Ensure you are calling createObjectStore within the onUpgrade callback of openDatabase. +``` + + + +## Error 186: `indexed-db-wrapper.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Object store "${storeName}" already exists in the database. Check the database version and only create stores when needed. +``` + + + +## Error 187: `indexed-db-wrapper.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Failed to create object store "${storeName}": ${error instanceof Error ? error.message : String(error)} +``` + + + +## Error 188: `indexed-db-wrapper.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Failed to create transaction for stores [${storeNamesArray.join(', ')}]: ${error instanceof Error ? error.message : String(error)} +``` + + + +## Error 189: `indexed-db-wrapper.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Object store "${storeName}" not found in the database. Ensure the store was created during the database upgrade. +``` + + + +## Error 190: `indexed-db-wrapper.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Transaction was aborted +``` + + + +## Error 191: `indexed-db-wrapper.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Failed to delete IndexedDB database "${name}": ${error instanceof Error ? error.message : String(error)} +``` + + + +## Error 192: `indexed-db-wrapper.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Failed to delete IndexedDB database "${name}": ${errorMessage} +``` + + + +## Error 193: `indexed-db.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +createIndexedDB requires at least one store in the stores array. +``` + + + +## Error 194: `indexed-db.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +createIndexedDB stores array contains duplicate store names. +``` + + + +## Error 195: `indexed-db.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +The "_versions" store is reserved for IndexedDB Collection metadata. +``` + + + +## Error 196: `indexed-db.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +createIndexedDB stores array contains invalid store names. Each store name must be a non-empty string. +``` + + + +## Error 197: `indexed-db.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +IndexedDB connection "${name}" closed. Recreate its Collections with a new database instance. +``` + + + +## Error 198: `indexed-db.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +The "_versions" store is reserved for IndexedDB Collection metadata. +``` + + + +## Error 199: `indexed-db.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Duplicate imported key: ${key} +``` + + + +## Error 200: `indexed-db-wrapper.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Failed to get all keys from object store "${objectStore.name}": ${cause} +``` + + + +## Error 201: `indexed-db-wrapper.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Failed to write item to object store "${objectStore.name}": ${cause} +``` + + + +## Error 202: `indexed-db-wrapper.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Failed to delete item with key "${String(key)}" from object store "${objectStore.name}": ${cause} +``` + + + +## Error 203: `indexed-db-wrapper.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Failed to clear object store "${objectStore.name}": ${cause} +``` + + + +## Error 204: `indexed-db-wrapper.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Failed to get all items from object store "${objectStore.name}": ${cause} +``` + + + +## Error 205: `indexed-db-wrapper.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Failed to get item with key "${String(key)}" from object store "${objectStore.name}": ${cause} +``` + + + +## Error 206: `collection/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +autoIndex: 'eager' requires defaultIndexType to be set. Import an index type and set it: + import { BasicIndex } from '@tanstack/db' + createCollection({ defaultIndexType: BasicIndex, autoIndex: 'eager', ... }) +``` + + + +## Error 207: `collection/indexes.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +No index type specified and no defaultIndexType set on collection. Either pass indexType in config, or set defaultIndexType on the collection: + import { BasicIndex } from '@tanstack/db' + createCollection({ defaultIndexType: BasicIndex, ... }) +``` + + + +## Error 208: `collection/lifecycle.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +You can't directly call "setStatus('ready'). You must use markReady instead. +``` + + + +## Error 209: `collection/lifecycle.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Cannot start collection "${this.id}" during cleanup. Restart after cleanup() completes. +``` + + + +## Error 210: `collection/sync.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Collection "${this.id}" is configured with syncMode "on-demand" but the sync function did not return a loadSubset handler. Either provide a loadSubset handler or use syncMode "eager". +``` + + + +## Error 211: `query/compiler/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Query uses alias "${alias}" more than once. Give each source in one query a distinct alias. +``` + + + +## Error 212: `query/live/collection-config-builder.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Live query graph failed +``` + + + +## Warning 213: `collection/change-events.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +${collection.id ? `[${collection.id}] ` : ``}Error processing where clause, falling back to full scan: +``` + + + +## Error 214: `collection/cleanup-queue.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Error in CleanupQueue task: +``` + + + +## Warning 215: `indexes/auto-index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +${collection.id ? `[${collection.id}] ` : ``}Failed to create auto-index for field path "${fieldPath.join(`.`)}": +``` + + + +## Warning 216: `indexes/base-index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Failed to evaluate index expression for key ${key} during removal: +``` + + + +## Error 218: `local-storage.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +[LocalStorageCollection] Error saving data to storage key "${config.storageKey}": +``` + + + +## Warning 219: `local-storage.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +[LocalStorageCollection] Error loading data from storage key "${storageKey}": +``` + + + +## Error 220: `query/effect.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +[Effect '${id}'] onSourceError callback threw: +``` + + + +## Error 221: `query/effect.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +[Effect '${id}'] ${error.message}. Disposing effect. +``` + + + +## Error 222: `query/effect.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +[Effect '${id}'] failed to dispose after a source error: +``` + + + +## Error 223: `query/effect.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +[Effect '${id}'] failed to dispose after a startup error: +``` + + + +## Error 224: `query/effect.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +[Effect] Error in onError handler: +``` + + + +## Error 225: `query/effect.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +[Effect] Original error: +``` + + + +## Error 226: `query/effect.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +[Effect] Unhandled error in handler: +``` + + + +## Error 228: `query/live/collection-config-builder.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Subset demand '${planId}' failed: ${normalized.message} +``` + + + +## Error 229: `query/live/collection-config-builder.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Initial subset load failed: ${normalized.message} +``` + + + +## Error 230: `local-storage.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +LocalStorage options can create only one Collection. Create fresh options for each Collection. +``` + + + +## Error 231: `local-storage.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +LocalStorage manual acceptance belongs to a different Collection. +``` + + + +## Warning 232: `local-storage.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +[LocalStorageCollection] Error refreshing a same-tab peer for storage key "${key}": +``` + + + +## Error 234: `client.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +DbClient cannot reuse live query "${queryHash}" with different source Collections. Use the same Collection objects or a distinct query key. +``` + + + +## Error 235: `query/live/bucket-facade-adapter.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Bucket facade received graph output between a flush and its rollback +``` + + + +## Error 236: `query/builder/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Includes subquery for "${fieldName}" reuses an ancestor's source declaration. Use new Query().from({ alias: collection }) in the child instead of passing the ancestor builder to from(). +``` + + + +## Error 237: `query/builder/query-ir.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Query must resolve to a QueryBuilder; received ${received}. +``` + + + +## Error 238: `query/compiler/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Query reference "${ref.path.join(`.`)}" is out of scope. Use a source from this query or a containing query. +``` + + + +## Error 239: `query/compiler/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Include query is missing a captured parent projection for "${ref.path.join(`.`)}". +``` + + + +## Error 240: `query/live/bucket-facade-adapter.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Bucket facade retired with rows the graph did not retract +``` + + + +## Error 241: `query/live/bucket-facade-adapter.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +A live query's child collection cannot be cleaned up directly. Clean up the live query instead. +``` + + + +## Error 242: `local-storage.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +LocalStorage sync belongs to its options Collection. +``` + + + +## Error 243: `query/compiler/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +A windowed or findOne result row has no include route +``` + + + +## Error 246: `live-query-window-binding.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +getNextPageParam is not supported by useLiveInfiniteQuery. Use an on-demand collection and fulfill meta.loadSubsetOptions in queryFn for server pagination. +``` + + + +## Error 249: `query/builder/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +include() must wrap a subquery builder +``` + + + +## Error 250: `query/builder/index.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +include() must wrap a subquery builder +``` + + + +## Error 252: `live-query-observer-client.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +SSR cannot render server-only live-query rows or readiness. Await DbClient.preloadLiveQuery() for this query, or explicitly preload its source Collections for hydration. Opaque queries require an explicit serializable queryKey. +``` + + + +## Error 253: `client.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +The DB stream ended before the server-rendered query snapshot arrived. +``` + + + +## Error 257: `collection/lifecycle.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +${operation}() reports on sync after the first ready, but Collection "${this.id}" is ${this.status}. Before the first ready, retry without reporting, or call fail() when the sync adapter stops. +``` + + + +## Error 258: `query/pooled-live-query.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +The pooled live query's source ended +``` + + + +## Error 259: `collection/lifecycle.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +${operation}() was called after fail() in the same sync run of Collection "${this.id}". fail() means the sync adapter stopped; a reload starts a new sync run. +``` + + + +## Error 260: `query/live/collection-config-builder.ts` + +Development builds show this message, with `${...}` replaced by values: + +```text +Source collection '${collectionId}' failed its first load +``` diff --git a/docs/framework/angular/reference/functions/injectLiveQuery.md b/docs/framework/angular/reference/functions/injectLiveQuery.md index 5eb790a543..d45309c46b 100644 --- a/docs/framework/angular/reference/functions/injectLiveQuery.md +++ b/docs/framework/angular/reference/functions/injectLiveQuery.md @@ -3,15 +3,13 @@ id: injectLiveQuery title: injectLiveQuery --- -# Function: injectLiveQuery() - ## Call Signature ```ts function injectLiveQuery(options): InjectLiveQueryResult; ``` -Defined in: [index.ts:89](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L89) +Defined in: [angular-db/src/index.ts:112](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L112) ### Type Parameters @@ -31,6 +29,13 @@ Defined in: [index.ts:89](https://github.com/TanStack/db/blob/main/packages/angu () => `TParams` +##### pool? + +\{ +\} + +Pass `pooledLiveQueries` to share one partition among equal-shape queries. + ##### query (`args`) => `QueryBuilder`\<`TContext`\> @@ -42,10 +47,10 @@ Defined in: [index.ts:89](https://github.com/TanStack/db/blob/main/packages/angu ## Call Signature ```ts -function injectLiveQuery(options): InjectLiveQueryResult; +function injectLiveQuery(options): InjectConditionalLiveQueryResult; ``` -Defined in: [index.ts:99](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L99) +Defined in: [angular-db/src/index.ts:124](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L124) ### Type Parameters @@ -65,13 +70,20 @@ Defined in: [index.ts:99](https://github.com/TanStack/db/blob/main/packages/angu () => `TParams` +##### pool? + +\{ +\} + +Pass `pooledLiveQueries` to share one partition among equal-shape queries. + ##### query (`args`) => `QueryBuilder`\<`TContext`\> \| `null` \| `undefined` ### Returns -[`InjectLiveQueryResult`](../interfaces/InjectLiveQueryResult.md)\<`TContext`\> +[`InjectConditionalLiveQueryResult`](../type-aliases/InjectConditionalLiveQueryResult.md)\<`TContext`\> ## Call Signature @@ -79,7 +91,7 @@ Defined in: [index.ts:99](https://github.com/TanStack/db/blob/main/packages/angu function injectLiveQuery(queryFn): InjectLiveQueryResult; ``` -Defined in: [index.ts:109](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L109) +Defined in: [angular-db/src/index.ts:136](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L136) ### Type Parameters @@ -100,10 +112,10 @@ Defined in: [index.ts:109](https://github.com/TanStack/db/blob/main/packages/ang ## Call Signature ```ts -function injectLiveQuery(queryFn): InjectLiveQueryResult; +function injectLiveQuery(queryFn): InjectConditionalLiveQueryResult; ``` -Defined in: [index.ts:112](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L112) +Defined in: [angular-db/src/index.ts:139](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L139) ### Type Parameters @@ -119,7 +131,7 @@ Defined in: [index.ts:112](https://github.com/TanStack/db/blob/main/packages/ang ### Returns -[`InjectLiveQueryResult`](../interfaces/InjectLiveQueryResult.md)\<`TContext`\> +[`InjectConditionalLiveQueryResult`](../type-aliases/InjectConditionalLiveQueryResult.md)\<`TContext`\> ## Call Signature @@ -127,7 +139,7 @@ Defined in: [index.ts:112](https://github.com/TanStack/db/blob/main/packages/ang function injectLiveQuery(config): InjectLiveQueryResult; ``` -Defined in: [index.ts:117](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L117) +Defined in: [angular-db/src/index.ts:144](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L144) ### Type Parameters @@ -139,7 +151,7 @@ Defined in: [index.ts:117](https://github.com/TanStack/db/blob/main/packages/ang #### config -`LiveQueryCollectionConfig`\<`TContext`\> +`LiveQueryHookConfig`\<`TContext`\> ### Returns @@ -151,7 +163,7 @@ Defined in: [index.ts:117](https://github.com/TanStack/db/blob/main/packages/ang function injectLiveQuery(liveQueryCollection): InjectLiveQueryResultWithCollection; ``` -Defined in: [index.ts:121](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L121) +Defined in: [angular-db/src/index.ts:148](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L148) ### Type Parameters @@ -183,7 +195,7 @@ Defined in: [index.ts:121](https://github.com/TanStack/db/blob/main/packages/ang function injectLiveQuery(liveQueryCollection): InjectLiveQueryResultWithSingleResultCollection; ``` -Defined in: [index.ts:129](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L129) +Defined in: [angular-db/src/index.ts:156](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L156) ### Type Parameters diff --git a/docs/framework/angular/reference/index.md b/docs/framework/angular/reference/index.md index 493d26dedb..33f0f1ce56 100644 --- a/docs/framework/angular/reference/index.md +++ b/docs/framework/angular/reference/index.md @@ -3,14 +3,17 @@ id: "@tanstack/angular-db" title: "@tanstack/angular-db" --- -# @tanstack/angular-db - ## Interfaces +- [InjectLiveQueryFlags](interfaces/InjectLiveQueryFlags.md) - [InjectLiveQueryResult](interfaces/InjectLiveQueryResult.md) - [InjectLiveQueryResultWithCollection](interfaces/InjectLiveQueryResultWithCollection.md) - [InjectLiveQueryResultWithSingleResultCollection](interfaces/InjectLiveQueryResultWithSingleResultCollection.md) +## Type Aliases + +- [InjectConditionalLiveQueryResult](type-aliases/InjectConditionalLiveQueryResult.md) + ## Functions - [injectLiveQuery](functions/injectLiveQuery.md) diff --git a/docs/framework/angular/reference/interfaces/InjectLiveQueryFlags.md b/docs/framework/angular/reference/interfaces/InjectLiveQueryFlags.md new file mode 100644 index 0000000000..d6aa51020f --- /dev/null +++ b/docs/framework/angular/reference/interfaces/InjectLiveQueryFlags.md @@ -0,0 +1,122 @@ +--- +id: InjectLiveQueryFlags +title: InjectLiveQueryFlags +--- + +Defined in: [angular-db/src/index.ts:44](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L44) + +Status, persistence, and health signals every `injectLiveQuery` result has. + +## Extended by + +- [`InjectLiveQueryResult`](InjectLiveQueryResult.md) +- [`InjectLiveQueryResultWithCollection`](InjectLiveQueryResultWithCollection.md) +- [`InjectLiveQueryResultWithSingleResultCollection`](InjectLiveQueryResultWithSingleResultCollection.md) + +## Properties + +### health + +```ts +health: Signal; +``` + +Defined in: [angular-db/src/index.ts:62](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L62) + +A signal containing the health tree for the query and its dependencies + +*** + +### isCleanedUp + +```ts +isCleanedUp: Signal; +``` + +Defined in: [angular-db/src/index.ts:58](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L58) + +A signal indicating whether the collection has been cleaned up + +*** + +### isCrashed + +```ts +isCrashed: Signal; +``` + +Defined in: [angular-db/src/index.ts:56](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L56) + +Local storage or an invariant broke. Only a cascade cleanup and a fresh start recover. + +*** + +### isIdle + +```ts +isIdle: Signal; +``` + +Defined in: [angular-db/src/index.ts:52](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L52) + +A signal indicating whether the collection is idle + +*** + +### isLoading + +```ts +isLoading: Signal; +``` + +Defined in: [angular-db/src/index.ts:48](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L48) + +A signal indicating whether the collection is currently loading + +*** + +### isLoadingError + +```ts +isLoadingError: Signal; +``` + +Defined in: [angular-db/src/index.ts:54](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L54) + +The first load failed: there is nothing to show. + +*** + +### isReady + +```ts +isReady: Signal; +``` + +Defined in: [angular-db/src/index.ts:50](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L50) + +A signal indicating whether the collection is ready + +*** + +### isSyncError + +```ts +isSyncError: Signal; +``` + +Defined in: [angular-db/src/index.ts:60](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L60) + +The data is shown, and a sync problem or failed request is in `health`. + +*** + +### status + +```ts +status: Signal; +``` + +Defined in: [angular-db/src/index.ts:46](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L46) + +A signal containing the current status of the collection diff --git a/docs/framework/angular/reference/interfaces/InjectLiveQueryResult.md b/docs/framework/angular/reference/interfaces/InjectLiveQueryResult.md index 066d6b5bab..7738bfb842 100644 --- a/docs/framework/angular/reference/interfaces/InjectLiveQueryResult.md +++ b/docs/framework/angular/reference/interfaces/InjectLiveQueryResult.md @@ -3,12 +3,13 @@ id: InjectLiveQueryResult title: InjectLiveQueryResult --- -# Interface: InjectLiveQueryResult\ +Defined in: [angular-db/src/index.ts:65](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L65) -Defined in: [index.ts:32](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L32) +Status, persistence, and health signals every `injectLiveQuery` result has. -The result of calling `injectLiveQuery`. -Contains reactive signals for the query state and data. +## Extends + +- [`InjectLiveQueryFlags`](InjectLiveQueryFlags.md) ## Type Parameters @@ -27,7 +28,7 @@ collection: Signal< | null>; ``` -Defined in: [index.ts:38](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L38) +Defined in: [angular-db/src/index.ts:73](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L73) A signal containing the underlying collection instance (null for disabled queries) @@ -39,33 +40,57 @@ A signal containing the underlying collection instance (null for disabled querie data: Signal>; ``` -Defined in: [index.ts:36](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L36) +Defined in: [angular-db/src/index.ts:71](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L71) A signal containing the results as an array, or single result for findOne queries *** +### health + +```ts +health: Signal; +``` + +Defined in: [angular-db/src/index.ts:62](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L62) + +A signal containing the health tree for the query and its dependencies + +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`health`](InjectLiveQueryFlags.md#health) + +*** + ### isCleanedUp ```ts isCleanedUp: Signal; ``` -Defined in: [index.ts:54](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L54) +Defined in: [angular-db/src/index.ts:58](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L58) A signal indicating whether the collection has been cleaned up +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`isCleanedUp`](InjectLiveQueryFlags.md#iscleanedup) + *** -### isError +### isCrashed ```ts -isError: Signal; +isCrashed: Signal; ``` -Defined in: [index.ts:52](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L52) +Defined in: [angular-db/src/index.ts:56](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L56) + +Local storage or an invariant broke. Only a cascade cleanup and a fresh start recover. -A signal indicating whether the collection has an error +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`isCrashed`](InjectLiveQueryFlags.md#iscrashed) *** @@ -75,10 +100,14 @@ A signal indicating whether the collection has an error isIdle: Signal; ``` -Defined in: [index.ts:50](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L50) +Defined in: [angular-db/src/index.ts:52](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L52) A signal indicating whether the collection is idle +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`isIdle`](InjectLiveQueryFlags.md#isidle) + *** ### isLoading @@ -87,10 +116,30 @@ A signal indicating whether the collection is idle isLoading: Signal; ``` -Defined in: [index.ts:46](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L46) +Defined in: [angular-db/src/index.ts:48](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L48) A signal indicating whether the collection is currently loading +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`isLoading`](InjectLiveQueryFlags.md#isloading) + +*** + +### isLoadingError + +```ts +isLoadingError: Signal; +``` + +Defined in: [angular-db/src/index.ts:54](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L54) + +The first load failed: there is nothing to show. + +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`isLoadingError`](InjectLiveQueryFlags.md#isloadingerror) + *** ### isReady @@ -99,10 +148,30 @@ A signal indicating whether the collection is currently loading isReady: Signal; ``` -Defined in: [index.ts:48](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L48) +Defined in: [angular-db/src/index.ts:50](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L50) A signal indicating whether the collection is ready +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`isReady`](InjectLiveQueryFlags.md#isready) + +*** + +### isSyncError + +```ts +isSyncError: Signal; +``` + +Defined in: [angular-db/src/index.ts:60](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L60) + +The data is shown, and a sync problem or failed request is in `health`. + +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`isSyncError`](InjectLiveQueryFlags.md#issyncerror) + *** ### state @@ -111,7 +180,7 @@ A signal indicating whether the collection is ready state: Signal[K] }>>; ``` -Defined in: [index.ts:34](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L34) +Defined in: [angular-db/src/index.ts:69](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L69) A signal containing the complete state map of results keyed by their ID @@ -123,6 +192,10 @@ A signal containing the complete state map of results keyed by their ID status: Signal; ``` -Defined in: [index.ts:44](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L44) +Defined in: [angular-db/src/index.ts:46](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L46) A signal containing the current status of the collection + +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`status`](InjectLiveQueryFlags.md#status) diff --git a/docs/framework/angular/reference/interfaces/InjectLiveQueryResultWithCollection.md b/docs/framework/angular/reference/interfaces/InjectLiveQueryResultWithCollection.md index c6f1c6c3bc..0b7e486571 100644 --- a/docs/framework/angular/reference/interfaces/InjectLiveQueryResultWithCollection.md +++ b/docs/framework/angular/reference/interfaces/InjectLiveQueryResultWithCollection.md @@ -3,9 +3,13 @@ id: InjectLiveQueryResultWithCollection title: InjectLiveQueryResultWithCollection --- -# Interface: InjectLiveQueryResultWithCollection\ +Defined in: [angular-db/src/index.ts:92](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L92) -Defined in: [index.ts:57](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L57) +Status, persistence, and health signals every `injectLiveQuery` result has. + +## Extends + +- [`InjectLiveQueryFlags`](InjectLiveQueryFlags.md) ## Type Parameters @@ -32,7 +36,7 @@ collection: Signal< | null>; ``` -Defined in: [index.ts:64](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L64) +Defined in: [angular-db/src/index.ts:99](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L99) *** @@ -42,7 +46,23 @@ Defined in: [index.ts:64](https://github.com/TanStack/db/blob/main/packages/angu data: Signal; ``` -Defined in: [index.ts:63](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L63) +Defined in: [angular-db/src/index.ts:98](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L98) + +*** + +### health + +```ts +health: Signal; +``` + +Defined in: [angular-db/src/index.ts:62](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L62) + +A signal containing the health tree for the query and its dependencies + +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`health`](InjectLiveQueryFlags.md#health) *** @@ -52,17 +72,29 @@ Defined in: [index.ts:63](https://github.com/TanStack/db/blob/main/packages/angu isCleanedUp: Signal; ``` -Defined in: [index.ts:70](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L70) +Defined in: [angular-db/src/index.ts:58](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L58) + +A signal indicating whether the collection has been cleaned up + +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`isCleanedUp`](InjectLiveQueryFlags.md#iscleanedup) *** -### isError +### isCrashed ```ts -isError: Signal; +isCrashed: Signal; ``` -Defined in: [index.ts:69](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L69) +Defined in: [angular-db/src/index.ts:56](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L56) + +Local storage or an invariant broke. Only a cascade cleanup and a fresh start recover. + +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`isCrashed`](InjectLiveQueryFlags.md#iscrashed) *** @@ -72,7 +104,13 @@ Defined in: [index.ts:69](https://github.com/TanStack/db/blob/main/packages/angu isIdle: Signal; ``` -Defined in: [index.ts:68](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L68) +Defined in: [angular-db/src/index.ts:52](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L52) + +A signal indicating whether the collection is idle + +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`isIdle`](InjectLiveQueryFlags.md#isidle) *** @@ -82,7 +120,29 @@ Defined in: [index.ts:68](https://github.com/TanStack/db/blob/main/packages/angu isLoading: Signal; ``` -Defined in: [index.ts:66](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L66) +Defined in: [angular-db/src/index.ts:48](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L48) + +A signal indicating whether the collection is currently loading + +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`isLoading`](InjectLiveQueryFlags.md#isloading) + +*** + +### isLoadingError + +```ts +isLoadingError: Signal; +``` + +Defined in: [angular-db/src/index.ts:54](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L54) + +The first load failed: there is nothing to show. + +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`isLoadingError`](InjectLiveQueryFlags.md#isloadingerror) *** @@ -92,7 +152,29 @@ Defined in: [index.ts:66](https://github.com/TanStack/db/blob/main/packages/angu isReady: Signal; ``` -Defined in: [index.ts:67](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L67) +Defined in: [angular-db/src/index.ts:50](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L50) + +A signal indicating whether the collection is ready + +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`isReady`](InjectLiveQueryFlags.md#isready) + +*** + +### isSyncError + +```ts +isSyncError: Signal; +``` + +Defined in: [angular-db/src/index.ts:60](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L60) + +The data is shown, and a sync problem or failed request is in `health`. + +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`isSyncError`](InjectLiveQueryFlags.md#issyncerror) *** @@ -102,7 +184,7 @@ Defined in: [index.ts:67](https://github.com/TanStack/db/blob/main/packages/angu state: Signal>; ``` -Defined in: [index.ts:62](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L62) +Defined in: [angular-db/src/index.ts:97](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L97) *** @@ -112,4 +194,10 @@ Defined in: [index.ts:62](https://github.com/TanStack/db/blob/main/packages/angu status: Signal; ``` -Defined in: [index.ts:65](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L65) +Defined in: [angular-db/src/index.ts:46](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L46) + +A signal containing the current status of the collection + +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`status`](InjectLiveQueryFlags.md#status) diff --git a/docs/framework/angular/reference/interfaces/InjectLiveQueryResultWithSingleResultCollection.md b/docs/framework/angular/reference/interfaces/InjectLiveQueryResultWithSingleResultCollection.md index 2cf88f2856..40bd02aa6e 100644 --- a/docs/framework/angular/reference/interfaces/InjectLiveQueryResultWithSingleResultCollection.md +++ b/docs/framework/angular/reference/interfaces/InjectLiveQueryResultWithSingleResultCollection.md @@ -3,9 +3,13 @@ id: InjectLiveQueryResultWithSingleResultCollection title: InjectLiveQueryResultWithSingleResultCollection --- -# Interface: InjectLiveQueryResultWithSingleResultCollection\ +Defined in: [angular-db/src/index.ts:102](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L102) -Defined in: [index.ts:73](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L73) +Status, persistence, and health signals every `injectLiveQuery` result has. + +## Extends + +- [`InjectLiveQueryFlags`](InjectLiveQueryFlags.md) ## Type Parameters @@ -32,7 +36,7 @@ collection: Signal< | null>; ``` -Defined in: [index.ts:80](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L80) +Defined in: [angular-db/src/index.ts:109](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L109) *** @@ -42,7 +46,23 @@ Defined in: [index.ts:80](https://github.com/TanStack/db/blob/main/packages/angu data: Signal; ``` -Defined in: [index.ts:79](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L79) +Defined in: [angular-db/src/index.ts:108](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L108) + +*** + +### health + +```ts +health: Signal; +``` + +Defined in: [angular-db/src/index.ts:62](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L62) + +A signal containing the health tree for the query and its dependencies + +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`health`](InjectLiveQueryFlags.md#health) *** @@ -52,17 +72,29 @@ Defined in: [index.ts:79](https://github.com/TanStack/db/blob/main/packages/angu isCleanedUp: Signal; ``` -Defined in: [index.ts:86](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L86) +Defined in: [angular-db/src/index.ts:58](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L58) + +A signal indicating whether the collection has been cleaned up + +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`isCleanedUp`](InjectLiveQueryFlags.md#iscleanedup) *** -### isError +### isCrashed ```ts -isError: Signal; +isCrashed: Signal; ``` -Defined in: [index.ts:85](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L85) +Defined in: [angular-db/src/index.ts:56](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L56) + +Local storage or an invariant broke. Only a cascade cleanup and a fresh start recover. + +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`isCrashed`](InjectLiveQueryFlags.md#iscrashed) *** @@ -72,7 +104,13 @@ Defined in: [index.ts:85](https://github.com/TanStack/db/blob/main/packages/angu isIdle: Signal; ``` -Defined in: [index.ts:84](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L84) +Defined in: [angular-db/src/index.ts:52](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L52) + +A signal indicating whether the collection is idle + +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`isIdle`](InjectLiveQueryFlags.md#isidle) *** @@ -82,7 +120,29 @@ Defined in: [index.ts:84](https://github.com/TanStack/db/blob/main/packages/angu isLoading: Signal; ``` -Defined in: [index.ts:82](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L82) +Defined in: [angular-db/src/index.ts:48](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L48) + +A signal indicating whether the collection is currently loading + +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`isLoading`](InjectLiveQueryFlags.md#isloading) + +*** + +### isLoadingError + +```ts +isLoadingError: Signal; +``` + +Defined in: [angular-db/src/index.ts:54](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L54) + +The first load failed: there is nothing to show. + +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`isLoadingError`](InjectLiveQueryFlags.md#isloadingerror) *** @@ -92,7 +152,29 @@ Defined in: [index.ts:82](https://github.com/TanStack/db/blob/main/packages/angu isReady: Signal; ``` -Defined in: [index.ts:83](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L83) +Defined in: [angular-db/src/index.ts:50](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L50) + +A signal indicating whether the collection is ready + +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`isReady`](InjectLiveQueryFlags.md#isready) + +*** + +### isSyncError + +```ts +isSyncError: Signal; +``` + +Defined in: [angular-db/src/index.ts:60](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L60) + +The data is shown, and a sync problem or failed request is in `health`. + +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`isSyncError`](InjectLiveQueryFlags.md#issyncerror) *** @@ -102,7 +184,7 @@ Defined in: [index.ts:83](https://github.com/TanStack/db/blob/main/packages/angu state: Signal>; ``` -Defined in: [index.ts:78](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L78) +Defined in: [angular-db/src/index.ts:107](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L107) *** @@ -112,4 +194,10 @@ Defined in: [index.ts:78](https://github.com/TanStack/db/blob/main/packages/angu status: Signal; ``` -Defined in: [index.ts:81](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L81) +Defined in: [angular-db/src/index.ts:46](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L46) + +A signal containing the current status of the collection + +#### Inherited from + +[`InjectLiveQueryFlags`](InjectLiveQueryFlags.md).[`status`](InjectLiveQueryFlags.md#status) diff --git a/docs/framework/angular/reference/type-aliases/InjectConditionalLiveQueryResult.md b/docs/framework/angular/reference/type-aliases/InjectConditionalLiveQueryResult.md new file mode 100644 index 0000000000..eeaf1a3929 --- /dev/null +++ b/docs/framework/angular/reference/type-aliases/InjectConditionalLiveQueryResult.md @@ -0,0 +1,24 @@ +--- +id: InjectConditionalLiveQueryResult +title: InjectConditionalLiveQueryResult +--- + +```ts +type InjectConditionalLiveQueryResult = Omit, "data"> & object; +``` + +Defined in: [angular-db/src/index.ts:85](https://github.com/TanStack/db/blob/main/packages/angular-db/src/index.ts#L85) + +## Type Declaration + +### data + +```ts +data: Signal>; +``` + +## Type Parameters + +### TContext + +`TContext` *extends* `Context` diff --git a/docs/framework/preact/overview.md b/docs/framework/preact/overview.md new file mode 100644 index 0000000000..ff3963438b --- /dev/null +++ b/docs/framework/preact/overview.md @@ -0,0 +1,168 @@ +--- +title: TanStack DB Preact Adapter +id: adapter +--- + +## Installation + +```sh +npm install @tanstack/preact-db +``` + +`@tanstack/preact-db` supports Preact 10 and later and ships as ES modules. + +Queries read concrete source Collections. Preact has no `DbProvider` yet, so it does not bind collection descriptors or read from a `DbClient`; a query over a descriptor throws when it is consumed. Use `@tanstack/react-db` or `@tanstack/svelte-db` for `DbClient`-based SSR and hydration. + +## Preact Hooks + +See the [Preact Functions Reference](./reference/index.md) to see the full list of hooks available in the Preact Adapter. + +For comprehensive documentation on writing queries (filtering, joins, aggregations, etc.), see the [Live Queries Guide](../../guides/live-queries). + +## Basic Usage + +### useLiveQuery + +The `useLiveQuery` hook creates a live query that automatically updates your component when data changes: + +```tsx +import { createCollection, eq, localOnlyCollectionOptions } from '@tanstack/db' +import { useLiveQuery } from '@tanstack/preact-db' + +type Todo = { + id: string + text: string + completed: boolean +} + +const todoCollection = createCollection( + localOnlyCollectionOptions({ + getKey: (todo) => todo.id, + initialData: [], + }), +) + +function TodoList() { + const { data, isLoading } = useLiveQuery((q) => + q + .from({ todos: todoCollection }) + .where(({ todos }) => eq(todos.completed, false)) + .select(({ todos }) => ({ id: todos.id, text: todos.text })), + ) + + if (isLoading) return
Loading...
+ + return ( +
    + {data.map((todo) => ( +
  • {todo.text}
  • + ))} +
+ ) +} +``` + +`useLiveQuery` accepts a query callback, a `{ query }` config object, or an existing live-query Collection. Return `undefined` or `null` from the callback to disable the query; a disabled query reports `status: 'disabled'` and has no `data` or `state`: + +```tsx +function UserTodos({ userId }: { userId?: string }) { + const { data, isEnabled } = useLiveQuery((q) => + userId + ? q + .from({ todos: todoCollection }) + .where(({ todos }) => eq(todos.userId, userId)) + : undefined, + ) + + if (!isEnabled) return
Select a user
+ return
{data?.length} todos
+} +``` + +### Query Identity + +Preact live query hooks derive the live query identity from structured query IR. The hook runs the query builder on each render, normalizes the resulting IR, and uses that as the identity. When the derived identity changes, the old live query collection is cleaned up and a new one is created. + +That means normal structured queries need no `queryKey` and no dependency array. Captured values inside structured expressions become part of the derived identity: + +```tsx +function FilteredTodos({ minPriority }: { minPriority: number }) { + const { data } = useLiveQuery((q) => + q + .from({ todos: todoCollection }) + .where(({ todos }) => gt(todos.priority, minPriority)), + ) + + return
{data.length} high-priority todos
+} +``` + +While a hook is mounted, each source Collection ID its queries read must keep referring to the same Collection object. If a render supplies a different source object with a previously used ID, the hook throws `SourceCollectionIdReuseError` instead of returning rows from the old source. To reuse an ID, unmount the hook, clean up its source, then mount the replacement. Separate hooks can use the same ID. + +#### When to Use Query Keys + +Use `queryKey` when DB cannot derive identity from structured IR. The common case is a functional query variant such as `.fn.where`, `.fn.select`, or `.fn.having`. Without a `queryKey`, such a query throws `UnhashableQueryIRError`: + +```tsx +function SearchTodos({ search }: { search: string }) { + const { data } = useLiveQuery({ + queryKey: [todoCollection.id, 'search', search], + query: (q) => + q + .from({ todos: todoCollection }) + .fn.where(({ todos }) => + todos.text.toLowerCase().includes(search.toLowerCase()), + ), + }) + + return
{data.length} matching todos
+} +``` + +An explicit `queryKey` is the caller's identity contract. Represent every value the opaque logic captures in the key, and change the key when its source object changes, even if the source ID stays the same. Use a primitive scope token in the key; Collection objects cannot be hashed there. + +#### What Happens When Identity Changes + +When the derived identity or explicit query key changes: + +1. The previous live query collection is cleaned up +2. A new query is created with the updated values +3. The component re-renders with the new data + +### useLiveInfiniteQuery + +For paginated data with live updates, use `useLiveInfiniteQuery`: + +```tsx +const { data, pages, fetchNextPage, hasNextPage } = useLiveInfiniteQuery( + (q) => + q + .from({ posts: postsCollection }) + .where(({ posts }) => eq(posts.category, category)) + .orderBy(({ posts }) => posts.createdAt, 'desc'), + { + pageSize: 20, + }, +) +``` + +The query must include `.orderBy()`. The hook keeps one live query collection and widens its window as pages load, so every loaded page stays live. Its identity follows `useLiveQuery`: pass `queryKey` in the config for a functional query variant. + +`fetchNextPage()` returns a promise that resolves after the page request settles. A failed page load does not reject the promise or change `status`; it appears as a request error in the returned `health` tree. + +This hook widens an ordered live query; it is not TanStack Query's `useInfiniteQuery`. `getNextPageParam` is rejected. `initialPageParam` only labels the returned pages; it does not set a server cursor or skip remote rows. For server pagination, use an on-demand Query Collection whose `queryFn` fulfills `meta.loadSubsetOptions`. See the [server pagination guide](../../collections/query-collection.md#server-pagination-with-live-queries). + +### Using Pre-created Collections + +Pass an existing live query collection to share one query across components. The hook starts its sync and leaves its retention policy to the caller: + +```tsx +const activeTodos = createLiveQueryCollection((q) => + q.from({ todos: todoCollection }).where(({ todos }) => eq(todos.active, true)), +) + +function ActiveTodos() { + const { data, collection } = useLiveQuery(activeTodos) + return
{data.length} active todos
+} +``` diff --git a/docs/framework/preact/reference/functions/useLiveInfiniteQuery.md b/docs/framework/preact/reference/functions/useLiveInfiniteQuery.md new file mode 100644 index 0000000000..82cbf0adee --- /dev/null +++ b/docs/framework/preact/reference/functions/useLiveInfiniteQuery.md @@ -0,0 +1,106 @@ +--- +id: useLiveInfiniteQuery +title: useLiveInfiniteQuery +--- + +## Call Signature + +```ts +function useLiveInfiniteQuery(liveQueryCollection, config): UseLiveInfiniteQueryReturnWithCollection; +``` + +Defined in: [useLiveInfiniteQuery.ts:85](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveInfiniteQuery.ts#L85) + +Page through an ordered live query, growing one live-query Collection's +window with `utils.setWindow()` instead of recreating it per page. + +The query must include `.orderBy()`. Its identity follows `useLiveQuery`: +derived from the query IR, or named by `queryKey`. A query whose IR cannot be +hashed throws `UnhashableQueryIRError` unless `queryKey` is supplied. + +### Type Parameters + +#### TResult + +`TResult` *extends* `object` + +#### TKey + +`TKey` *extends* `string` \| `number` + +#### TUtils + +`TUtils` *extends* `Record`\<`string`, `any`\> + +### Parameters + +#### liveQueryCollection + +`Collection`\<`TResult`, `TKey`, `TUtils`, `StandardSchemaV1`\<`unknown`, `unknown`\>, `TResult`\> & `NonSingleResult` + +#### config + +[`UseLiveInfiniteQueryConfig`](../type-aliases/UseLiveInfiniteQueryConfig.md)\<`any`\> + +### Returns + +[`UseLiveInfiniteQueryReturnWithCollection`](../type-aliases/UseLiveInfiniteQueryReturnWithCollection.md)\<`TResult`, `TKey`, `TUtils`\> + +### Example + +```ts +const { pages, fetchNextPage, hasNextPage } = useLiveInfiniteQuery( + (q) => + q + .from({ posts: postsCollection }) + .orderBy(({ posts }) => posts.createdAt, `desc`), + { pageSize: 20 }, +) +``` + +## Call Signature + +```ts +function useLiveInfiniteQuery(queryFn, config): UseLiveInfiniteQueryReturn; +``` + +Defined in: [useLiveInfiniteQuery.ts:94](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveInfiniteQuery.ts#L94) + +Page through an ordered live query, growing one live-query Collection's +window with `utils.setWindow()` instead of recreating it per page. + +The query must include `.orderBy()`. Its identity follows `useLiveQuery`: +derived from the query IR, or named by `queryKey`. A query whose IR cannot be +hashed throws `UnhashableQueryIRError` unless `queryKey` is supplied. + +### Type Parameters + +#### TContext + +`TContext` *extends* `Context` + +### Parameters + +#### queryFn + +(`q`) => `QueryBuilder`\<`TContext`\> + +#### config + +[`UseLiveInfiniteQueryConfig`](../type-aliases/UseLiveInfiniteQueryConfig.md)\<`TContext`\> + +### Returns + +[`UseLiveInfiniteQueryReturn`](../type-aliases/UseLiveInfiniteQueryReturn.md)\<`TContext`\> + +### Example + +```ts +const { pages, fetchNextPage, hasNextPage } = useLiveInfiniteQuery( + (q) => + q + .from({ posts: postsCollection }) + .orderBy(({ posts }) => posts.createdAt, `desc`), + { pageSize: 20 }, +) +``` diff --git a/docs/framework/preact/reference/functions/useLiveQuery.md b/docs/framework/preact/reference/functions/useLiveQuery.md new file mode 100644 index 0000000000..4040c8b2fe --- /dev/null +++ b/docs/framework/preact/reference/functions/useLiveQuery.md @@ -0,0 +1,435 @@ +--- +id: useLiveQuery +title: useLiveQuery +--- + +## Call Signature + +```ts +function useLiveQuery(queryFn): QueryResult; +``` + +Defined in: [useLiveQuery.ts:154](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L154) + +Subscribe a Preact component to a live query. + +The hook derives the query's identity from its structured IR on each render, +so values captured by the callback recompile the query when they change. A +query whose IR cannot be hashed, such as one using `fn.where`, throws +`UnhashableQueryIRError` unless its config supplies `queryKey`. While the +hook is mounted, each Collection ID its queries read must keep naming one +source object, or it throws `SourceCollectionIdReuseError`. +Return `undefined` or `null` from the callback to disable the query. + +### Type Parameters + +#### TContext + +`TContext` *extends* `Context` + +### Parameters + +#### queryFn + +(`q`) => `QueryBuilder`\<`TContext`\> + +### Returns + +`QueryResult`\<`TContext`\> + +### Examples + +```ts +const { data, isLoading } = useLiveQuery((q) => + q + .from({ todos: todosCollection }) + .where(({ todos }) => eq(todos.completed, false)), +) +``` + +```ts +// Disable the query until its input exists +const { data, isEnabled } = useLiveQuery((q) => + userId + ? q + .from({ todos: todosCollection }) + .where(({ todos }) => eq(todos.userId, userId)) + : undefined, +) +``` + +## Call Signature + +```ts +function useLiveQuery(queryFn): ConditionalQueryResult; +``` + +Defined in: [useLiveQuery.ts:158](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L158) + +Subscribe a Preact component to a live query. + +The hook derives the query's identity from its structured IR on each render, +so values captured by the callback recompile the query when they change. A +query whose IR cannot be hashed, such as one using `fn.where`, throws +`UnhashableQueryIRError` unless its config supplies `queryKey`. While the +hook is mounted, each Collection ID its queries read must keep naming one +source object, or it throws `SourceCollectionIdReuseError`. +Return `undefined` or `null` from the callback to disable the query. + +### Type Parameters + +#### TContext + +`TContext` *extends* `Context` + +### Parameters + +#### queryFn + +(`q`) => `QueryBuilder`\<`TContext`\> \| `null` \| `undefined` + +### Returns + +`ConditionalQueryResult`\<`TContext`\> + +### Examples + +```ts +const { data, isLoading } = useLiveQuery((q) => + q + .from({ todos: todosCollection }) + .where(({ todos }) => eq(todos.completed, false)), +) +``` + +```ts +// Disable the query until its input exists +const { data, isEnabled } = useLiveQuery((q) => + userId + ? q + .from({ todos: todosCollection }) + .where(({ todos }) => eq(todos.userId, userId)) + : undefined, +) +``` + +## Call Signature + +```ts +function useLiveQuery(queryFn): ConditionalQueryResult; +``` + +Defined in: [useLiveQuery.ts:164](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L164) + +Subscribe a Preact component to a live query. + +The hook derives the query's identity from its structured IR on each render, +so values captured by the callback recompile the query when they change. A +query whose IR cannot be hashed, such as one using `fn.where`, throws +`UnhashableQueryIRError` unless its config supplies `queryKey`. While the +hook is mounted, each Collection ID its queries read must keep naming one +source object, or it throws `SourceCollectionIdReuseError`. +Return `undefined` or `null` from the callback to disable the query. + +### Type Parameters + +#### TContext + +`TContext` *extends* `Context` + +### Parameters + +#### queryFn + +(`q`) => + \| `LiveQueryCollectionConfig`\<`TContext`, `RootQueryResult`\<`TContext`\>\> + \| `null` + \| `undefined` + +### Returns + +`ConditionalQueryResult`\<`TContext`\> + +### Examples + +```ts +const { data, isLoading } = useLiveQuery((q) => + q + .from({ todos: todosCollection }) + .where(({ todos }) => eq(todos.completed, false)), +) +``` + +```ts +// Disable the query until its input exists +const { data, isEnabled } = useLiveQuery((q) => + userId + ? q + .from({ todos: todosCollection }) + .where(({ todos }) => eq(todos.userId, userId)) + : undefined, +) +``` + +## Call Signature + +```ts +function useLiveQuery(queryFn): UseConditionalLiveQueryResult, TResult>>; +``` + +Defined in: [useLiveQuery.ts:170](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L170) + +Subscribe a Preact component to a live query. + +The hook derives the query's identity from its structured IR on each render, +so values captured by the callback recompile the query when they change. A +query whose IR cannot be hashed, such as one using `fn.where`, throws +`UnhashableQueryIRError` unless its config supplies `queryKey`. While the +hook is mounted, each Collection ID its queries read must keep naming one +source object, or it throws `SourceCollectionIdReuseError`. +Return `undefined` or `null` from the callback to disable the query. + +### Type Parameters + +#### TResult + +`TResult` *extends* `object` + +#### TKey + +`TKey` *extends* `string` \| `number` + +#### TUtils + +`TUtils` *extends* `Record`\<`string`, `any`\> + +### Parameters + +#### queryFn + +(`q`) => + \| `Collection`\<`TResult`, `TKey`, `TUtils`, `StandardSchemaV1`\<`unknown`, `unknown`\>, `TResult`\> + \| `null` + \| `undefined` + +### Returns + +[`UseConditionalLiveQueryResult`](../interfaces/UseConditionalLiveQueryResult.md)\<`TResult`, `TKey`, `TResult`[], `Collection`\<`TResult`, `TKey`, `TUtils`, `StandardSchemaV1`\<`unknown`, `unknown`\>, `TResult`\>\> + +### Examples + +```ts +const { data, isLoading } = useLiveQuery((q) => + q + .from({ todos: todosCollection }) + .where(({ todos }) => eq(todos.completed, false)), +) +``` + +```ts +// Disable the query until its input exists +const { data, isEnabled } = useLiveQuery((q) => + userId + ? q + .from({ todos: todosCollection }) + .where(({ todos }) => eq(todos.userId, userId)) + : undefined, +) +``` + +## Call Signature + +```ts +function useLiveQuery(config): QueryResult; +``` + +Defined in: [useLiveQuery.ts:194](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L194) + +Subscribe to a live query described by a config object. + +### Type Parameters + +#### TContext + +`TContext` *extends* `Context` + +### Parameters + +#### config + +[`UseLiveQueryConfig`](../type-aliases/UseLiveQueryConfig.md)\<`TContext`\> + +### Returns + +`QueryResult`\<`TContext`\> + +### Example + +```ts +const { data } = useLiveQuery({ + query: (q) => q.from({ todos: todosCollection }), + gcTime: 60_000, +}) +``` + +## Call Signature + +```ts +function useLiveQuery(config): ConditionalQueryResult; +``` + +Defined in: [useLiveQuery.ts:198](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L198) + +Subscribe a Preact component to a live query. + +The hook derives the query's identity from its structured IR on each render, +so values captured by the callback recompile the query when they change. A +query whose IR cannot be hashed, such as one using `fn.where`, throws +`UnhashableQueryIRError` unless its config supplies `queryKey`. While the +hook is mounted, each Collection ID its queries read must keep naming one +source object, or it throws `SourceCollectionIdReuseError`. +Return `undefined` or `null` from the callback to disable the query. + +### Type Parameters + +#### TContext + +`TContext` *extends* `Context` + +### Parameters + +#### config + +[`ConditionalUseLiveQueryConfig`](../type-aliases/ConditionalUseLiveQueryConfig.md)\<`TContext`\> + +### Returns + +`ConditionalQueryResult`\<`TContext`\> + +### Examples + +```ts +const { data, isLoading } = useLiveQuery((q) => + q + .from({ todos: todosCollection }) + .where(({ todos }) => eq(todos.completed, false)), +) +``` + +```ts +// Disable the query until its input exists +const { data, isEnabled } = useLiveQuery((q) => + userId + ? q + .from({ todos: todosCollection }) + .where(({ todos }) => eq(todos.userId, userId)) + : undefined, +) +``` + +## Call Signature + +```ts +function useLiveQuery(liveQueryCollection): UseLiveQueryResult, TResult>>; +``` + +Defined in: [useLiveQuery.ts:212](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L212) + +Subscribe to an existing live-query Collection. The hook starts its sync and +leaves its retention policy to the caller. + +### Type Parameters + +#### TResult + +`TResult` *extends* `object` + +#### TKey + +`TKey` *extends* `string` \| `number` + +#### TUtils + +`TUtils` *extends* `Record`\<`string`, `any`\> + +### Parameters + +#### liveQueryCollection + +`Collection`\<`TResult`, `TKey`, `TUtils`, `StandardSchemaV1`\<`unknown`, `unknown`\>, `TResult`\> & `NonSingleResult` + +### Returns + +[`UseLiveQueryResult`](../interfaces/UseLiveQueryResult.md)\<`TResult`, `TKey`, `TResult`[], `Collection`\<`TResult`, `TKey`, `TUtils`, `StandardSchemaV1`\<`unknown`, `unknown`\>, `TResult`\>\> + +### Example + +```ts +const activeTodos = createLiveQueryCollection((q) => + q.from({ todos: todosCollection }).where(({ todos }) => todos.active), +) +const { data, collection } = useLiveQuery(activeTodos) +``` + +## Call Signature + +```ts +function useLiveQuery(liveQueryCollection): UseLiveQueryResult, TResult> & SingleResult>; +``` + +Defined in: [useLiveQuery.ts:225](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L225) + +Subscribe a Preact component to a live query. + +The hook derives the query's identity from its structured IR on each render, +so values captured by the callback recompile the query when they change. A +query whose IR cannot be hashed, such as one using `fn.where`, throws +`UnhashableQueryIRError` unless its config supplies `queryKey`. While the +hook is mounted, each Collection ID its queries read must keep naming one +source object, or it throws `SourceCollectionIdReuseError`. +Return `undefined` or `null` from the callback to disable the query. + +### Type Parameters + +#### TResult + +`TResult` *extends* `object` + +#### TKey + +`TKey` *extends* `string` \| `number` + +#### TUtils + +`TUtils` *extends* `Record`\<`string`, `any`\> + +### Parameters + +#### liveQueryCollection + +`Collection`\<`TResult`, `TKey`, `TUtils`, `StandardSchemaV1`\<`unknown`, `unknown`\>, `TResult`\> & `SingleResult` + +### Returns + +[`UseLiveQueryResult`](../interfaces/UseLiveQueryResult.md)\<`TResult`, `TKey`, `TResult` \| `undefined`, `Collection`\<`TResult`, `TKey`, `TUtils`, `StandardSchemaV1`\<`unknown`, `unknown`\>, `TResult`\> & `SingleResult`\> + +### Examples + +```ts +const { data, isLoading } = useLiveQuery((q) => + q + .from({ todos: todosCollection }) + .where(({ todos }) => eq(todos.completed, false)), +) +``` + +```ts +// Disable the query until its input exists +const { data, isEnabled } = useLiveQuery((q) => + userId + ? q + .from({ todos: todosCollection }) + .where(({ todos }) => eq(todos.userId, userId)) + : undefined, +) +``` diff --git a/docs/framework/preact/reference/index.md b/docs/framework/preact/reference/index.md new file mode 100644 index 0000000000..6c7bc6c787 --- /dev/null +++ b/docs/framework/preact/reference/index.md @@ -0,0 +1,23 @@ +--- +id: "@tanstack/preact-db" +title: "@tanstack/preact-db" +--- + +## Interfaces + +- [UseConditionalLiveQueryResult](interfaces/UseConditionalLiveQueryResult.md) +- [UseLiveQueryResult](interfaces/UseLiveQueryResult.md) + +## Type Aliases + +- [ConditionalUseLiveQueryConfig](type-aliases/ConditionalUseLiveQueryConfig.md) +- [UseLiveInfiniteQueryConfig](type-aliases/UseLiveInfiniteQueryConfig.md) +- [UseLiveInfiniteQueryReturn](type-aliases/UseLiveInfiniteQueryReturn.md) +- [UseLiveInfiniteQueryReturnWithCollection](type-aliases/UseLiveInfiniteQueryReturnWithCollection.md) +- [UseLiveQueryConfig](type-aliases/UseLiveQueryConfig.md) +- [UseLiveQueryStatus](type-aliases/UseLiveQueryStatus.md) + +## Functions + +- [useLiveInfiniteQuery](functions/useLiveInfiniteQuery.md) +- [useLiveQuery](functions/useLiveQuery.md) diff --git a/docs/framework/preact/reference/interfaces/UseConditionalLiveQueryResult.md b/docs/framework/preact/reference/interfaces/UseConditionalLiveQueryResult.md new file mode 100644 index 0000000000..4dd659d970 --- /dev/null +++ b/docs/framework/preact/reference/interfaces/UseConditionalLiveQueryResult.md @@ -0,0 +1,117 @@ +--- +id: UseConditionalLiveQueryResult +title: UseConditionalLiveQueryResult +--- + +Defined in: [useLiveQuery.ts:98](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L98) + +The result of a live query whose callback can disable it. + +## Extends + +- `LiveQueryResultFlags` + +## Type Parameters + +### TResult + +`TResult` *extends* `object` + +### TKey + +`TKey` *extends* `string` \| `number` + +### TData + +`TData` + +### TCollection + +`TCollection` + +## Properties + +### collection + +```ts +collection: TCollection | undefined; +``` + +Defined in: [useLiveQuery.ts:106](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L106) + +*** + +### data + +```ts +data: TData | undefined; +``` + +Defined in: [useLiveQuery.ts:105](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L105) + +*** + +### health + +```ts +health: HealthNode | undefined; +``` + +Defined in: [useLiveQuery.ts:80](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L80) + +The health tree for the query and every Collection it depends on, or +`undefined` for a disabled query. + +#### Inherited from + +```ts +LiveQueryResultFlags.health +``` + +*** + +### isEnabled + +```ts +isEnabled: boolean; +``` + +Defined in: [useLiveQuery.ts:108](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L108) + +*** + +### isSyncError + +```ts +isSyncError: boolean; +``` + +Defined in: [useLiveQuery.ts:75](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L75) + +The data is shown, and a sync problem or failed request is in `health`. + +#### Inherited from + +```ts +LiveQueryResultFlags.isSyncError +``` + +*** + +### state + +```ts +state: ReadonlyMap | undefined; +``` + +Defined in: [useLiveQuery.ts:104](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L104) + +*** + +### status + +```ts +status: UseLiveQueryStatus; +``` + +Defined in: [useLiveQuery.ts:107](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L107) diff --git a/docs/framework/preact/reference/interfaces/UseLiveQueryResult.md b/docs/framework/preact/reference/interfaces/UseLiveQueryResult.md new file mode 100644 index 0000000000..b46858c7b1 --- /dev/null +++ b/docs/framework/preact/reference/interfaces/UseLiveQueryResult.md @@ -0,0 +1,117 @@ +--- +id: UseLiveQueryResult +title: UseLiveQueryResult +--- + +Defined in: [useLiveQuery.ts:84](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L84) + +The result of a live query that is always enabled. + +## Extends + +- `LiveQueryResultFlags` + +## Type Parameters + +### TResult + +`TResult` *extends* `object` + +### TKey + +`TKey` *extends* `string` \| `number` + +### TData + +`TData` + +### TCollection + +`TCollection` + +## Properties + +### collection + +```ts +collection: TCollection; +``` + +Defined in: [useLiveQuery.ts:92](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L92) + +*** + +### data + +```ts +data: TData; +``` + +Defined in: [useLiveQuery.ts:91](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L91) + +*** + +### health + +```ts +health: HealthNode | undefined; +``` + +Defined in: [useLiveQuery.ts:80](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L80) + +The health tree for the query and every Collection it depends on, or +`undefined` for a disabled query. + +#### Inherited from + +```ts +LiveQueryResultFlags.health +``` + +*** + +### isEnabled + +```ts +isEnabled: true; +``` + +Defined in: [useLiveQuery.ts:94](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L94) + +*** + +### isSyncError + +```ts +isSyncError: boolean; +``` + +Defined in: [useLiveQuery.ts:75](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L75) + +The data is shown, and a sync problem or failed request is in `health`. + +#### Inherited from + +```ts +LiveQueryResultFlags.isSyncError +``` + +*** + +### state + +```ts +state: ReadonlyMap; +``` + +Defined in: [useLiveQuery.ts:90](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L90) + +*** + +### status + +```ts +status: CollectionStatus; +``` + +Defined in: [useLiveQuery.ts:93](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L93) diff --git a/docs/framework/preact/reference/type-aliases/ConditionalUseLiveQueryConfig.md b/docs/framework/preact/reference/type-aliases/ConditionalUseLiveQueryConfig.md new file mode 100644 index 0000000000..3385f9eb3b --- /dev/null +++ b/docs/framework/preact/reference/type-aliases/ConditionalUseLiveQueryConfig.md @@ -0,0 +1,26 @@ +--- +id: ConditionalUseLiveQueryConfig +title: ConditionalUseLiveQueryConfig +--- + +```ts +type ConditionalUseLiveQueryConfig = UseLiveQueryConfigOptions & object; +``` + +Defined in: [useLiveQuery.ts:63](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L63) + +## Type Declaration + +### query + +```ts +query: + | ConfiguredQueryBuilder + | (q) => ConfiguredQueryBuilder | undefined | null; +``` + +## Type Parameters + +### TContext + +`TContext` *extends* `Context` diff --git a/docs/framework/preact/reference/type-aliases/UseLiveInfiniteQueryConfig.md b/docs/framework/preact/reference/type-aliases/UseLiveInfiniteQueryConfig.md new file mode 100644 index 0000000000..da63625e89 --- /dev/null +++ b/docs/framework/preact/reference/type-aliases/UseLiveInfiniteQueryConfig.md @@ -0,0 +1,51 @@ +--- +id: UseLiveInfiniteQueryConfig +title: UseLiveInfiniteQueryConfig +--- + +```ts +type UseLiveInfiniteQueryConfig<_TContext> = object; +``` + +Defined in: [useLiveInfiniteQuery.ts:27](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveInfiniteQuery.ts#L27) + +## Type Parameters + +### _TContext + +`_TContext` *extends* `Context` + +## Properties + +### initialPageParam? + +```ts +optional initialPageParam: number; +``` + +Defined in: [useLiveInfiniteQuery.ts:35](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveInfiniteQuery.ts#L35) + +First result-page label, not a server cursor or remote offset. + +*** + +### pageSize? + +```ts +optional pageSize: number; +``` + +Defined in: [useLiveInfiniteQuery.ts:33](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveInfiniteQuery.ts#L33) + +*** + +### queryKey? + +```ts +optional queryKey: LiveQueryKey; +``` + +Defined in: [useLiveInfiniteQuery.ts:32](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveInfiniteQuery.ts#L32) + +Explicit identity for a query whose IR cannot derive one, such as a query +with functional variants. Structured queries should omit it. diff --git a/docs/framework/preact/reference/type-aliases/UseLiveInfiniteQueryReturn.md b/docs/framework/preact/reference/type-aliases/UseLiveInfiniteQueryReturn.md new file mode 100644 index 0000000000..1c0883646e --- /dev/null +++ b/docs/framework/preact/reference/type-aliases/UseLiveInfiniteQueryReturn.md @@ -0,0 +1,17 @@ +--- +id: UseLiveInfiniteQueryReturn +title: UseLiveInfiniteQueryReturn +--- + +```ts +type UseLiveInfiniteQueryReturn = UseLiveQueryResult[number], string | number, InferResultType, Collection[number], string | number, { +}>> & InfiniteQueryPagination[number]>; +``` + +Defined in: [useLiveInfiniteQuery.ts:46](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveInfiniteQuery.ts#L46) + +## Type Parameters + +### TContext + +`TContext` *extends* `Context` diff --git a/docs/framework/preact/reference/type-aliases/UseLiveInfiniteQueryReturnWithCollection.md b/docs/framework/preact/reference/type-aliases/UseLiveInfiniteQueryReturnWithCollection.md new file mode 100644 index 0000000000..f54fb42677 --- /dev/null +++ b/docs/framework/preact/reference/type-aliases/UseLiveInfiniteQueryReturnWithCollection.md @@ -0,0 +1,24 @@ +--- +id: UseLiveInfiniteQueryReturnWithCollection +title: UseLiveInfiniteQueryReturnWithCollection +--- + +```ts +type UseLiveInfiniteQueryReturnWithCollection = UseLiveQueryResult & NonSingleResult> & InfiniteQueryPagination; +``` + +Defined in: [useLiveInfiniteQuery.ts:55](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveInfiniteQuery.ts#L55) + +## Type Parameters + +### TResult + +`TResult` *extends* `object` + +### TKey + +`TKey` *extends* `string` \| `number` + +### TUtils + +`TUtils` *extends* `Record`\<`string`, `any`\> diff --git a/docs/framework/preact/reference/type-aliases/UseLiveQueryConfig.md b/docs/framework/preact/reference/type-aliases/UseLiveQueryConfig.md new file mode 100644 index 0000000000..3834faa19f --- /dev/null +++ b/docs/framework/preact/reference/type-aliases/UseLiveQueryConfig.md @@ -0,0 +1,16 @@ +--- +id: UseLiveQueryConfig +title: UseLiveQueryConfig +--- + +```ts +type UseLiveQueryConfig = UseLiveQueryConfigOptions & Pick, "query">; +``` + +Defined in: [useLiveQuery.ts:59](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L59) + +## Type Parameters + +### TContext + +`TContext` *extends* `Context` diff --git a/docs/framework/preact/reference/type-aliases/UseLiveQueryStatus.md b/docs/framework/preact/reference/type-aliases/UseLiveQueryStatus.md new file mode 100644 index 0000000000..e86fd36b2c --- /dev/null +++ b/docs/framework/preact/reference/type-aliases/UseLiveQueryStatus.md @@ -0,0 +1,10 @@ +--- +id: UseLiveQueryStatus +title: UseLiveQueryStatus +--- + +```ts +type UseLiveQueryStatus = CollectionStatus | "disabled"; +``` + +Defined in: [useLiveQuery.ts:36](https://github.com/TanStack/db/blob/main/packages/preact-db/src/useLiveQuery.ts#L36) diff --git a/docs/framework/react/overview.md b/docs/framework/react/overview.md index 1c10d644c6..868f2765bb 100644 --- a/docs/framework/react/overview.md +++ b/docs/framework/react/overview.md @@ -17,19 +17,34 @@ For comprehensive documentation on writing queries (filtering, joins, aggregatio ## Basic Usage +Create a `DbClient` and provide it to your React tree: + +```tsx +import { DbClient, DbProvider } from '@tanstack/react-db' + +const dbClient = new DbClient() + +root.render( + + + +) +``` + ### useLiveQuery The `useLiveQuery` hook creates a live query that automatically updates your component when data changes: ```tsx -import { useLiveQuery, eq } from '@tanstack/react-db' +import { and, eq, gt, useDbClient, useLiveQuery } from '@tanstack/react-db' function TodoList() { - const { data, isLoading } = useLiveQuery((q) => - q.from({ todos: todosCollection }) - .where(({ todos }) => eq(todos.completed, false)) - .select(({ todos }) => ({ id: todos.id, text: todos.text })) - ) + const { data, isLoading } = useLiveQuery({ + query: (q) => + q.from({ todos: todoCollection }) + .where(({ todos }) => eq(todos.completed, false)) + .select(({ todos }) => ({ id: todos.id, text: todos.text })), + }) if (isLoading) return
Loading...
@@ -41,29 +56,73 @@ function TodoList() { } ``` -### Dependency Arrays - -All query hooks (`useLiveQuery`, `useLiveInfiniteQuery`, `useLiveSuspenseQuery`) accept an optional dependency array as their last parameter. This array works similarly to React's `useEffect` dependencies - when any value in the array changes, the query is recreated and re-executed. +### Query Identity -#### When to Use Dependency Arrays +React live query hooks derive the live query identity from structured query IR by default. The hook runs the query builder, normalizes the resulting IR, and uses that as the identity. When the derived identity changes, the old live query collection is cleaned up and a new one is created. -Use dependency arrays when your query depends on external reactive values (props, state, or other hooks): +That means normal structured queries do not need a separate `queryKey`. Collection descriptors provide stable collection IDs, and captured values inside structured expressions become part of the derived identity: ```tsx function FilteredTodos({ minPriority }: { minPriority: number }) { - const { data } = useLiveQuery( - (q) => q.from({ todos: todosCollection }) + const { data } = useLiveQuery({ + query: (q) => q.from({ todos: todoCollection }) .where(({ todos }) => gt(todos.priority, minPriority)), - [minPriority] // Re-run when minPriority changes - ) + }) return
{data.length} high-priority todos
} ``` -#### What Happens When Dependencies Change +For queries without `queryKey` in one mounted hook and `DbClient` scope, a +source Collection ID must keep referring to the same Collection object. If a +render supplies a different source object with a previously used ID, the hook +throws `SourceCollectionIdReuseError` instead of returning rows from the old +source. To reuse an ID within that hook, +unmount it, clean up its source and client scope, then mount the replacement. +Separate hooks and independent client scopes can use the same ID. + +#### Collection Hooks + +`useLiveQuery` resolves collection descriptors from `DbProvider` automatically. Create small collection hooks when components need imperative collection methods like `insert`, `update`, `delete`, or `preload`: + +```tsx +function useTodoCollection() { + return useDbClient().collection(todoCollection) +} +``` + +#### When to Use Query Keys -When a dependency value changes: +Use `queryKey` only when DB cannot derive identity from structured IR, or when you intentionally want to avoid deriving identity on a hot render path. The common case is a functional query variant such as `.fn.where`, `.fn.select`, or `.fn.having`: + +```tsx +function SearchTodos({ search }: { search: string }) { + const { data } = useLiveQuery({ + queryKey: [todoCollection.id, 'search', search], + query: (q) => q.from({ todos: todoCollection }) + .fn.where(({ todos }) => + todos.text.toLowerCase().includes(search.toLowerCase()) + ), + }) + + return
{data.length} matching todos
+} +``` + +An explicit `queryKey` is the caller's identity contract. If its source object +changes, change the key too, even when the source ID stays the same. Use a +primitive scope token in the key; Collection objects cannot be hashed there. + +An unhashable query without `queryKey` throws `UnhashableQueryIRError`, so +represent the values that opaque logic captures in the key. An empty +`queryKey` counts as none, as it does for a `DbClient` preload. A dependency +array does not replace `queryKey` for such a query. If deriving identity becomes +expensive across renders, the hook warns once and suggests adding a `queryKey` +as a performance escape hatch. + +#### What Happens When Identity Changes + +When the derived identity or explicit query key changes: 1. The previous live query collection is cleaned up 2. A new query is created with the updated values 3. The component re-renders with the new data @@ -71,46 +130,39 @@ When a dependency value changes: #### Best Practices -**Include all external values used in the query:** +**Use structured expressions when possible:** ```tsx -// Good - all external values in deps -const { data } = useLiveQuery( - (q) => q.from({ todos: todosCollection }) +// Good - DB can derive identity from this structured IR +const { data } = useLiveQuery({ + query: (q) => q.from({ todos: todoCollection }) .where(({ todos }) => and( eq(todos.userId, userId), eq(todos.status, status) )), - [userId, status] -) - -// Bad - missing dependencies -const { data } = useLiveQuery( - (q) => q.from({ todos: todosCollection }) - .where(({ todos }) => eq(todos.userId, userId)), - [] // Missing userId! -) +}) ``` -**Empty array for static queries:** +**Add a query key for opaque runtime logic:** ```tsx -// No external dependencies - query never changes -const { data } = useLiveQuery( - (q) => q.from({ todos: todosCollection }), - [] -) +const { data } = useLiveQuery({ + queryKey: [todoCollection.id, 'by-user-fn', userId], + query: (q) => q.from({ todos: todoCollection }) + .fn.where(({ todos }) => todos.userId === userId), +}) ``` -**Omit the array for queries with no external dependencies:** +**Omit query keys for static structured queries:** ```tsx -// Same as above - no deps needed -const { data } = useLiveQuery( - (q) => q.from({ todos: todosCollection }) -) +const { data } = useLiveQuery({ + query: (q) => q.from({ todos: todoCollection }), +}) ``` +For SSR setup, collection hydration, and migration details, see the [SSR and Hydration guide](../../guides/ssr.md). + ### useLiveInfiniteQuery For paginated data with live updates, use `useLiveInfiniteQuery`: @@ -123,14 +175,20 @@ const { data, pages, fetchNextPage, hasNextPage } = useLiveInfiniteQuery( .orderBy(({ posts }) => posts.createdAt, 'desc'), { pageSize: 20, - getNextPageParam: (lastPage, allPages) => - lastPage.length === 20 ? allPages.length : undefined - }, - [category] // Re-run when category changes + } ) ``` -**Note:** The dependency array is only available when using the query function variant, not when passing a pre-created collection. +`fetchNextPage()` returns a promise that resolves after the page request settles. A failed page load does not reject the promise or change `status`; it appears as a request error in the returned `health` tree. + +This hook widens an ordered live query; it is not TanStack Query's +`useInfiniteQuery`. `getNextPageParam` is not supported. +`initialPageParam` only labels the returned pages; it does not set a server cursor +or skip remote rows. For server pagination, use an on-demand Query Collection +whose `queryFn` fulfills `meta.loadSubsetOptions`. See the +[server pagination guide](../../collections/query-collection.md#server-pagination-with-live-queries). + +React hook dependency arrays were removed in the first 1.0 RC. Use derived query identity or an explicit `queryKey`. ### useLiveSuspenseQuery @@ -138,11 +196,10 @@ For React Suspense integration, use `useLiveSuspenseQuery`: ```tsx function TodoList({ filter }: { filter: string }) { - const { data } = useLiveSuspenseQuery( - (q) => q.from({ todos: todosCollection }) + const { data } = useLiveSuspenseQuery({ + query: (q) => q.from({ todos: todoCollection }) .where(({ todos }) => eq(todos.filter, filter)), - [filter] // Re-suspends when filter changes - ) + }) return (