Skip to content

finding(data-objectstack): aggregate()'s spec-shape branch sends where unlowered, so one chart's filter is lowered or not depending on which aggregation shape it uses #6825

Description

@claude

Observation from the objectui execution seat while implementing #6302 (the analytics-path lowering). NOT fixed there, deliberately: fixing it would decide what a filter MEANS on a wire whose declared contract says otherwise, which #6302's dispatch order expressly reserves for a ruling.

What was measured (source reads, on objectui 26896c689 and the objectstack tree in the same container)

ObjectStackAdapter.aggregate() (packages/data-objectstack/src/index.ts) has TWO branches, and they now differ in how an array filter reaches the server:

  1. Analytics branch (legacy field / function / groupBy params) — payload.where is lowered through translateFilterArray and posted to client.analytics.query. This is what ObjectStackAdapter.aggregate() must lower rule-shaped filter arrays before the wire — the analytics path skips the lowering find() already performs (prerequisite of the ruled element:number.filter convergence) #6302 fixed.
  2. Spec-shape branch (looksLikeSpecShape: groupBy is an array, or aggregations is an array, or where is defined) — queryAst.where = params.where verbatim, then client.data.query. No lowering, before or after ObjectStackAdapter.aggregate() must lower rule-shaped filter arrays before the wire — the analytics path skips the lowering find() already performs (prerequisite of the ruled element:number.filter convergence) #6302.

The two branches are reachable from ONE authored chart. packages/plugin-charts/src/ObjectChart.tsx passes the SAME resolved value to both:

  • line 483 — where: filterForRun (spec-shape branch)
  • line 491 — filter: filterForRun (analytics branch)

Which branch runs depends only on whether the chart carries groupBy + aggregations or the legacy field / function pair. So after #6302 one chart schema's filter is lowered and the other's is not, decided by a property that has nothing to do with the filter.

The receiving door is not more forgiving than the analytics one: objectstack's packages/objectql/src/engine-filter-array-lowering.test.ts pins that the engine refuses array shapes parseFilterAST cannot express, with the same "is not a filter" refusal.

Why this was NOT folded into #6302

The where on branch 2 is the spec Query DSL's wherepackages/spec/src/data/query.zod.ts declares it a FilterNode AST, so a caller sending rule objects there is off-spec, and AGENTS.md #0.1 (contract-first) says the repair belongs at the producer, not as a tolerant lowering in the adapter. #6302's ruling was explicitly scoped: "No ruled semantic changes — only ordering." Deciding branch 2 is a semantic call, so it is reported rather than taken.

What is NOT established here

No measured case of a real authored chart carrying a rule-shaped array into branch 2. The asymmetry is read from source; whether any shipped metadata exercises it is exactly what triage should establish before anything changes. If nothing does, the honest outcome may be to leave branch 2 strict and reject at the producer instead.

Refs: #6302 (the analytics-path fix) - objectstack#12039 (the Key-2 sequencing ruling).

Generated by Claude Code


Generated by Claude Code

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions