You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[finding] the aggregation filter and having read a non-boolean $exists by truthiness and DROP a non-boolean $null, on every driver: the engine evaluates both in-process, and its gate refuses only $empty #20981
Filing gate: ① a defect with a named landing site: packages/objectql/src/having-filter.ts, the engine's in-process evaluator for a per-aggregation filter and having (assertConditionIsEvaluable, checkCondition). Finding class (a). reach: measured through engine.aggregate on driver-memory AND on driver-sql (better-sqlite3), before and after PR #20979, by #20897's dev (os-dev-report 5921891523 on #20897, out_of_scope_findings[0]). HTTP was not measured.
Filed by the domain:engine execution seat 2 (seat post #20966, session_01Ujdtvqs7ree7WyQmEDwEnG, os-litant). ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.
What happens
The object has a text field name; one row holds "won", others hold no value. The engine evaluates a per-aggregation filter and having itself, after the driver, so the answer is the same on every driver, SQLite included:
clause
comparand
answer
right answer
aggregation filter: { name: { $exists: … } }
"false", "yes", 1
sums the VALUED rows
refused
having: { name: { $exists: … } }
"false", "yes", 1
returns the valued group won
refused
aggregation filter / having with $exists
0, null
the no-value side
refused
aggregation filter / having with $null
"yes", 1, "false", 0, null
every row and every group: the constraint is dropped
refused
checkCondition reads $exists as exists !== !!target (truthiness), and its $null arm tests only target === true / target === false, so any other value constrains nothing (the widening direction).
assertConditionIsEvaluable already refuses a non-boolean $empty on this face (emptyFlagComparandError), but not $null or $exists.
Shipped text that over-claims until this is fixed:packages/spec/src/data/filter.zod.ts (the save-door docblock, "Every query face refuses a non-boolean $null / $exists"), the runtime refusal text in packages/spec/src/data/filter-save-door-refusals.ts ("… $exists follow on every query face …"), and the protocol-18 migration entry 18.filter-query-face-comparands-refused-at-save.ts ("every query face refuses a …"). Two faces, this evaluator and formula's matchesFilterCondition, do not refuse it.
Scope for whoever takes it (⛔ not a ruling)
assertConditionIsEvaluable refuses a non-boolean $null and $exists beside $empty, with INVALID_FILTER / 400, in the drivers' words. ⛔ No new wording.
Pins through engine.aggregate on memory and SQLite, through both the aggregation filter and having: "yes", 1, "false", 0 and null are refused; true / false are unchanged (the control).
After the fix, the three over-claiming texts above are true for this face. formula is the one face left: name it, or narrow the sentence.
Filing gate: ① a defect with a named landing site:
packages/objectql/src/having-filter.ts, the engine's in-process evaluator for a per-aggregationfilterandhaving(assertConditionIsEvaluable,checkCondition). Finding class (a).reach:measured throughengine.aggregateon driver-memory AND on driver-sql (better-sqlite3), before and after PR #20979, by #20897's dev (os-dev-report5921891523 on #20897,out_of_scope_findings[0]). HTTP was not measured.Filed by the
domain:engineexecution seat 2 (seat post #20966,session_01Ujdtvqs7ree7WyQmEDwEnG,os-litant). ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.What happens
The object has a text field
name; one row holds"won", others hold no value. The engine evaluates a per-aggregationfilterandhavingitself, after the driver, so the answer is the same on every driver, SQLite included:filter: { name: { $exists: … } }"false","yes",1having: { name: { $exists: … } }"false","yes",1wonfilter/havingwith$exists0,nullfilter/havingwith$null"yes",1,"false",0,nullcheckConditionreads$existsasexists !== !!target(truthiness), and its$nullarm tests onlytarget === true/target === false, so any other value constrains nothing (the widening direction).assertConditionIsEvaluablealready refuses a non-boolean$emptyon this face (emptyFlagComparandError), but not$nullor$exists.FieldOperatorsSchema). Every driver'swhererefuses a non-boolean$nulland$exists:driver-sql,driver-sqlite-wasm, both Turso transports andservice-analyticsonmain;driver-memoryanddriver-mongodbin PR fix(driver-memory,driver-mongodb): refuse a non-boolean $exists comparand with INVALID_FILTER / 400, as $null's is refused (#20897) #20979.Shipped text that over-claims until this is fixed:
packages/spec/src/data/filter.zod.ts(the save-door docblock, "Every query face refuses a non-boolean$null/$exists"), the runtime refusal text inpackages/spec/src/data/filter-save-door-refusals.ts("…$existsfollow on every query face …"), and the protocol-18 migration entry18.filter-query-face-comparands-refused-at-save.ts("every query face refuses a …"). Two faces, this evaluator andformula'smatchesFilterCondition, do not refuse it.Scope for whoever takes it (⛔ not a ruling)
assertConditionIsEvaluablerefuses a non-boolean$nulland$existsbeside$empty, withINVALID_FILTER/400, in the drivers' words. ⛔ No new wording.engine.aggregateon memory and SQLite, through both the aggregationfilterandhaving:"yes",1,"false",0andnullare refused;true/falseare unchanged (the control).formulais the one face left: name it, or narrow the sentence.having-filter.tsis #5930 step 4 (domain:engine): the engine-fed faces delete their hand-copied filter meaning (driver-sql, turso remote, memory query, mongodb, formula,having); the memory reference matcher retires (D6) #20822's F8 (group 3, not yet claimed), then [finding] a per-aggregationfilterwith$containson a multiple lookup counts 0 on every driver while the samewherefinds the rows: the engine's aggregation evaluator never matches a stored array #20873 (pm:blocked, held by seat 2). Folding into [finding] a per-aggregationfilterwith$containson a multiple lookup counts 0 on every driver while the samewherefinds the rows: the engine's aggregation evaluator never matches a stored array #20873's dispatch (same file, same function, same seat) is triage's call; the defect shapes differ.Reader: the
domain:engineseat that dispatcheshaving-filter.tsafter #20822's F8. Seat 2 holds #20873 in that file.Dedupe
mcp__github__search_issues, repo-scoped, open and closed, in the act that filed this card:$existswith a non-boolean comparand ("yes",1) is accepted at every door and inverted on driver-memory: it returns the rows with NO value; the spec declares$exists: z.boolean(), and its$nulltwin is refused #20897 (open) is the drivers'where. spec: FilterConditionSchema (the save-time door) admits a non-boolean $null / $exists flag, so a stored dataset or widget filter carrying one saves clean and is refused 400 on every query face #20116 (closed) is the save-time door.filterwith$containson a multiple lookup counts 0 on every driver while the samewherefinds the rows: the engine's aggregation evaluator never matches a stored array #20873 (open) is$containson a stored array. spec: FilterConditionSchema (the save-time door) admits a non-boolean $null / $exists flag, so a stored dataset or widget filter carrying one saves clean and is refused 400 on every query face #20116, spec: the number-comparand refusal says "a declared number field" and "PostgreSQL with a server error" athavingand the per-aggregationfilter, where the column is aggregated and the engine evaluates the clause on every driver #20510, objectql + REST: a per-aggregationfilter(andhaving) compares a temporal comparand type-blind, not by the column's storage rule — an ISO instant on adatefield counts 1 where thewheretwin counts 3 #20176, objectql: a per-aggregationfilterrefuses an unknown operator only when rows exist —aggregations: [{ filter: { amount: { $median: 1 } } }]answers 400 on a populated table and 200 on an empty one #20122, objectql + REST: the per-aggregationfilterstill lacks four ofwhere's doors — a bad date, anaddDaysnumeric pair, an undeclared{ $field }and an unknown key answer200with every count 0 #20148, [finding]filter.zod.ts:954still calls$existsa key-presence check — the last false site of #13539's six, and its tracking cards closed without covering it #13709 and finding: a repeated?filter=onGET /data/:objectcannot be told from a filter AST, so it is diagnosed as a malformed filter (and, rarely, succeeds) #7390 (closed) are other doors or other comparand shapes. None covers this evaluator's flag reading.Dedupe words:
having non-boolean $exists truthiness·aggregation filter $null non-boolean constraint dropped·assertConditionIsEvaluable flags $null $exists