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.
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.
The implementation assigns a binding ID when it creates each CollectionRef or QueryRef. Callback-created PropRefs 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. |
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.
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.