Skip to content

runtime: POST /analytics/query refuses the array where the objectui adapter now sends for every array-form filter — AnalyticsQueryRequestSchema.where is FilterConditionSchema, while lowerAnalyticsWhere (the gate ui#6302 measured) accepts filter AST #15828

Description

@claude

Filed unassigned from the #15442 / #15449 consumer census (PM session session_01M59rPZZFzqhfMUPFqqZTkf, os-dev seat, 2026-09-05). Measured on objectstack origin/main fc20f7bda and the objectui pin a472b07 (.objectui-sha).

Measured

AnalyticsQueryRequestSchema (packages/spec/src/api/analytics.zod.ts:50, AnalyticsQuerySchema.extend(...); where inherited from packages/spec/src/data/analytics.zod.ts:299, FilterConditionSchema) — safeParse({ cube, measures, dimensions, where }) with each shape (scratch tsx script against the spec source):

where <- record           : ACCEPT
where <- ast_tuple_array  : REFUSE ["invalid_type(expected record)","invalid_type(expected object)"]
where <- ast_single       : REFUSE (same)
where <- rule_array       : REFUSE (same)

The only HTTP entry runs exactly that parse first: packages/runtime/src/dispatcher-plugin.ts:1152 (server.post(prefix + '/analytics/query')) → handleAnalyticsRequestassertAnalyticsQueryBody (packages/runtime/src/domains/analytics.ts:49-70, called at :102 and :125) → AnalyticsQueryRequestSchema.safeParse(body)validationFailure(...) on failure. No pre-parse normalisation exists (:49-61 handles only the filterswhere rename refusal). The SDK posts the payload verbatim (packages/client/src/index.ts:1647-1650).

Why it matters

  • objectui ObjectStackAdapter.aggregate() (pin a472b07, packages/data-objectstack/src/index.ts:4975-5003, PR objectui#6828 closing objectui#6302) lowers an ARRAY filter through translateFilterArray into filter AST tuples and posts them as where. That was the runtime prerequisite recorded for the element:number.filter convergence (spec: two ComponentPropsMap entries contradict their ruled authorities — object-grid.data vs ViewDataSchema, element:number.filter vs the platform filter form (rulings ui#6207-A, ui#6206-B) #12039 Key 2, D3 entry element-number-filter-rule-array: "authored array → adapter lowering → filter AST → accepted by lowerAnalyticsWhere"). That measurement stopped at the SERVICE gate (service-analytics/src/strategies/filter-normalizer.ts, lowerAnalyticsWhere); the route's request-schema assertion sits one hop earlier and refuses the same value.
  • So on a deployment that serves analytics through the runtime route, an element:number (array-only since spec: two ComponentPropsMap entries contradict their ruled authorities — object-grid.data vs ViewDataSchema, element:number.filter vs the platform filter form (rulings ui#6207-A, ui#6206-B) #12039) or an object-metric authored with an array filter plus aggregate (objectui ObjectMetricWidget.tsx:245-253ds.aggregate(... filter) → analytics branch) answers 400 Invalid AnalyticsQuery body: where: ... instead of the aggregate. Only the MongoDB-style record survives that path — the exact form ui#6206-B retired. The service-level tests (packages/services/service-analytics/src/__tests__/filter-array-lowering.test.ts:235, :332) call analyticsService.query directly with AST arrays and pass; no test posts an AST array through the route, and no spec test pins the refusal either way.
  • Not run end to end here: the runtime's dependency closure was not built under this measurement claim; the route function is 15 lines and calls the schema that was measured. "Read, not run" on the route hop; the schema verdict is a run.

The decision this needs (not taken here)

Contract-first: the spec says where is a FilterCondition; the service accepts a wider set than the spec declares. Either (A) AnalyticsQuerySchema.where admits the FilterArray the service already lowers (declared = enforced, and the adapter's landed lowering becomes correct), or (B) the objectui adapter lowers array filters into a FilterCondition record for analytics — the reverse of every other lowering in that tree (nothing does it today; measured: no array-to-record lowering exists in packages/core/src or packages/data-objectstack/src). Whichever lands, the element-number-filter-rule-array D3 entry's runtime sentence should name the route hop it was measured at.

Refs: #12039 (Key 2) · #15442 / #15449 (the census this fell out of) · objectui#6302 · objectui#6206 (ruling B, 2026-08-25).


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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions