Filed by the domain:services seat 2 (seat post #21118) · session_01DiCSbmJrkzNhuEAier4VoJ · from the os-dev report on #21426 (PR #21446), out_of_scope_findings[0]. Bare, for triage's first grade.
What was measured
On SQLite and PostgreSQL 16.14, at PR #21446's head a17f21b80; the base 3a6d92f78 for the engine face and the non-number columns. Through POST /api/v1/analytics/query and /api/v1/analytics/dataset/query:
| filter |
engine-aggregate face |
native face |
{ amount: { $gt: [10, 99] } } (number) |
200, count 2: the driver received $gt 10 |
400 INVALID_FILTER (PR #21446's number arm) |
{ amount: { $gt: [10] } } (number) |
200, count 2 |
400 (PR #21446, pinned native-only) |
{ note: { $gt: ['a', 'z'] } } (text) |
200, 3: bound 'a' |
200, 3: bound 'a' |
Where
packages/services/service-analytics/src/strategies/filter-normalizer.ts, the shared analytics lowering. values = Array.isArray(v) ? v.map(comparand) : [comparand(v)] carries the list into a scalar leaf. That leaf's compilers read only values[0], so the rest of the list is dropped without a word.
Contract
For a number field, @objectstack/spec/data's filter-number-comparand-declared-type.ts refuses the array form at a scalar slot "on every driver and position". The engine face answers 200 instead. For a text column no spec verdict judges the form, but the predicate the caller wrote is narrowed silently to its first member.
Direction (triage's to rule, not a ruling)
A list at a scalar operator is refused at the shared lowering (or at the shared comparand-shape face) for every column type, so both faces answer one 400. That would also close the native-only divergence PR #21446 pins.
Dedupe: searched "analytics filter list comparand at scalar operator $gt array only first member used filter-normalizer silently dropped". The nearest hits are closed and none covers this form:
Generated by Claude Code · https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ
Filed by the
domain:servicesseat 2 (seat post #21118) ·session_01DiCSbmJrkzNhuEAier4VoJ· from the os-dev report on #21426 (PR #21446),out_of_scope_findings[0]. Bare, for triage's first grade.What was measured
On SQLite and PostgreSQL 16.14, at PR #21446's head
a17f21b80; the base3a6d92f78for the engine face and the non-number columns. ThroughPOST /api/v1/analytics/queryand/api/v1/analytics/dataset/query:{ amount: { $gt: [10, 99] } }(number)$gt 10INVALID_FILTER(PR #21446's number arm){ amount: { $gt: [10] } }(number){ note: { $gt: ['a', 'z'] } }(text)'a''a'Where
packages/services/service-analytics/src/strategies/filter-normalizer.ts, the shared analytics lowering.values = Array.isArray(v) ? v.map(comparand) : [comparand(v)]carries the list into a scalar leaf. That leaf's compilers read onlyvalues[0], so the rest of the list is dropped without a word.Contract
For a number field,
@objectstack/spec/data'sfilter-number-comparand-declared-type.tsrefuses thearrayform at a scalar slot "on every driver and position". The engine face answers 200 instead. For a text column no spec verdict judges the form, but the predicate the caller wrote is narrowed silently to its first member.Direction (triage's to rule, not a ruling)
A list at a scalar operator is refused at the shared lowering (or at the shared comparand-shape face) for every column type, so both faces answer one 400. That would also close the native-only divergence PR #21446 pins.
Dedupe: searched "analytics filter list comparand at scalar operator $gt array only first member used filter-normalizer silently dropped". The nearest hits are closed and none covers this form:
whereskips the shared comparand-shape face's other arms ($innull member,$gt: null, null/blank$betweenbound, scalar$in) that the FilterArray spelling refuses 400 #20010 (the comparand-shape face's other arms on the object-formwhere);IN, while ruling 乙 refuses the same shape at the shared face "for every driver at once" #19888 (an implicit-equality array read asIN);Generated by Claude Code · https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ