Repository navigation
objectql having: a { $field } reference with addDays against a non-temporal aggregated column answers by epoch-ms coercion, where SQL push-down refuses the same pair on where — the aggregated row declares no temporal class to judge it by #20127
Description
Activity
objectstack-fleet commented
on Sep 25, 2026 ContributorAuthorMore actionsPath: an API a customer can call | api-backend.aggregate-contract-matrix | P2
Triage: first grade —
bug·priority:p3·domain:engine·area:api·pm:queueTriage: lands in
packages/objectql/src/having-filter.ts(compareWithReference) and thehavingentry (assertHavingIsEvaluable,aggregatedRowColumns) ⇒domain:engine; rationale:addDayson a$fieldreference between non-temporal aggregated columns answers by epoch-ms coercion where SQL push-down refuses the pair;FieldReferenceSchema.addDaysdeclares temporal-only. A narrow authored shape ⇒ p3.Triage seat #6015 ·
session_01CRZSc7dU8oDStbTbSwhuZe· 2026-09-25T09:56Z. ⛔ Not a claim, ⛔ not a dispatch. Read: this card and its full thread, andorigin/main.Execution note: derive each aggregated column's class statically (the card's shape) and refuse in
driver-sql's words; region order after PR #20117, same batch as #20122 / #20123.- addedarea:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsThe API a customer can call, and integrations — REST, connectors, webhooks, jobsbugSomething isn't workingSomething isn't workingand removed
on Sep 25, 2026 objectstack-fleet commented
on Sep 25, 2026 ContributorAuthorMore actionsClaim: PM loop round 22
Session:session_01Bvd69VPa6puiNzzPUroDBx
Account:os-sales(the seat's linked user asGET /useranswers it; always the card's assignee)
Branch:claude/issue-20122-aggregate-filter-doors
Worktree:objectstack-issue-20122
Domain:domain:engine
Seat:domain:engine#1
File surface: a combined claim of #20122 (the chain head), #20123 and #20127, as triage batched them (same files, same entry). One PR carries oneFixes #<n>line per member and one commit per card.packages/objectql/src/engine.ts: theaggregateentry only, meaning the per-aggregationfilterloop ([engine.aggregate: add per-aggregation filter to the contract — ruled half of #10413 (measure-level filters on the ObjectQL analytics path) #10576]) and thehavingentry'sassertHavingIsEvaluablecall;packages/objectql/src/having-filter.ts:matchesAggregationFilter,checkCondition's no-value exit,compareWithReference,assertHavingIsEvaluableandaggregatedRowColumns;- tests in
packages/objectql; .changeset/2012{2,3,7}-*.md.
Stop on breach and explain in the report. ⛔ Not
lowerWhereFilterArray(PR #20144, #20121, p0 in review). ⛔ Not the drivers' ownhaving/ aggregate push-down, notservice-analytics, and not the docblock hunk of draft PR #20125.
Container & model:M,mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate, floor sonnet · default opus · ceiling fable)
Clause-②: no (narrowing)
Thread-read: 5830458033
Serial constraints cleared: at 2026-09-25T11:19Z, the region predecessor PR #20117 (#20099) landed asfc646cf09c. A census of the 8 open PRs finds two onengine.tsand none onhaving-filter.ts: PR #20144 (lowerWhereFilterArray, near:863) and draft PR #20125 (a docblock near:1245). Both are disjoint fromaggregate(:15971onmainf09d4122bc). Whichever lands second mergesmainfirst.- added a commit that references this issue
on Sep 25, 2026 objectstack-fleet commented
on Sep 25, 2026 ContributorAuthorMore actionsLanded: PR #20147, verified on
mainas16c5a33fdddomain:engine#1,session_01Bvd69VPa6puiNzzPUroDBx, written 2026-09-25T13:59Z. This card was a member of the combined claim led by #20122. The full landing record is 5833618445 on #20122. The card is closed via its ownFixesline, andpm:dispatchedis removed.- added 4 commits that reference this issue
on Sep 28, 2026 - added a commit that references this issue
on Sep 29, 2026
Filing gate: ① a defect with a named landing site:
packages/objectql/src/having-filter.ts(compareWithReference), and thehavingentry ofObjectQL.aggregateinpackages/objectql/src/engine.ts(assertHavingIsEvaluable,aggregatedRowColumns). Finding class (a).The
domain:engineexecution seat 1 (session_01Bvd69VPa6puiNzzPUroDBx) filed this from its #20099 dev's open question 2 (os-dev-reporton #20099) and PR #20117's contract review (5828166480, ③ Q2). The seat ruled option A for PR #20117, keep, so this card carries the end state. ⛔ Filed bare: routing and grading are triage's. ⛔ Not a claim.What happens
Measured at PR #20117's head
98abdf4ee3:having: { total: { $gt: { $field: 'max_cap', addDays: 1 } } }, withtotalandmax_capboth numeric aggregation aliases, keeps no group.havingresolves the reference through@objectstack/formula'smatchesFilterCondition, which reads a number as epoch-ms and adds a day.whereanswers three ways, by face:driver-sql/driver-sqlite-wasmrefuse it with 400 (an uncompilable pair:addDayscompiles only between two temporal columns of one class),driver-memoryanswers[](it never resolves$field, [finding] driver-memory's own reference matcher has no$fieldarm — a cross-field comparand (bare or withaddDays) reaching it is presumably compared as a literal object rather than resolved or refused (grep reading, to be measured) #15104), and formula answers by coercion.FieldReferenceSchema.addDaysdeclares that it 「Compiles on SQL push-down between two temporal columns of the same class (date/date, datetime/datetime) and evaluates identically in memory」.havingcannot apply that rule, because an aggregated row declares no temporal class for its columns.Suggested shape (⛔ not a ruling)
groupByfield takes its declared type, and an aggregation alias takes its function applied to its source field's declared type (min/maxof adatetimeis adatetime;countandsumare numbers). Then refuseaddDaysunless both sides are temporal columns of one class, in the wordsdriver-sqluses onwhere.applyHavingdoors and on an empty grouped set.Filing-gate answers
havingdoes not take the rest ofwhere's filter doors: a$fieldreference is never resolved, the comparand-TYPE door does not run, FilterArray sugar answers no group, and its own refusals fire only on a non-empty grouped set #20099 dev and the PR fix(objectql)!:havingtakes the rest ofwhere's filter doors — the comparand-type door, row-independent refusals, a resolved{ $field }, and a refused non-condition #20117 reviewer.domain:engine, the owner ofpackages/objectql). It is sequenced after PR fix(objectql)!:havingtakes the rest ofwhere's filter doors — the comparand-type door, row-independent refusals, a resolved{ $field }, and a refused non-condition #20117 (objectqlhavingdoes not take the rest ofwhere's filter doors: a$fieldreference is never resolved, the comparand-TYPE door does not run, FilterArray sugar answers no group, and its own refusals fire only on a non-empty grouped set #20099), which adds the entry, the column set and the resolution this extends. That is a region order, not aBlocked-by:.closedincluded:having addDays field reference non-temporal aggregated column temporal class derivation→ 0 hits.addDays field reference offset cross-field comparison temporal columns same class→ 0 hits.$field cross-field comparison field reference→ 3 hits: [finding] driver-memory's own reference matcher has no$fieldarm — a cross-field comparand (bare or withaddDays) reaching it is presumably compared as a literal object rather than resolved or refused (grep reading, to be measured) #15104, [spec] SqlDriver 将$field编译为列对列比较(cross-field comparison push-down) #5222 and [spec]$field跨字段比较:spec 声明 + cel-to-filter 产出,但无任何 SQL 执行层实现 —— enforce-or-remove 裁决位 #5041, the$fieldfamily. None covershaving'saddDays.Dedupe words:
having addDays non-temporal·aggregated column temporal class·field reference addDays having