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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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