Repository navigation
Keep an AND group's estimate finite when a sibling condition is filter-only - #3724
Merged
Merged
Conversation
This was referenced Oct 28, 2026
kriszyp
marked this pull request as ready for review
October 30, 2026 12:57
kriszyp
force-pushed
the
fix/estimate-condition-filter-only-sibling
branch
from
October 30, 2026 17:49
3b46197 to
b822e44
Compare
…stimate into Infinity estimateCondition multiplied an AND group's running estimate by every sub-estimate, so one filter-only leg (Infinity) made the whole group Infinity (and 0 x Infinity NaN). orderConditions sorts a nested group's children finite-first before the parent estimates it, which is the order that poisons the group. SQL `!=` / NOT IN lower to such a group, so after a filter-only predicate (IS NULL, LIKE '%x%', LIKE '%x') they failed 403 under allowFullScan: false, nested FIQL AND groups did the same, and count: 'estimated' reported null. The intersection now skips non-finite sub-estimates; a group stays Infinity only when every leg is. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Dispatch-Task: harper-estimate-and-infinite-sibling (cherry picked from commit f8634f2)
…r gaps Pin that the SQL cases run under allowFullScan: false, bound the REST estimated total, drop comments that restate test names, and shrink the DESIGN note to the invariant plus the known gaps (`in` / empty starts_with estimate a finite cardinality yet full-scan; intersectionEstimate). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Dispatch-Task: harper-estimate-and-infinite-sibling (cherry picked from commit eb1690c)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Dispatch-Task: harper-estimate-and-infinite-sibling (cherry picked from commit 4931740)
… the test Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Dispatch-Task: harper-estimate-and-infinite-sibling (cherry picked from commit e864fea)
…scan `in`, an empty `starts_with` and a range on an unindexed attribute estimate a finite count but searchByIndex answers them with a full scan, so they could lead an AND ahead of an indexed sibling (403/404 under allowFullScan: false, a full scan otherwise). `conditionDrivesIndex` mirrors searchByIndex's full-scan decisions and is the first sort key in orderConditions; estimates are unchanged, so count: 'estimated' and the lazy index switch read the same numbers. Leading with the primary-key range exposed a nested relationship selection that read `filtered[attribute]` once at preparation; a relationship condition that filters registers it on its first match, so the selection now reads it per record. Fixes #3725 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-3724-prfix-fold-3725 (cherry picked from commit 9ef7c46)
…index scan searchByIndex resolves a `from` relationship on the local primary key with a primary-store lookup, so conditionDrivesIndex must not require a secondary index for it. Also trims comments that restated the sort keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-3724-prfix-fold-3725 (cherry picked from commit 1df7720)
searchByIndex full-scans the synthetic `sort` condition, so conditionDrivesIndex now reports it as non-driving. orderConditions keeps main's rule for it: it leads only when its estimate is below every other condition's. Ranking it above non-driving legs dropped rows missing the sort attribute; ranking it below every driving leg would lose index-order streaming of a sorted page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-3724-prfix-fold-3725 (cherry picked from commit 3d9ed2d)
Only the first AND condition executes as an index scan; the rest are record filters that short-circuit in order. Instead of ranking every condition by whether it can drive, orderConditions keeps the estimate order and moves the first condition that can drive to the front, unless the synthetic sort already sorts first. Plans match main whenever main's lead could already drive, and filters keep their estimate order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-3724-prfix-fold-3725 (cherry picked from commit d3264d9)
…d and ready searchByIndex rejects a full-text comparator whose @fulltext index is undeclared (400) or not ready (IndexRebuildingError), so the lead must not go to it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-3724-prfix-fold-3725 (cherry picked from commit acbd60c)
…efixes out of the lead A one-element `['id']` path seeks the primary store but estimated Infinity, so leading with it handed its sibling filters an Infinity incoming count and the lazy index switch fired on their first miss. A `starts_with` with a null or undefined value cannot drive a scan. Also trims comments. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-3724-prfix-fold-3725 (cherry picked from commit b1cdfad)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-3724-prfix-fold-3725 (cherry picked from commit 0e74d46)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-3724-prfix-fold-3725 (cherry picked from commit cff9c78)
A relationship condition inside an OR group decides which rows match. It no longer registers a select restriction (first branch won), no longer pushes its related-level filter into a leading join on the same relationship, and an OR branch's own leading join stays out of the AND scope's `filtered` map. The related records a row selects now depend only on the AND scope's conditions, not on which condition or branch runs first. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7groWP5o828Tt7QJHAnB2 Dispatch-Task: pr-maint-6969638615aeffe18e5ba9386e33648b
…d attribute as the primary key Promoting a relationship condition to the lead changes which related record its sibling conditions on that relationship must meet, so a row matched through different related records disappeared. A relationship path no longer counts as able to drive, which leaves its place in the estimate order as on main. A finite group can now estimate below an implicit primary-key condition's 1 and lead ahead of it, which demoted that condition to a filter reading `record[undefined]`. `filterByType` now reads an omitted attribute as the primary key, as `searchByIndex` does. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7groWP5o828Tt7QJHAnB2 Dispatch-Task: pr-maint-6969638615aeffe18e5ba9386e33648b
…ep a user sort-aligned lead Excluding relationship paths from the lead promotion also let it displace a relationship seek the estimate order had already put first. Promotion now skips a query whose lead is a relationship condition (or a group holding one). A user condition on the sort attribute that leads keeps its place when full scans are allowed, as the synthetic sort does, so it still streams a sorted page. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7groWP5o828Tt7QJHAnB2 Dispatch-Task: pr-maint-6969638615aeffe18e5ba9386e33648b
…, and keep a sort-aligned lead unless full scans are forbidden A group holding a relationship only as a later filter no longer blocks promotion, which turned an indexed lead into a 403. A user condition on the sort attribute keeps the lead whenever allowFullScan is not exactly false, as REST leaves it unset. An empty attribute path is not the primary key. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7groWP5o828Tt7QJHAnB2 Dispatch-Task: pr-maint-6969638615aeffe18e5ba9386e33648b
…leton null path as the primary key An OR group's branches never share a relationship join with their siblings, so only an AND's executing lead decides whether promotion leaves the lead alone. A one-element null attribute path now resolves to the primary key as an omitted attribute does. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7groWP5o828Tt7QJHAnB2 Dispatch-Task: pr-maint-6969638615aeffe18e5ba9386e33648b
… an AND group by its executing lead The relationship-lead exemption kept a lead whose scan was unusable under allowFullScan false (a 404 where main found the row), and an AND group counted as driving when any leg did, so a group led by a relationship join was promoted ahead of a local index. Both now follow the group's first ordered condition. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7groWP5o828Tt7QJHAnB2 Dispatch-Task: pr-maint-6969638615aeffe18e5ba9386e33648b
…stating comments A two-hop path was always classified as unable to run, so a broader local index displaced a relationship lead and admitted a parent whose employees met the two conditions separately. Each hop's local join index and the leaf's scan are now checked. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7groWP5o828Tt7QJHAnB2 Dispatch-Task: pr-maint-6969638615aeffe18e5ba9386e33648b
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7groWP5o828Tt7QJHAnB2 Dispatch-Task: pr-maint-6969638615aeffe18e5ba9386e33648b
… the DESIGN.md budget Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7groWP5o828Tt7QJHAnB2 Dispatch-Task: pr-maint-6969638615aeffe18e5ba9386e33648b
…ted record whichever leads The first non-leading condition on a to-many relationship registers the whole set of sibling filters and the rest only add to it, as a leading join already takes them, so rows no longer depend on the lead. An AND inside an OR gets its own scope; a condition in an OR still decides rows only. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7groWP5o828Tt7QJHAnB2
…a runnable relationship scan drive Sibling filters of a relationship now live in a per-scope registry under a symbol key, so a relationship named like the maps that track relationships, and a to-one hop before a to-many one, meet one related record too. conditionDrivesIndex asks relationshipScanRuns for a related path, which replaces the lead-retention special case in orderConditions and lets a runnable relationship scan pass an `in` that would full-scan. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7groWP5o828Tt7QJHAnB2
…y be named like the maps that track them relationshipRestriction now reads a symbol-keyed registry instead of string-keyed own properties of the join and selection maps, so a relationship named filters, get or fromRecord no longer collides with their members, and a promoted two-hop lead no longer pushes a sibling filter into a Map. The promotion loop and the relationship scan check no longer allocate, and a later sibling of a shared relationship does not build an id set. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7groWP5o828Tt7QJHAnB2
… two planning callbacks Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7groWP5o828Tt7QJHAnB2
…drives Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7groWP5o828Tt7QJHAnB2
…s keep their id shortcut Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7groWP5o828Tt7QJHAnB2
…e the relationship A key condition among several conditions on one to-many relationship now rejects a record before any related record loads, and the whole set still decides for the rest. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7groWP5o828Tt7QJHAnB2
…me related record Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7groWP5o828Tt7QJHAnB2
…s a relationship Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7groWP5o828Tt7QJHAnB2
…ed-filter fixtures Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7groWP5o828Tt7QJHAnB2
kriszyp
force-pushed
the
fix/estimate-condition-filter-only-sibling
branch
from
October 31, 2026 01:21
b822e44 to
ac43c5a
Compare
…elationship Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7groWP5o828Tt7QJHAnB2
kriszyp
added a commit
that referenced
this pull request
Nov 2, 2026
kriszyp
added a commit
that referenced
this pull request
Nov 2, 2026
kriszyp
added a commit
that referenced
this pull request
Nov 2, 2026
…/DESIGN.md from 339 KB to 44 KB of invariant notes (#3856) * Load a resources/ concept map with the first file read, and trim resources/DESIGN.md to its invariants resources/DESIGN.md had grown to 339 KB, mostly symbol inventory and incident narrative. It is now 40 KB of invariant notes, one per original heading: the rule, why it holds, the enforcing symbol and the pinning test. Navigation moves to resources/MAP.md, which resources/CLAUDE.md imports so Claude Code loads it on the first read in the directory; AGENTS.md lists the map for agents that do not load subdirectory CLAUDE.md files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-resources-design-map-trim * Restore the negative and cross-symbol rules review found missing from resources/DESIGN.md Corrects the commit-clock statement (the per-attempt clock drives load shedding), the equal-version walk rule, the full-PUT fallback on an invalidated head, the sharding switch prerequisites and LMDB write takeover; restores dropped rules such as primitive-get exact-class matching, X-Replicate-From staying open, copy bounds before rows and the registration-boundary exceptions. Folds four one-line notes into their neighbours and shortens headings only the root index links. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-resources-design-map-trim * Restore the remaining failure-guarding rules review round 2 named in resources/DESIGN.md Single-record resumes check only the generation, listeners never wait on the source commit, a read verdict names its gate, readableInFull proofs stay within one permission source, received-key dedup skips unnamed ids, and the struct-mode note keeps its structon companion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-resources-design-map-trim * Qualify the cached-read options rule in resources/DESIGN.md Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-resources-design-map-trim * List the write.skipped outcomes and the attribute-slot acknowledgement in resources/DESIGN.md Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-resources-design-map-trim * Restore the replica-walk, branch-ref and single-record-resume details review round 4 named Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-resources-design-map-trim * Say the replica audit walk continues after commit Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-resources-design-map-trim * Compact #3724's estimator and relationship notes and lower #3857's ceilings Dispatch-Task: pr-maint-4ea3ff57cdacd1640c5b5f6e1f084d80 Co-Authored-By: Gemini <noreply@google.com> * Restore gap qualifications and map attributions from review Dispatch-Task: pr-maint-4ea3ff57cdacd1640c5b5f6e1f084d80 Co-Authored-By: Gemini <noreply@google.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Gemini <noreply@google.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
⊙ Problem
Choosing the leading AND condition failed three ways:
IS NULL,LIKE '%x%') estimatesInfinity, which made SQL!=/NOT INand nested FIQL groups fail with 403 underallowFullScan: false.in, an emptystarts_withand an unindexed range scan every row, yet could lead before an indexed condition.Changing the lead also changed which related record two conditions on one relationship had to meet, so a finite estimate could widen a collection
DELETE.🤔 Is the requirement right?
Yes: both rules are the owner's decisions.
💡 Solution
orderConditionsleads with the narrowest condition that can drive an index scan.⚖️ Alternatives
Rejected approaches
Infinityestimates for full-scan legs —count: 'estimated'and the lazy index switch read them as counts.main— the framing review found the finite estimate could widen a collectionDELETE.🤔 Same-record matching changes a result
mainpinnedFix wrong rows, unfiltered selects and ignored sorts in relationship queries pinned that a non-leading AND matches a parent whose children meet the conditions separately. That test now expects no match, as a leading join gave.
🤔 An unindexed foreign key never drives
Its scan reads the local table once per related record, which the estimate does not price, so an indexed condition leads even under
allowFullScan: true.🔧 Changes
resources/search.ts:conditionDrivesIndexfollowssearchByIndex's full-scan and missing-index decisions, andrelationshipScanRunsextends it hop by hop. Look hardest here: it must stay in step with the executor, and a test checks them leaf by leaf.executeConditionsandmapConditionsToFilterstake ascopewheremainthreadedinUnion;filterByTyperegisters sibling filters per scope under a symbol key, and the first condition checks the whole set per related record. A to-one relationship shares only its inner scope.relationshipRestriction, keyed by symbol, so a relationship may be namedfiltersorget.['id']path estimates 1.resources/Table.ts: the lead promotion, which keeps a syntheticsort(or a user condition on the sort attribute unlessallowFullScanisfalse) in front; a nested select'sconditionsfollow the same rule.resources/DESIGN.md: the estimator, lead, scope and OR rules.🤔 Async relationship selection still allocates per row
Selecting a to-many relationship whose related records load asynchronously builds a match arrow and a
matchingIdsclosure for each parent row, the same asmain. Hoisting the arrow is a separate cleanup.✅ Verification
relationshipConditionScope.test.js(split-child parents in every order, reserved names, two hops, nested selects, collectionDELETE, OR branches),estimateFilterOnlySibling.test.js(reducer, SQL mutations, lead selection) and key-only rejection.DELETEon split-product brands and the SQL conjunction cases withsql.allowFullScan: false.filtersthroughfiltered.childrennow go throughrelationshipRestriction(async reads, key invalidation and the nested-selection cancellation).test:unit:typestrip:resources:coreand the two touched integration files pass.Fixes #3725
Closes #3739
Closes #3813
🤖 Generated by Claude Opus 5.5 via Claude Code; posted via @kriszyp.
Release target: v5.4 — fixes a regression from #3601, which is milestoned v5.4
Related PRs: #3601 overlaps (fixes a regression it introduced); #3776 overlaps (merged first); #3775 overlaps (merged first); #3772 overlaps (search.ts and Table.ts); 6 others independent
Complexity: high
Framing-Verdict: chosen-approach-sound
Deferred findings
db316ad3c3eb6566major performance —resources/search.ts— Allowed full scans lose an executable relationship lead with an unindexed foreign key — NOT FILED6ce214f314a60636major correctness —resources/Table.ts— A non-driving sort-aligned lead suppresses sorting or rejects a null-equality query — NOT FILEDb1c07dbbe9115cd7minor performance —resources/search.ts— conditionDrivesIndex recomputed recursively per ancestor, O(depth^2) — NOT FILEDa4bee76312e168cfminor performance —resources/Table.ts— a narrower non-driving condition displaces the synthetic sort, and promotion then skips the sort and promotes another indexed condition — NOT FILED563aa49357eaca44minor correctness —resources/search.ts— starts_with null/undefined value: TypeError in searchByIndex on lead or lazy index switch; filter path matches 'null' prefix — NOT FILEDde7af54d4747481cminor performance —resources/search.ts— async to-many selection allocates a match closure and a matchingIds closure per parent row — NOT FILEDe937c699d7582749nit test-coverage —unitTests/resources/estimateFilterOnlySibling.test.js— broad-in plus sort case asserts only the plan, not the rows — NOT FILED052305fc5f7fa204nit reliability —resources/search.ts— Sibling predicates on a leading join rely on lazy iteration of the lead — NOT FILEDc98b870701ea83f7nit performance —resources/search.ts— intersectionEstimate multiplies Infinity into the incoming-row hint — filed intersectionEstimate multiplies Infinity into the incoming-row hint used by filter index switching #3726🤔 defer this PR's major performance finding
db316ad3c3eb6566(Allowed full scans lose an executable relationship lead with an unindexed foreign key) to a separate issue instead of fixing it here?Origin — the dispatch brief this PR was written from
In estimateCondition (resources/search.ts ~2214-2222), the AND branch multiplies the running estimate by each sub-condition's estimated_count / N without checking that it is finite. Filter-only legs get estimated_count = Infinity (~2283), so any filter-only sibling (IS NULL, LIKE '%x%', LIKE '%x', NOT (...)) turns a finite index-backed estimate into Infinity. Since harper#3601 lowers SQL != and NOT IN into compound AND groups, SQL SELECT/UPDATE/DELETE with != or NOT IN after a filter-only predicate, and nested FIQL AND groups, now fail with 403 under allowFullScan: false, and count: 'estimated' returns null. Skip non-finite sub-estimates in the AND intersection; if every leg is Infinity the group stays Infinity.
Acceptance
Unit test on rocksdb and lmdb covering SQL SELECT/UPDATE/DELETE with != and NOT IN after IS NULL / LIKE '%x%' / LIKE '%x' / NOT(...), a nested FIQL AND group under allowFullScan:false (200, not 403), Table.search({count:'estimated'}) non-null, and orderConditions picking the indexed leg. Show each new test failing on origin/main sources (forced rebuild) before the fix.
Dispatch: task
harper-estimate-and-infinite-sibling· queued by finding-triage/wave-42 · ran by claude/sonnet/xhigh · worker kzyp-xps-1Late Grok findings
2 surviving (highest major) — Grok reviewed 4931740, ruled against e864fea
resources/search.ts:2218— An AND group that previously estimatedInfinitynow takes a finite estimate from a leg that cannot drive a scan: an unindexed or emptystarts_with, an unindexed range, or an indexedin. The parent AND then sorts that group ahead of an indexed sibling. Example: indexedc = common(50 of 100 rresources/search.ts:2216— Open comment finding about the new line that says whatInfinitymeans in the reducer. It is the only comment the diff adds, and it states a convention thatestimateConditionForTableshares across functions, which the code cannot express on its own; author's callNot standing: 1 merged. Grok ran 15m (16m from launch to report); the fold adjudication (
cursor-grok@4931740106a6-37ccf37f, claude) took 1m.Late Grok findings
4 surviving (highest minor) — Grok reviewed b1cdfad, ruled against 0e74d46
resources/search.ts:1056— astarts_withcondition whose value isnullorundefinedthrows aTypeErrorfromvalue.toString()oncesearchByIndexruns it, either as the lead or through the lazy index switch atresources/search.ts:2092; when it only filters, it matches values beginning with the text "null"/"undefinresources/search.ts:71— when OR branches constrain the same to-many relationship, selection projects related records from the first branch's slot only, so parents matched by a later branch come back withchildren: []resources/search.ts:2221—conditionDrivesIndexre-walks descendant groups at every ancestor, so planning is O(depth²) in nesting depthunitTests/resources/estimateFilterOnlySibling.test.js:317— comment cleanup is mostly done; the column-narration comment is still thereNot standing: 2 accepted-known. Grok ran 12m (13m from launch to report); the fold adjudication (
cursor-grok@b1cdfad2fa38-48603d22, claude) took 1m.Late Grok findings
2 surviving (highest major) — Grok reviewed 6aa3504, ruled against a36d1b7
resources/search.ts:2300— Composite (array) primary keys: once an AND group with an infinite-estimate leg gets a finite estimate, the group can lead ahead ofid = [...](estimate 1,search.ts:2329). The id condition then becomes a post-filter. Equality compares with===against a freshly decoded array (search.ts:1938resources/search.ts:1900— Selection on an async to-many relationship still allocates a new(value) => value != null && match(value)arrow per parent row, plus thematchingIdsclosure at:1901. It could be hoisted once per condition. No measurement covers it, so the hot-path floor applies.Not standing: 1 resolved. Grok ran 18m (18m from launch to report); the fold adjudication (
cursor-grok@6aa3504fda13-83a12601, claude) took 1m.Late Grok findings
1 surviving (highest major) — Grok reviewed cfda0d1
resources/search.ts:1880— An AND group of two conditions on the same to-many relationship, placed inside an OR, now admits a parent whose related rows meet the conditions separately. When the label condition leads, its join is written to a fresh map (resources/search.ts:161), and the!inUniongate keeps the tier conditioNot standing: none. Grok ran 13m (14m from launch to report); the fold adjudication (
cursor-grok@cfda0d1eedfc-775b5c75, claude) took 1m.Late Grok findings
5 surviving (highest major) — Grok reviewed 1f74746
resources/search.ts:1885— When two conditions on the same to-many relationship are ANDed inside an OR, they no longer have to be met by the same related record. Example:(children.label=sw=match & children.tier=gold) | id=nobodyreturnsparent-3, because one of its children matches the label and a different child is goldresources/Table.ts:7333— The exemption that keeps a sort-aligned condition first only covers the syntheticsortpseudo-condition. A user's own condition on the sort attribute (e.g.priority=in=(1)withsort(priority)) cannot drive an index, so:7334promotes a broader indexed condition ahead of it. Then:7427setsresources/search.ts:2356— A multi-hop relationship path always returns false fromconditionDrivesIndex, so when the estimate already sorted a relationship join first,findIndexatresources/Table.ts:7334promotes any broader local driving condition ahead of it. Example:group='g1'(100 rows) displaceskids.name='a'resources/search.ts:2356— A condition with attribute[]unwraps toundefined, which classifies it as driving the primary store, so it can be promoted to lead. Code-trace only: I did not confirm that searchByIndex then full-scans; reachability depends on a caller sending an empty path.resources/search.ts:160— New comments atresources/search.ts:160,resources/search.ts:1884andunitTests/resources/relationshipAsyncReads.test.js:418repeat whatresources/DESIGN.md:276and the test title at:402already say.Not standing: none. Grok ran 15m (16m from launch to report); the fold adjudication (
cursor-grok@1f74746b6a26-f10ad633, claude) took 1m.Late Grok findings
3 surviving (highest major) — Grok reviewed eb07c09
resources/search.ts:1989— With two AND conditions on the same to-one relationship, the id-set shortcut is turned off, so every candidate row loads its related recordresources/search.ts:1016— Added comments describe the code next to themresources/Table.ts:7389— The sort-streaming exemption (syntheticOrderCondition/orderAlignedCondition) is not mentioned inresources/DESIGN.md. Grep finds both names only inTable.tsNot standing: 1 merged. Grok ran 16m (16m from launch to report); the fold adjudication (
cursor-grok@eb07c0931360-5eadf0c6, claude) took 1m.Late Grok findings
1 surviving (highest minor) — Grok reviewed 7005532
resources/Table.ts:7777— In a nestedselect, two AND conditions that go through the same to-many relationship can still be satisfied by different related records. Top-level queries now require both conditions to match the same related record, so the two paths disagreeNot standing: 1 merged. Grok ran 18m (18m from launch to report); the fold adjudication (
cursor-grok@700553240f78-8afde2c0, claude) took 1m.Review-Coverage: authored=claude; ran=gemini,cursor-composer,codex,cursor-grok; pending=cursor-grok; late=cursor-grok:pending; adjudicated=domain,domain(codex); blocked=claude(fallback)(out-of-budget); declined=muse; rounds=32; full=10 @ 90ebd4e
Review-Attention: deep ~45m (critical: Table.ts, search.ts; decisions: same-record-and-semantics, or-condition-decides-rows-only, promote-driving-lead-unconditionally, and-estimate-ignores-filter-only, pr-scope, do-less-alternative, deferred-db316ad3) @ 90ebd4e